2025-03-27 17:47:37 vscode task自动提交
@ -21,7 +21,7 @@
|
|||||||
4. 设置数据库不区分大小写:修改配置文件my.cnf 的[mysqld]中增加 lower_case_table_names = 1
|
4. 设置数据库不区分大小写:修改配置文件my.cnf 的[mysqld]中增加 lower_case_table_names = 1
|
||||||
|
|
||||||
5. 安装依赖环境
|
5. 安装依赖环境
|
||||||
pip install -r requirements_linux.txt -i https://mirrors.aliyun.com/pypi/simple/
|
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
|
||||||
6. 数据初始化使用sql脚本直接导入(本sql脚本使用的是SQL_Front5.3工具配合mysql5.7数据库导出的)
|
6. 数据初始化使用sql脚本直接导入(本sql脚本使用的是SQL_Front5.3工具配合mysql5.7数据库导出的)
|
||||||
sql脚本位置:backend/lyadmin_pro.sql
|
sql脚本位置:backend/lyadmin_pro.sql
|
||||||
|
|||||||
@ -325,7 +325,7 @@ X_FRAME_OPTIONS = 'SAMEORIGIN'#SAMEORIGIN允许同源iframe嵌套、 DENY不允
|
|||||||
# "accept-encoding",
|
# "accept-encoding",
|
||||||
# "lypage"
|
# "lypage"
|
||||||
# )
|
# )
|
||||||
|
CORS_EXPOSE_HEADERS = ['Content-Disposition'] # Content-Disposition 头部添加到 Access-Control-Expose-Headers 中,允许客户端 JavaScript 访问该头部
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ********************* 日志配置 ******************* #
|
# ********************* 日志配置 ******************* #
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
@ -441,7 +441,7 @@ REST_FRAMEWORK = {
|
|||||||
# 'user': '60/minute' #已登录用户每分钟可以请求60次
|
# 'user': '60/minute' #已登录用户每分钟可以请求60次
|
||||||
# },
|
# },
|
||||||
'EXCEPTION_HANDLER': 'utils.exception.CustomExceptionHandler', # 自定义的异常处理
|
'EXCEPTION_HANDLER': 'utils.exception.CustomExceptionHandler', # 自定义的异常处理
|
||||||
#线上部署正式环境,关闭web接口测试页面
|
# #线上部署正式环境,关闭web接口测试页面
|
||||||
# 'DEFAULT_RENDERER_CLASSES':(
|
# 'DEFAULT_RENDERER_CLASSES':(
|
||||||
# 'rest_framework.renderers.JSONRenderer',
|
# 'rest_framework.renderers.JSONRenderer',
|
||||||
# ),
|
# ),
|
||||||
@ -557,6 +557,7 @@ API_MODEL_MAP = {
|
|||||||
"/api/token/": "登录模块",
|
"/api/token/": "登录模块",
|
||||||
"/api/super/operate/":"前端API关闭开启",
|
"/api/super/operate/":"前端API关闭开启",
|
||||||
"/api/platformsettings/uploadplatformimg/":"图片上传",
|
"/api/platformsettings/uploadplatformimg/":"图片上传",
|
||||||
|
"/api/system/fileManage/":"文件管理"
|
||||||
}
|
}
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
||||||
|
|||||||
@ -41,6 +41,10 @@ from apps.lyTiktokUnion.views.douyinMsgWebhook import DouyinAllianceDarenOrderWe
|
|||||||
from apps.lyTiktokUnion.views.doudianCallback import *
|
from apps.lyTiktokUnion.views.doudianCallback import *
|
||||||
from apps.lyTiktokUnion.views.dySystemAccountViews import DouyinSytemDarenCodeCallbackView
|
from apps.lyTiktokUnion.views.dySystemAccountViews import DouyinSytemDarenCodeCallbackView
|
||||||
|
|
||||||
|
#文件管理
|
||||||
|
|
||||||
|
from mysystem.views.file_manage import RYFileMediaView,RYGetFileDownloadView
|
||||||
|
|
||||||
#字典信息数据获取
|
#字典信息数据获取
|
||||||
from mysystem.views.dictionary import GetDictionaryInfoView,GetDictionaryAllView
|
from mysystem.views.dictionary import GetDictionaryInfoView,GetDictionaryAllView
|
||||||
#app下载页
|
#app下载页
|
||||||
@ -97,6 +101,10 @@ urlpatterns = [
|
|||||||
path('api/lyformbuilder/', include('apps.lyFormBuilder.urls')),
|
path('api/lyformbuilder/', include('apps.lyFormBuilder.urls')),
|
||||||
path('api/lytiktokunion/', include('apps.lyTiktokUnion.urls')),
|
path('api/lytiktokunion/', include('apps.lyTiktokUnion.urls')),
|
||||||
path('api/workflow/', include('apps.lyworkflow.urls')),
|
path('api/workflow/', include('apps.lyworkflow.urls')),
|
||||||
|
|
||||||
|
#文件管理
|
||||||
|
path('api/fileMedia/', RYFileMediaView.as_view(), name='file_media'),
|
||||||
|
path('api/download/', RYGetFileDownloadView.as_view(), name='download'),
|
||||||
|
|
||||||
# ========================================================================================= #
|
# ========================================================================================= #
|
||||||
# ********************************** 前端微服务API用户接口************************************ #
|
# ********************************** 前端微服务API用户接口************************************ #
|
||||||
|
|||||||
@ -13,9 +13,9 @@ system_url = routers.SimpleRouter()
|
|||||||
system_url.register(r'messagetemplate', MyMessageTemplateViewSet)
|
system_url.register(r'messagetemplate', MyMessageTemplateViewSet)
|
||||||
system_url.register(r'messagenotice', MyMessageViewSet)
|
system_url.register(r'messagenotice', MyMessageViewSet)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path('messagenotice/ownmsg/',MyMessageViewSet.as_view({'get':'get_own_receive'}), name='获取自己的消息列表'),
|
||||||
|
path('messagenotice/delownmsg/',MyMessageViewSet.as_view({'post':'del_own_receive'}), name='删除自己的消息'),
|
||||||
|
path('messagenotice/readownmsg/',MyMessageViewSet.as_view({'post':'read_own_receive'}), name='设置自己的消息已读'),
|
||||||
]
|
]
|
||||||
urlpatterns += system_url.urls
|
urlpatterns += system_url.urls
|
||||||
@ -12,6 +12,8 @@ from rest_framework.permissions import IsAuthenticated
|
|||||||
from utils.pagination import CustomPagination
|
from utils.pagination import CustomPagination
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
import datetime
|
import datetime
|
||||||
|
from asgiref.sync import async_to_sync
|
||||||
|
from channels.layers import get_channel_layer
|
||||||
|
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ************** 后台消息中心 view ************** #
|
# ************** 后台消息中心 view ************** #
|
||||||
@ -67,6 +69,20 @@ class MyMessageSerializer(CustomModelSerializer):
|
|||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
# exclude = ['password']
|
# exclude = ['password']
|
||||||
|
|
||||||
|
def websocket_push(user_id, message):
|
||||||
|
"""
|
||||||
|
主动推送消息
|
||||||
|
"""
|
||||||
|
room_group_name = "notifications_user_" + str(user_id)
|
||||||
|
channel_layer = get_channel_layer()
|
||||||
|
async_to_sync(channel_layer.group_send)(
|
||||||
|
room_group_name,
|
||||||
|
{
|
||||||
|
"type": "push.message",
|
||||||
|
"json": message
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
class MyMessageCreateUpdateSerializer(CustomModelSerializer):
|
class MyMessageCreateUpdateSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
消息公告 -序列化器
|
消息公告 -序列化器
|
||||||
@ -98,6 +114,9 @@ class MyMessageCreateUpdateSerializer(CustomModelSerializer):
|
|||||||
targetuser_instance = MyMessageUserSerializer(data=targetuser_data, many=True, request=self.request)
|
targetuser_instance = MyMessageUserSerializer(data=targetuser_data, many=True, request=self.request)
|
||||||
targetuser_instance.is_valid(raise_exception=True)
|
targetuser_instance.is_valid(raise_exception=True)
|
||||||
targetuser_instance.save()
|
targetuser_instance.save()
|
||||||
|
for user in users:
|
||||||
|
unread_nums = MyMessageUser.objects.filter(revuserid_id=user, is_read=False,is_delete=False).count()
|
||||||
|
websocket_push(user, message={"sender": 'system', "msg_type": 'SYS',"content": '您有一条新消息~', "unread": unread_nums})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -106,6 +125,28 @@ class MyMessageCreateUpdateSerializer(CustomModelSerializer):
|
|||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
# exclude = ['password']
|
# exclude = ['password']
|
||||||
|
|
||||||
|
|
||||||
|
class MyMessageListSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
用户消息列表序列化器-序列化器
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MyMessage
|
||||||
|
fields = ("id","target_type","msg_title","msg_content")
|
||||||
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
class MyMessageUserListSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
目标用户序列化器-序列化器
|
||||||
|
"""
|
||||||
|
messageid = MyMessageListSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MyMessageUser
|
||||||
|
fields = "__all__"
|
||||||
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
class MyMessageViewSet(CustomModelViewSet):
|
class MyMessageViewSet(CustomModelViewSet):
|
||||||
"""
|
"""
|
||||||
后台消息公告 接口:
|
后台消息公告 接口:
|
||||||
@ -116,6 +157,39 @@ class MyMessageViewSet(CustomModelViewSet):
|
|||||||
update_serializer_class = MyMessageCreateUpdateSerializer
|
update_serializer_class = MyMessageCreateUpdateSerializer
|
||||||
filterset_fields = ('msg_title',)
|
filterset_fields = ('msg_title',)
|
||||||
search_fields = ('msg_title',)
|
search_fields = ('msg_title',)
|
||||||
|
|
||||||
|
def get_own_receive(self, request):
|
||||||
|
"""
|
||||||
|
获取自己的接收消息
|
||||||
|
"""
|
||||||
|
own_id = self.request.user.id
|
||||||
|
queryset = MyMessageUser.objects.filter(revuserid_id=own_id)
|
||||||
|
page = self.paginate_queryset(queryset)
|
||||||
|
if page is not None:
|
||||||
|
serializer = MyMessageUserListSerializer(page, many=True, request=request)
|
||||||
|
return self.get_paginated_response(serializer.data)
|
||||||
|
serializer = MyMessageUserListSerializer(queryset, many=True, request=request)
|
||||||
|
return SuccessResponse(data=serializer.data, msg="获取成功")
|
||||||
|
|
||||||
|
def del_own_receive(self, request):
|
||||||
|
"""
|
||||||
|
删除自己的接收消息
|
||||||
|
"""
|
||||||
|
reqData = get_parameter_dic(request)
|
||||||
|
id = reqData.get("id")
|
||||||
|
own_id = self.request.user.id
|
||||||
|
MyMessageUser.objects.filter(id=id,revuserid_id=own_id).delete()
|
||||||
|
return SuccessResponse(msg="删除成功")
|
||||||
|
|
||||||
|
def read_own_receive(self, request):
|
||||||
|
"""
|
||||||
|
设置自己的接收消息已读
|
||||||
|
"""
|
||||||
|
reqData = get_parameter_dic(request)
|
||||||
|
id = reqData.get("id")
|
||||||
|
own_id = self.request.user.id
|
||||||
|
MyMessageUser.objects.filter(id=id,revuserid_id=own_id,is_read=False).update(is_read=True,read_at=datetime.datetime.now())
|
||||||
|
return SuccessResponse(msg="删除成功")
|
||||||
|
|
||||||
def send_sys_template_message(revuser,code):
|
def send_sys_template_message(revuser,code):
|
||||||
"""
|
"""
|
||||||
|
|||||||
60
backend/apps/lymessages/wsmessage.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import json
|
||||||
|
from asgiref.sync import sync_to_async, async_to_sync
|
||||||
|
from channels.db import database_sync_to_async
|
||||||
|
from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
||||||
|
|
||||||
|
@database_sync_to_async
|
||||||
|
def _get_message_unread_count(user):
|
||||||
|
"""获取用户的未读消息数量"""
|
||||||
|
from apps.lymessages.models import MyMessageUser
|
||||||
|
count = MyMessageUser.objects.filter(revuserid=user, is_read=False, is_delete=False).count()
|
||||||
|
return count or 0
|
||||||
|
|
||||||
|
def set_message(sender, msg_type, msg, unread=0):
|
||||||
|
text = {
|
||||||
|
'sender': sender,
|
||||||
|
'msg_type': msg_type,
|
||||||
|
'content': msg,
|
||||||
|
'unread': unread
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationConsumer(AsyncJsonWebsocketConsumer):
|
||||||
|
async def connect(self):
|
||||||
|
try:
|
||||||
|
self.user = self.scope.get('user',None)
|
||||||
|
room_name = "user_" + str(self.user.id)
|
||||||
|
self.room_group_name = f'notifications_{room_name}'
|
||||||
|
await self.channel_layer.group_add(
|
||||||
|
self.room_group_name,
|
||||||
|
self.channel_name
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.accept("JWTLYADMIN")
|
||||||
|
|
||||||
|
unread_count = await _get_message_unread_count(self.user)
|
||||||
|
if unread_count == 0:
|
||||||
|
await self.send_json(set_message('system', 'SYS', f'{self.user.name},你好!消息通知已上线!'))
|
||||||
|
else:
|
||||||
|
await self.send_json(set_message('system', 'SYS', "请查看您的未读消息~",unread=unread_count))
|
||||||
|
except Exception as e:
|
||||||
|
await self.close()
|
||||||
|
|
||||||
|
async def receive(self, text_data=None, bytes_data=None, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def disconnect(self, close_code):
|
||||||
|
await self.channel_layer.group_discard(
|
||||||
|
self.room_group_name,
|
||||||
|
self.channel_name
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.close(close_code)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def push_message(self, event):
|
||||||
|
message = event['json']
|
||||||
|
await self.send(text_data=json.dumps(message))
|
||||||
@ -6,7 +6,9 @@
|
|||||||
from django.urls import re_path,path
|
from django.urls import re_path,path
|
||||||
|
|
||||||
from . import consumers
|
from . import consumers
|
||||||
|
from apps.lymessages.wsmessage import NotificationConsumer
|
||||||
|
|
||||||
websocket_urlpatterns = [
|
websocket_urlpatterns = [
|
||||||
re_path(r'^ws/webssh/$', consumers.TerminalConsumer.as_asgi()),
|
re_path(r'^ws/webssh/$', consumers.TerminalConsumer.as_asgi()),
|
||||||
|
re_path(r'^ws/msg/$', NotificationConsumer.as_asgi()),
|
||||||
]
|
]
|
||||||
51
backend/frontend/download-app/index.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
{% load i18n static %}
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="shortcut icon" href="{% static 'images/favicon.2554488.lllxs2download-app.ico' %}" type="image/x-icon" />
|
||||||
|
<title>APP下载</title>
|
||||||
|
<link rel="stylesheet" href="{% static 'css/index.2554488.lllxs2download-app.css' %}" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page">
|
||||||
|
<!-- 丝带 -->
|
||||||
|
<div class="pattern left">
|
||||||
|
<img src="{% static 'images/download_pattern_left.2554488.lllxs2download-app.png' %}" />
|
||||||
|
</div>
|
||||||
|
<div class="pattern right">
|
||||||
|
<img src="{% static 'images/download_pattern_right.2554488.lllxs2download-app.png' %}" />
|
||||||
|
</div>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="wrap">
|
||||||
|
{% if data.logo %}
|
||||||
|
<img class="logo" src="{{ data.logo }}" />
|
||||||
|
{% else %}
|
||||||
|
<img class="logo" src="{% static 'images/icon_logo.2554488.lllxs2download-app.png' %}" />
|
||||||
|
{% endif %}
|
||||||
|
<div class="platform">
|
||||||
|
<img class="icon-env ios" src="{% static 'images/icon_ios_1.2554488.lllxs2download-app.png' %}" />
|
||||||
|
<img class="icon-env android" src="{% static 'images/icon_android_1.2554488.lllxs2download-app.png' %}" />
|
||||||
|
<span class="app-name">{{ data.name }}APP下载</span>
|
||||||
|
</div>
|
||||||
|
<div class="download-button">点击下载</div>
|
||||||
|
<div id="lyandroidurl" style="display:none;">{{ data.apkurl }}</div>
|
||||||
|
{# <div class="open-tips">已安装?立即打开</div>#}
|
||||||
|
<div class="browser-tips">
|
||||||
|
温馨提示:请在移动端(手机)浏览器打开此页面
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 提示信息@即将开放 -->
|
||||||
|
<div class="dialog">
|
||||||
|
<div class="dialog-content">即将开放</div>
|
||||||
|
</div>
|
||||||
|
<!-- 提示信息@微信环境 -->
|
||||||
|
<img class="tips" src="{% static 'images/tips.2554488.lllxs2download-app.png' %}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="{% static 'js/utils.2554488.lllxs2download-app.js' %}"></script>
|
||||||
|
<script src="{% static 'js/index.2554488.lllxs2download-app.js' %}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
@charset "utf-8";
|
||||||
|
|
||||||
|
/* 动画 */
|
||||||
|
@keyframes debounce {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
10%,
|
||||||
|
30%,
|
||||||
|
50%,
|
||||||
|
70%,
|
||||||
|
90% {
|
||||||
|
transform: translateX(-2px);
|
||||||
|
}
|
||||||
|
20%,
|
||||||
|
40%,
|
||||||
|
60%,
|
||||||
|
80% {
|
||||||
|
transform: translateX(2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
.page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pattern {
|
||||||
|
width: 18%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.pattern img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.pattern.left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.pattern.right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.platform {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.platform .icon-env {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.platform .icon-env.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.platform .app-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #505556;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-button {
|
||||||
|
width: 211px;
|
||||||
|
height: 40px;
|
||||||
|
background: #32b2a7;
|
||||||
|
margin-top: 17px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
border-radius: 22px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 已安装提示 */
|
||||||
|
.open-tips {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 21px;
|
||||||
|
color: #fb4545;
|
||||||
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.open-tips.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tips {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 20px;
|
||||||
|
color: #999999;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.browser-tips.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 微信环境提示 */
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
display: none;
|
||||||
|
width: 160px;
|
||||||
|
position: absolute;
|
||||||
|
right: 6px;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.tips.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.tips.ani {
|
||||||
|
animation: debounce 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 即将开放 */
|
||||||
|
.dialog {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.dialog.show {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.dialog-content {
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
padding: 4px 15px;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 946 B |
|
After Width: | Height: | Size: 736 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 621 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
(function () {
|
||||||
|
// 1. 获取必要的DOM元素
|
||||||
|
var oTips = document.querySelector(".tips");
|
||||||
|
var oBrowserTips = document.querySelector(".browser-tips");
|
||||||
|
var oEnvImgForiOS = document.querySelector(".icon-env.ios");
|
||||||
|
var oEnvImgForAndroid = document.querySelector(".icon-env.android");
|
||||||
|
var oButtonDownload = document.querySelector(".download-button");
|
||||||
|
var oButtonOpenApp = document.querySelector(".open-tips");
|
||||||
|
var oDialog = document.querySelector(".dialog");
|
||||||
|
// 2. 常量值
|
||||||
|
var env = getEnv();
|
||||||
|
var isAnimating = false;
|
||||||
|
var SCHEME_URI = "d-point-life://www.lybbn.com/switch?index=0";
|
||||||
|
var downloadUrlForiOS = "https://itunes.apple.com/cn/app/id12346546xx";
|
||||||
|
var downloadUrlForAndroid = document.getElementById("lyandroidurl").innerText;
|
||||||
|
// 3. 控制默认显示
|
||||||
|
switch (env) {
|
||||||
|
// 微信环境,提示浏览器打开
|
||||||
|
case "weixin":
|
||||||
|
oTips.classList.add("show");
|
||||||
|
break;
|
||||||
|
// iOS环境,展示图标和下载按钮
|
||||||
|
case "ios":
|
||||||
|
oEnvImgForiOS.classList.add("show");
|
||||||
|
// oButtonOpenApp.classList.add("show");
|
||||||
|
break;
|
||||||
|
// Android环境,展示图标和下载按钮
|
||||||
|
case "android":
|
||||||
|
oEnvImgForAndroid.classList.add("show");
|
||||||
|
// oButtonOpenApp.classList.add("show");
|
||||||
|
break;
|
||||||
|
case "unknown":
|
||||||
|
oBrowserTips.classList.add("show");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 事件 --- 唤醒APP
|
||||||
|
// oButtonOpenApp.addEventListener(
|
||||||
|
// "click",
|
||||||
|
// function () {
|
||||||
|
// if (["ios", "android"].indexOf(env) !== -1) {
|
||||||
|
// window.location.href = SCHEME_URI;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// false
|
||||||
|
// );
|
||||||
|
// 5. 事件 --- 下载APP
|
||||||
|
oButtonDownload.addEventListener(
|
||||||
|
"click",
|
||||||
|
function () {
|
||||||
|
switch (env) {
|
||||||
|
case "weixin":
|
||||||
|
if (isAnimating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isAnimating = true;
|
||||||
|
oTips.classList.add("ani");
|
||||||
|
var t = setTimeout(function () {
|
||||||
|
isAnimating = false;
|
||||||
|
oTips.classList.remove("ani");
|
||||||
|
clearTimeout(t);
|
||||||
|
}, 1000);
|
||||||
|
break;
|
||||||
|
case "android":
|
||||||
|
window.location.href = downloadUrlForAndroid;
|
||||||
|
break;
|
||||||
|
case "ios":
|
||||||
|
window.location.href = downloadUrlForiOS;
|
||||||
|
// # 即将开放提示
|
||||||
|
/*
|
||||||
|
oDialog.classList.add("show");
|
||||||
|
var t = setTimeout(function () {
|
||||||
|
oDialog.classList.remove("show");
|
||||||
|
clearTimeout(t);
|
||||||
|
}, 1000);*/
|
||||||
|
break;
|
||||||
|
case "unknown":
|
||||||
|
window.location.href = downloadUrlForAndroid;
|
||||||
|
// alert("请在移动端(手机)浏览器打开此页面");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
})();
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
function getEnv() {
|
||||||
|
var _userAgent = window.navigator.userAgent;
|
||||||
|
if (/MicroMessenger/i.test(_userAgent)) {
|
||||||
|
return "weixin";
|
||||||
|
} else if (/Linux|Android/i.test(_userAgent)) {
|
||||||
|
return "android";
|
||||||
|
} else if (/iPhone/i.test(_userAgent)) {
|
||||||
|
return "ios";
|
||||||
|
} else {
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setEnvImg(element, env) {
|
||||||
|
switch (env) {
|
||||||
|
case "android":
|
||||||
|
element.src = "images/icon_android_1.2554488.lllxs2download-app.png";
|
||||||
|
break;
|
||||||
|
case "ios":
|
||||||
|
element.src = "images/icon_ios_1.2554488.lllxs2download-app.png";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
20
backend/frontend/h5/index.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>django-vue-lyadmin启动成功</title>
|
||||||
|
<style type="text/css">
|
||||||
|
html, body {
|
||||||
|
overflow: hidden;
|
||||||
|
margin: auto;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<iframe src="https://doc.lybbn.cn/articles/%E6%96%87%E6%A1%A3/%E7%BA%BF%E4%B8%8A%E9%83%A8%E7%BD%B2.html" width="100%" height="100%" frameborder="0">
|
||||||
|
<p>您的浏览器不支持 iframe 标签。</p>
|
||||||
|
</iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
backend/frontend/h5/static/favicon.ico
Normal file
|
After Width: | Height: | Size: 946 B |
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
}</style><script>var _hmt = _hmt || [];
|
}</style><script>var _hmt = _hmt || [];
|
||||||
var hmid = "33e0b6798fd8809c21ef51bc99e3149e";
|
var hmid = "33e0b6798fd8809c21ef51bc99e3149e";
|
||||||
(function () { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?" + hmid; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })();</script><script defer="defer" src="static/js/echarts.f8cde3b5.js"></script><script defer="defer" src="static/js/tinymce.3973fba9.js"></script><script defer="defer" src="static/js/elicons.61c4f779.js"></script><script defer="defer" src="static/js/modules.be4f7764.js"></script><script defer="defer" src="static/js/app.e6d3bb8a.js"></script><link href="static/css/modules.8c9e63f3.css" rel="stylesheet"><link href="static/css/app.e8b4186e.css" rel="stylesheet"></head><body><noscript><strong>Sorry django-vue-lyadmin (dvlyadmin_pro) doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><script>var dark = window.localStorage.getItem('siteTheme');
|
(function () { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?" + hmid; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })();</script><script defer="defer" src="static/js/vendors.43bd63f4.js"></script><script defer="defer" src="static/js/app.b5863ad2.js"></script><link href="static/css/vendors.25c1fb87.css" rel="stylesheet"><link href="static/css/app.193d5b92.css" rel="stylesheet"></head><body><noscript><strong>Sorry django-vue-lyadmin (dvlyadmin_pro) doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><script>var dark = window.localStorage.getItem('siteTheme');
|
||||||
if(dark && dark=="dark"){
|
if(dark && dark=="dark"){
|
||||||
document.documentElement.classList.add("dark")
|
document.documentElement.classList.add("dark")
|
||||||
}</script><div id="app" class="lyadmin"><div class="app-loading"><div class="app-loading__logo"><img src="static/img/logo.png"/></div><div class="app-loading__loader"></div><div class="app-loading__title">加载中</div></div></div></body></html>
|
}</script><div id="app" class="lyadmin"><div class="app-loading"><div class="app-loading__logo"><img src="static/img/logo.png"/></div><div class="app-loading__loader"></div><div class="app-loading__title">加载中</div></div></div></body></html>
|
||||||
2157
backend/frontend/static/css/app.193d5b92.css
Normal file
BIN
backend/frontend/static/css/app.193d5b92.css.gz
Normal file
605
backend/frontend/static/css/vendors.25c1fb87.css
Normal file
BIN
backend/frontend/static/css/vendors.25c1fb87.css.gz
Normal file
1
backend/frontend/static/js/839.a7e840a5.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
"use strict";(self.webpackChunkdjango_vue_lyadmin_pro=self.webpackChunkdjango_vue_lyadmin_pro||[]).push([[839],{96839:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});var s=r(61431);const o={class:"ly-cropper"},p={class:"ly-cropper__img"},a=["src"],i={class:"ly-cropper__preview"},c={class:"ly-cropper__preview__img",ref:"preview"};var l=r(65643),n=r.n(l),d={props:{src:{type:String,default:""},compress:{type:Number,default:1},aspectRatio:{type:Number,default:NaN}},data(){return{crop:null}},watch:{aspectRatio(e){this.crop.setAspectRatio(e)}},mounted(){this.init()},methods:{init(){this.crop=new(n())(this.$refs.img,{viewMode:2,dragMode:"move",responsive:!1,aspectRatio:this.aspectRatio,preview:this.$refs.preview})},setAspectRatio(e){this.crop.setAspectRatio(e)},getCropData(e,t="image/jpeg"){e(this.crop.getCroppedCanvas().toDataURL(t,this.compress))},getCropBlob(e,t="image/jpeg"){this.crop.getCroppedCanvas().toBlob((t=>{e(t)}),t,this.compress)},getCropFile(e,t="fileName.jpg",r="image/jpeg"){this.crop.getCroppedCanvas().toBlob((s=>{let o=new File([s],t,{type:r});e(o)}),r,this.compress)}}},m=(0,r(66262).A)(d,[["render",function(e,t,r,l,n,d){return(0,s.openBlock)(),(0,s.createElementBlock)("div",o,[(0,s.createElementVNode)("div",p,[(0,s.createElementVNode)("img",{src:r.src,ref:"img"},null,8,a)]),(0,s.createElementVNode)("div",i,[t[0]||(t[0]=(0,s.createElementVNode)("h4",null,"图像预览",-1)),(0,s.createElementVNode)("div",c,null,512)])])}],["__scopeId","data-v-2d8252e6"]])}}]);
|
||||||
1
backend/frontend/static/js/app.b5863ad2.js
Normal file
BIN
backend/frontend/static/js/app.b5863ad2.js.gz
Normal file
2
backend/frontend/static/js/vendors.43bd63f4.js
Normal file
150
backend/frontend/static/js/vendors.43bd63f4.js.LICENSE.txt
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
|
||||||
|
* @license MIT */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
JSZip v3.10.1 - A JavaScript class for generating and reading zip files
|
||||||
|
<http://stuartk.com/jszip>
|
||||||
|
|
||||||
|
(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
|
||||||
|
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.
|
||||||
|
|
||||||
|
JSZip uses the library pako released under the MIT license :
|
||||||
|
https://github.com/nodeca/pako/blob/main/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* core-base v10.0.6
|
||||||
|
* (c) 2025 kazuya kawaguchi
|
||||||
|
* Released under the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* shared v10.0.6
|
||||||
|
* (c) 2025 kazuya kawaguchi
|
||||||
|
* Released under the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* vue-i18n v10.0.6
|
||||||
|
* (c) 2025 kazuya kawaguchi
|
||||||
|
* Released under the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* vue-router v4.5.0
|
||||||
|
* (c) 2024 Eduardo San Martin Morote
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Cropper.js v1.6.2
|
||||||
|
* https://fengyuanchen.github.io/cropperjs
|
||||||
|
*
|
||||||
|
* Copyright 2015-present Chen Fengyuan
|
||||||
|
* Released under the MIT license
|
||||||
|
*
|
||||||
|
* Date: 2024-04-21T07:43:05.335Z
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* clipboard.js v2.0.11
|
||||||
|
* https://clipboardjs.com/
|
||||||
|
*
|
||||||
|
* Licensed MIT © Zeno Rocha
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* html2canvas 1.4.1 <https://html2canvas.hertzen.com>
|
||||||
|
* Copyright (c) 2022 Niklas von Hertzen <https://hertzen.com>
|
||||||
|
* Released under MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* pinia v2.3.1
|
||||||
|
* (c) 2025 Eduardo San Martin Morote
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* VueCodemirror v6.1.1
|
||||||
|
* Copyright (c) Surmon. All rights reserved.
|
||||||
|
* Released under the MIT License.
|
||||||
|
* Surmon
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
|
|
||||||
|
/*! *****************************************************************************
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
***************************************************************************** */
|
||||||
|
|
||||||
|
/*! *****************************************************************************
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
***************************************************************************** */
|
||||||
|
|
||||||
|
/*! Element Plus Icons Vue v2.3.1 */
|
||||||
|
|
||||||
|
/*! Element Plus v2.9.6 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an event is supported in the current execution environment.
|
||||||
|
*
|
||||||
|
* NOTE: This will not work correctly for non-generic events such as `change`,
|
||||||
|
* `reset`, `load`, `error`, and `select`.
|
||||||
|
*
|
||||||
|
* Borrows from Modernizr.
|
||||||
|
*
|
||||||
|
* @param {string} eventNameSuffix Event name, e.g. "click".
|
||||||
|
* @param {?boolean} capture Check if the capture phase is supported.
|
||||||
|
* @return {boolean} True if the event is supported.
|
||||||
|
* @internal
|
||||||
|
* @license Modernizr 3.0.0pre (Custom Build) | MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @vue/runtime-core v3.5.13
|
||||||
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||||
|
* @license MIT
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @vue/runtime-dom v3.5.13
|
||||||
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||||
|
* @license MIT
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @vue/shared v3.5.13
|
||||||
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||||
|
* @license MIT
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**!
|
||||||
|
* Sortable 1.15.6
|
||||||
|
* @author RubaXa <trash@rubaxa.org>
|
||||||
|
* @author owenm <owen23355@gmail.com>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
BIN
backend/frontend/static/js/vendors.43bd63f4.js.gz
Normal file
@ -16,6 +16,8 @@ echo -e "!!!若安装异常请手动删除venv虚拟环境目录!!!"
|
|||||||
echo -e "============================================"
|
echo -e "============================================"
|
||||||
|
|
||||||
py_path="/usr/local/lyadmin"
|
py_path="/usr/local/lyadmin"
|
||||||
|
cpu_core=$(cat /proc/cpuinfo|grep processor|wc -l)
|
||||||
|
|
||||||
Return_Error(){
|
Return_Error(){
|
||||||
echo '=================================================';
|
echo '=================================================';
|
||||||
printf '\033[1;31;40m%b\033[0m\n' "$@";
|
printf '\033[1;31;40m%b\033[0m\n' "$@";
|
||||||
@ -51,7 +53,7 @@ Install_Openssl111() {
|
|||||||
rm -f openssl-${opensslVersion}.tar.gz
|
rm -f openssl-${opensslVersion}.tar.gz
|
||||||
cd /tmp/openssl-${opensslVersion}
|
cd /tmp/openssl-${opensslVersion}
|
||||||
./config --prefix=/usr/local/openssl111 zlib-dynamic
|
./config --prefix=/usr/local/openssl111 zlib-dynamic
|
||||||
make -j${cpuCore} -C /tmp/openssl-${opensslVersion}/
|
make -j${cpu_core} -C /tmp/openssl-${opensslVersion}/
|
||||||
make install -C /tmp/openssl-${opensslVersion}/
|
make install -C /tmp/openssl-${opensslVersion}/
|
||||||
echo "/usr/local/openssl111/lib" >>/etc/ld.so.conf.d/openssl111.conf
|
echo "/usr/local/openssl111/lib" >>/etc/ld.so.conf.d/openssl111.conf
|
||||||
ldconfig
|
ldconfig
|
||||||
@ -81,7 +83,7 @@ Install_Python(){
|
|||||||
if [ "$(printf '%s\n' "1.1.1" "$openssl_version" | sort -V | head -n1)" = "1.1.1" ]; then
|
if [ "$(printf '%s\n' "1.1.1" "$openssl_version" | sort -V | head -n1)" = "1.1.1" ]; then
|
||||||
Show_Text_With_Ellipsis "检测到OpenSSL版本为${openssl_version}正在使用此环境" 0.3
|
Show_Text_With_Ellipsis "检测到OpenSSL版本为${openssl_version}正在使用此环境" 0.3
|
||||||
sys_openssl_path=$(which openssl)
|
sys_openssl_path=$(which openssl)
|
||||||
sys_openssl_dir=$(dirname "$openssl_path")
|
sys_openssl_dir=$(dirname "$sys_openssl_path")
|
||||||
WITH_SSL="--with-openssl=${sys_openssl_dir}"
|
WITH_SSL="--with-openssl=${sys_openssl_dir}"
|
||||||
else
|
else
|
||||||
Show_Text_With_Ellipsis "检测到OpenSSL版本为${openssl_version}正在安装支持Python${py_version}版本的openssl" 0.3
|
Show_Text_With_Ellipsis "检测到OpenSSL版本为${openssl_version}正在安装支持Python${py_version}版本的openssl" 0.3
|
||||||
@ -138,7 +140,7 @@ else
|
|||||||
source ${PROJECT_ROOT}/venv/bin/activate
|
source ${PROJECT_ROOT}/venv/bin/activate
|
||||||
|
|
||||||
# 安装 Django (可选)
|
# 安装 Django (可选)
|
||||||
pip install -r ${PROJECT_ROOT}/requirements_linux.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
pip install -r ${PROJECT_ROOT}/requirements_linux.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
|
||||||
echo "requrements.txt依赖安装完成"
|
echo "requrements.txt依赖安装完成"
|
||||||
fi
|
fi
|
||||||
@ -87,7 +87,11 @@ if object.{key}:
|
|||||||
{"id": 21, "name": "菜单配置", "value": "MenuConfig", },
|
{"id": 21, "name": "菜单配置", "value": "MenuConfig", },
|
||||||
{"id": 22, "name": "审核", "value": "Audit", },
|
{"id": 22, "name": "审核", "value": "Audit", },
|
||||||
{"id": 23, "name": "修改排序", "value": "EditSort", },
|
{"id": 23, "name": "修改排序", "value": "EditSort", },
|
||||||
{"id": 24, "name": "同步", "value": "Sync", },
|
{"id": 24, "name": "同步", "value": "Sync"},
|
||||||
|
{"id": 25, "name": "管理", "value": "Manage"},
|
||||||
|
{"id": 26, "name": "下载", "value": "Download"},
|
||||||
|
{"id": 27, "name": "上传", "value": "Upload"},
|
||||||
|
{"id": 28, "name": "Token", "value": "Token"},
|
||||||
|
|
||||||
]
|
]
|
||||||
self.save(Button, self.button_data, "权限表标识")
|
self.save(Button, self.button_data, "权限表标识")
|
||||||
@ -97,55 +101,57 @@ if object.{key}:
|
|||||||
初始化菜单表
|
初始化菜单表
|
||||||
"""
|
"""
|
||||||
self.menu_data = [
|
self.menu_data = [
|
||||||
{"id": 7,"icon": "","name": "轮播图设置","sort": 1,"is_link": 0,"web_path": "carouselSettingsimg","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 3},
|
{"id": 7,"icon": "","name": "轮播图设置","sort": 1,"is_link": 0,"web_path": "carouselSettingsimg","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 3},
|
||||||
{"id": 12,"icon": "","name": "部门管理","sort": 1,"is_link": 0,"web_path": "departmentManage","component": "system/dept","component_name": "dept","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 20},
|
{"id": 12,"icon": "","name": "部门管理","sort": 1,"is_link": 0,"web_path": "departmentManage","component": "system/dept","component_name": "dept","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 20},
|
||||||
{"id": 14,"icon": "","name": "操作日志","sort": 1,"is_link": 0,"web_path": "journalManage","component": "system/log/operationLog","component_name": "operationLog","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 22},
|
{"id": 14,"icon": "","name": "操作日志","sort": 1,"is_link": 0,"web_path": "journalManage","component": "system/log/operationLog","component_name": "operationLog","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 22},
|
||||||
{"id": 23,"icon": "DataLine","name": "DashBoard","sort": 1,"is_link": 0,"web_path": "analysis","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": None},
|
{"id": 23,"icon": "DataLine","name": "DashBoard","sort": 1,"is_link": 0,"web_path": "analysis","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": None},
|
||||||
{"id": 30,"icon": "","name": "商品管理","sort": 1,"is_link": 0,"web_path": "goodsManage","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 29},
|
{"id": 30,"icon": "","name": "商品管理","sort": 1,"is_link": 0,"web_path": "goodsManage","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 29},
|
||||||
{"id": 36,"icon": "Edit","name": "代码生成","sort": 1,"is_link": 0,"web_path": "lycodeGenerate","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 35},
|
{"id": 36,"icon": "Edit","name": "代码生成","sort": 1,"is_link": 0,"web_path": "lycodeGenerate","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 35},
|
||||||
{"id": 15,"icon": "","name": "菜单管理","sort": 2,"is_link": 0,"web_path": "menuManage","component": "system/menu","component_name": "menu","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 20},
|
{"id": 15,"icon": "","name": "菜单管理","sort": 2,"is_link": 0,"web_path": "menuManage","component": "system/menu","component_name": "menu","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 20},
|
||||||
{"id": 16,"icon": "","name": "按钮配置","sort": 2,"is_link": 0,"web_path": "buttonConfig","component": "system/ menuButton","component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 0,"parent": 20},
|
{"id": 16,"icon": "","name": "按钮配置","sort": 2,"is_link": 0,"web_path": "buttonConfig","component": "system/ menuButton","component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 0,"parent_id": 20},
|
||||||
{"id": 31,"icon": "","name": "商品分类","sort": 2,"is_link": 0,"web_path": "goodsType","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 29},
|
{"id": 31,"icon": "","name": "商品分类","sort": 2,"is_link": 0,"web_path": "goodsType","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 29},
|
||||||
{"id": 34,"icon": "Cpu","name": "表单构建","sort": 2,"is_link": 0,"web_path": "lyFormBuilders","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 35},
|
{"id": 34,"icon": "Cpu","name": "表单构建","sort": 2,"is_link": 0,"web_path": "lyFormBuilders","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 35},
|
||||||
{"id": 40,"icon": "DataAnalysis","name": "数据面板","sort": 2,"is_link": 0,"web_path": "lyDataPanel","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": None},
|
{"id": 40,"icon": "DataAnalysis","name": "数据面板","sort": 2,"is_link": 0,"web_path": "lyDataPanel","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": None},
|
||||||
{"id": 1,"icon": "avatar","name": "管理员管理","sort": 3,"is_link": 0,"web_path": "adminManage","component": None,"component_name": None,"status": 1,"isautopm": 1,"cache": 0,"visible": 1,"parent": None},
|
{"id": 1,"icon": "avatar","name": "管理员管理","sort": 3,"is_link": 0,"web_path": "adminManage","component": None,"component_name": None,"status": 1,"isautopm": 1,"cache": 0,"visible": 1,"parent_id": None},
|
||||||
{"id": 17,"icon": "","name": "角色管理","sort": 3,"is_link": 0,"web_path": "roleManage","component": "system/role","component_name": "role","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"creator": None,"parent": 20},
|
{"id": 17,"icon": "","name": "角色管理","sort": 3,"is_link": 0,"web_path": "roleManage","component": "system/role","component_name": "role","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"creator": None,"parent_id": 20},
|
||||||
{"id": 43,"icon": "CopyDocument","name": "表单模板","sort": 3,"is_link": 0,"web_path": "lyFormBuilderTemplate","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 35},
|
{"id": 43,"icon": "CopyDocument","name": "表单模板","sort": 3,"is_link": 0,"web_path": "lyFormBuilderTemplate","component": None,"component_name": None,"status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 35},
|
||||||
{"id": 18,"icon": "","name": "权限管理","sort": 4,"is_link": 0,"web_path": "authorityManage","component": "system/rolePermission","component_name": "rolePermission","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent": 20},
|
{"id": 18,"icon": "","name": "权限管理","sort": 4,"is_link": 0,"web_path": "authorityManage","component": "system/rolePermission","component_name": "rolePermission","status": 1,"isautopm": 0,"cache": 0,"visible": 1,"parent_id": 20},
|
||||||
{"id":2,"icon":"user-filled","name":"用户管理","sort":5,"is_link":0,"web_path":"userManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":5},
|
{"id":2,"icon":"user-filled","name":"用户管理","sort":5,"is_link":0,"web_path":"userManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":5},
|
||||||
{"id":8,"icon":"","name":"通知公告","sort":5,"is_link":0,"web_path":"messagNotice","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":3},
|
{"id":8,"icon":"","name":"通知公告","sort":5,"is_link":0,"web_path":"messagNotice","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":3},
|
||||||
{"id":5,"icon":"UserFilled","name":"用户管理","sort":6,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":5,"icon":"UserFilled","name":"用户管理","sort":6,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":19,"icon":"","name":"按钮管理","sort":6,"is_link":0,"web_path":"buttonManage","component":"system/button","component_name":"buttonManage","status":1,"isautopm":0,"cache":0,"visible":0,"parent":20},
|
{"id":19,"icon":"","name":"按钮管理","sort":6,"is_link":0,"web_path":"buttonManage","component":"system/button","component_name":"buttonManage","status":1,"isautopm":0,"cache":0,"visible":0,"parent_id":20},
|
||||||
{"id":4,"icon":"user-filled","name":"用户管理CRUD","sort":7,"is_link":0,"web_path":"userManageCrud","component":None,"component_name":None,"status":1,"isautopm":1,"cache":0,"visible":1,"parent":5},
|
{"id":4,"icon":"user-filled","name":"用户管理CRUD","sort":7,"is_link":0,"web_path":"userManageCrud","component":None,"component_name":None,"status":1,"isautopm":1,"cache":0,"visible":1,"parent_id":5},
|
||||||
{"id":10,"icon":"","name":"意见反馈","sort":8,"is_link":0,"web_path":"userFeekback","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":3},
|
{"id":10,"icon":"","name":"意见反馈","sort":8,"is_link":0,"web_path":"userFeekback","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":3},
|
||||||
{"id":37,"icon":"","name":"字典管理","sort":8,"is_link":0,"web_path":"sysDictionary","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":20},
|
{"id":37,"icon":"","name":"字典管理","sort":8,"is_link":0,"web_path":"sysDictionary","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":20},
|
||||||
{"id":3,"icon":"platform","name":"基础管理","sort":9,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":3,"icon":"platform","name":"基础管理","sort":9,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":11,"icon":"","name":"APP版本管理","sort":10,"is_link":0,"web_path":"lyAppVersion","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":3},
|
{"id":11,"icon":"","name":"APP版本管理","sort":10,"is_link":0,"web_path":"lyAppVersion","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":3},
|
||||||
{"id":26,"icon":"","name":"服务监控","sort":10,"is_link":0,"web_path":"server","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":25},
|
{"id":26,"icon":"","name":"服务监控","sort":10,"is_link":0,"web_path":"server","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":25},
|
||||||
{"id":48,"icon":"","name":"运费配置","sort":10,"is_link":0,"web_path":"freightConfigManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":29},
|
{"id":48,"icon":"","name":"运费配置","sort":10,"is_link":0,"web_path":"freightConfigManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":29},
|
||||||
{"id":50,"icon":"","name":"流程设计器","sort":10,"is_link":0,"web_path":"lyWorkflowDesign","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":49},
|
{"id":50,"icon":"","name":"流程设计器","sort":10,"is_link":0,"web_path":"lyWorkflowDesign","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":49},
|
||||||
{"id":9,"icon":"","name":"参数设置","sort":12,"is_link":0,"web_path":"platformSettingsother","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":3},
|
{"id":9,"icon":"","name":"参数设置","sort":12,"is_link":0,"web_path":"platformSettingsother","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":3},
|
||||||
{"id":6,"icon":"","name":"系统配置","sort":15,"is_link":0,"web_path":"systemConfig","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":3},
|
{"id":6,"icon":"","name":"系统配置","sort":15,"is_link":0,"web_path":"systemConfig","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":3},
|
||||||
{"id":24,"icon":"","name":"计划任务","sort":20,"is_link":0,"web_path":"crontab","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":25},
|
{"id":24,"icon":"","name":"计划任务","sort":20,"is_link":0,"web_path":"crontab","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":25},
|
||||||
{"id":41,"icon":"","name":"系统日志","sort":20,"is_link":0,"web_path":"lySystemLogs","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":22},
|
{"id":41,"icon":"","name":"系统日志","sort":20,"is_link":0,"web_path":"lySystemLogs","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":22},
|
||||||
{"id":27,"icon":"","name":"终端服务","sort":30,"is_link":0,"web_path":"terminal","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":25},
|
{"id":27,"icon":"","name":"终端服务","sort":30,"is_link":0,"web_path":"terminal","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":25},
|
||||||
{"id":32,"icon":"","name":"商城订单","sort":30,"is_link":0,"web_path":"mallOrderManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":29},
|
{"id":32,"icon":"","name":"商城订单","sort":30,"is_link":0,"web_path":"mallOrderManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":29},
|
||||||
{"id":33,"icon":"","name":"财务流水","sort":40,"is_link":0,"web_path":"financeStatisticsGoods","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":29},
|
{"id":33,"icon":"","name":"财务流水","sort":40,"is_link":0,"web_path":"financeStatisticsGoods","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":29},
|
||||||
{"id":45,"icon":"","name":"Redis监控","sort":40,"is_link":0,"web_path":"lyredis","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":25},
|
{"id":45,"icon":"","name":"Redis监控","sort":40,"is_link":0,"web_path":"lyredis","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":25},
|
||||||
{"id":44,"icon":"","name":"老师管理","sort":87,"is_link":0,"web_path":"lyFormBuilderteacherManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":35},
|
{"id":44,"icon":"","name":"老师管理","sort":87,"is_link":0,"web_path":"lyFormBuilderteacherManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":35},
|
||||||
{"id":28,"icon":"","name":"地区管理","sort":88,"is_link":0,"web_path":"areaManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":20},
|
{"id":28,"icon":"","name":"地区管理","sort":88,"is_link":0,"web_path":"areaManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":20},
|
||||||
{"id":38,"icon":"","name":"学生管理","sort":98,"is_link":0,"web_path":"lyAutoCodeStudentManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":35},
|
{"id":38,"icon":"","name":"学生管理","sort":98,"is_link":0,"web_path":"lyAutoCodeStudentManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":35},
|
||||||
{"id":29,"icon":"GoodsFilled","name":"商城管理","sort":180,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":29,"icon":"GoodsFilled","name":"商城管理","sort":180,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":49,"icon":"Connection","name":"流程管理","sort":186,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":49,"icon":"Connection","name":"流程管理","sort":186,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":42,"icon":"Coordinate","name":"功能大全","sort":188,"is_link":0,"web_path":"lyFunctionSets","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":42,"icon":"Coordinate","name":"功能大全","sort":188,"is_link":0,"web_path":"lyFunctionSets","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":21,"icon":"user","name":"个人中心","sort":866,"is_link":0,"web_path":"personalCenter","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":46},
|
{"id":46,"icon":"Aim","name":"个人中心","sort":866,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":46,"icon":"Aim","name":"个人中心","sort":866,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":21,"icon":"user","name":"个人中心","sort":866,"is_link":0,"web_path":"personalCenter","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":46},
|
||||||
{"id":25,"icon":"TrendCharts","name":"系统监控","sort":888,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":25,"icon":"TrendCharts","name":"系统监控","sort":888,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":47,"icon":"User","name":"个人中心新版","sort":900,"is_link":0,"web_path":"userCenter","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":46},
|
{"id":47,"icon":"User","name":"个人中心新版","sort":900,"is_link":0,"web_path":"userCenter","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":46},
|
||||||
{"id":35,"icon":"Box","name":"系统工具","sort":980,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":35,"icon":"Box","name":"系统工具","sort":980,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":20,"icon":"tools","name":"系统管理","sort":990,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":20,"icon":"tools","name":"系统管理","sort":990,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":22,"icon":"info-filled","name":"日志管理","sort":999,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":None},
|
{"id":22,"icon":"info-filled","name":"日志管理","sort":999,"is_link":0,"web_path":"","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":None},
|
||||||
{"id":39,"icon":"Opportunity","name":"关于系统","sort":1000,"is_link":0,"web_path":"lyabout","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent":20}
|
{"id":39,"icon":"Opportunity","name":"关于系统","sort":1000,"is_link":0,"web_path":"lyabout","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":20},
|
||||||
|
{"id":59,"icon":"Message","name":"我的消息","sort":6,"is_link":0,"web_path":"myMessage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":3},
|
||||||
|
{"id":60,"icon":"FolderOpened","name":"文件管理","sort":60,"is_link":0,"web_path":"sysFileManage","component":None,"component_name":None,"status":1,"isautopm":0,"cache":0,"visible":1,"parent_id":25},
|
||||||
]
|
]
|
||||||
self.save(Menu, self.menu_data, "菜单表")
|
self.save(Menu, self.menu_data, "菜单表")
|
||||||
|
|
||||||
@ -154,195 +160,202 @@ if object.{key}:
|
|||||||
初始化菜单权限表
|
初始化菜单权限表
|
||||||
"""
|
"""
|
||||||
self.menu_button_data = [
|
self.menu_button_data = [
|
||||||
{"id":1,"name":"编辑","value":"Update","api":"/api/platformsettings/lunboimg/{id}/","method":2,"menu":7},
|
{"id":1,"name":"编辑","value":"Update","api":"/api/platformsettings/lunboimg/{id}/","method":2,"menu_id":7},
|
||||||
{"id":2,"name":"编辑","value":"Update","api":"/api/platformsettings/other/{id}/","method":2,"menu":9},
|
{"id":2,"name":"编辑","value":"Update","api":"/api/platformsettings/other/{id}/","method":2,"menu_id":9},
|
||||||
{"id":3,"name":"编辑","value":"Update","api":"/api/system/button/{id}/","method":2,"menu":19},
|
{"id":3,"name":"编辑","value":"Update","api":"/api/system/button/{id}/","method":2,"menu_id":19},
|
||||||
{"id":4,"name":"编辑","value":"Update","api":"/api/system/menu/{id}/","method":2,"menu":15},
|
{"id":4,"name":"编辑","value":"Update","api":"/api/system/menu/{id}/","method":2,"menu_id":15},
|
||||||
{"id":5,"name":"编辑","value":"Update","api":"/api/system/dept/{id}/","method":2,"menu":12},
|
{"id":5,"name":"编辑","value":"Update","api":"/api/system/dept/{id}/","method":2,"menu_id":12},
|
||||||
{"id":6,"name":"修改密码","value":"Changepassword","api":"/api/system/user/change_password/{id}/","method":2,"menu":21},
|
{"id":6,"name":"修改密码","value":"Changepassword","api":"/api/system/user/change_password/{id}/","method":2,"menu_id":21},
|
||||||
{"id":7,"name":"编辑","value":"Update","api":"/api/users/users/{id}/","method":2,"menu":2},
|
{"id":7,"name":"编辑","value":"Update","api":"/api/users/users/{id}/","method":2,"menu_id":2},
|
||||||
{"id":8,"name":"编辑","value":"Update","api":"/api/system/user/{id}/","method":2,"menu":1},
|
{"id":8,"name":"编辑","value":"Update","api":"/api/system/user/{id}/","method":2,"menu_id":1},
|
||||||
{"id":9,"name":"编辑","value":"Update","api":"/api/system/menu_button/{id}/","method":2,"menu":16},
|
{"id":9,"name":"编辑","value":"Update","api":"/api/system/menu_button/{id}/","method":2,"menu_id":16},
|
||||||
{"id":10,"name":"编辑","value":"Update","api":"/api/system/role/{id}/","method":2,"menu":17},
|
{"id":10,"name":"编辑","value":"Update","api":"/api/system/role/{id}/","method":2,"menu_id":17},
|
||||||
{"id":11,"name":"编辑","value":"Update","api":"/api/system/user/user_info/","method":2,"menu":21},
|
{"id":11,"name":"编辑","value":"Update","api":"/api/system/user/user_info/","method":2,"menu_id":21},
|
||||||
{"id":12,"name":"编辑","value":"Update","api":"/api/system/operation_log/{id}/","method":2,"menu":14},
|
{"id":12,"name":"编辑","value":"Update","api":"/api/system/operation_log/{id}/","method":2,"menu_id":14},
|
||||||
{"id":13,"name":"编辑","value":"Update","api":"/api/messages/messagenotice/{id}/","method":2,"menu":8},
|
{"id":13,"name":"编辑","value":"Update","api":"/api/messages/messagenotice/{id}/","method":2,"menu_id":8},
|
||||||
{"id":14,"name":"查询","value":"Search","api":"/api/platformsettings/lunboimg/","method":0,"menu":7},
|
{"id":14,"name":"查询","value":"Search","api":"/api/platformsettings/lunboimg/","method":0,"menu_id":7},
|
||||||
{"id":15,"name":"查询","value":"Search","api":"/api/platformsettings/other/","method":0,"menu":9},
|
{"id":15,"name":"查询","value":"Search","api":"/api/platformsettings/other/","method":0,"menu_id":9},
|
||||||
{"id":16,"name":"查询","value":"Search","api":"/api/system/role/","method":0,"menu":17},
|
{"id":16,"name":"查询","value":"Search","api":"/api/system/role/","method":0,"menu_id":17},
|
||||||
{"id":17,"name":"查询","value":"Search","api":"/api/system/user/","method":0,"menu":1},
|
{"id":17,"name":"查询","value":"Search","api":"/api/system/user/","method":0,"menu_id":1},
|
||||||
{"id":18,"name":"查询","value":"Search","api":"/api/system/user/user_info/","method":0,"menu":21},
|
{"id":18,"name":"查询","value":"Search","api":"/api/system/user/user_info/","method":0,"menu_id":21},
|
||||||
{"id":19,"name":"查询","value":"Search","api":"/api/system/operation_log/","method":0,"menu":14},
|
{"id":19,"name":"查询","value":"Search","api":"/api/system/operation_log/","method":0,"menu_id":14},
|
||||||
{"id":20,"name":"查询","value":"Search","api":"/api/system/menu/","method":0,"menu":15},
|
{"id":20,"name":"查询","value":"Search","api":"/api/system/menu/","method":0,"menu_id":15},
|
||||||
{"id":21,"name":"查询","value":"Search","api":"/api/users/users/","method":0,"menu":2},
|
{"id":21,"name":"查询","value":"Search","api":"/api/users/users/","method":0,"menu_id":2},
|
||||||
{"id":22,"name":"查询","value":"Search","api":"/api/system/menu_button/","method":0,"menu":16},
|
{"id":22,"name":"查询","value":"Search","api":"/api/system/menu_button/","method":0,"menu_id":16},
|
||||||
{"id":23,"name":"查询","value":"Search","api":"/api/system/dept/","method":0,"menu":12},
|
{"id":23,"name":"查询","value":"Search","api":"/api/system/dept/","method":0,"menu_id":12},
|
||||||
{"id":24,"name":"查询","value":"Search","api":"/api/system/button/","method":0,"menu":19},
|
{"id":24,"name":"查询","value":"Search","api":"/api/system/button/","method":0,"menu_id":19},
|
||||||
{"id":25,"name":"查询","value":"Search","api":"/api/messages/messagenotice/","method":0,"menu":8},
|
{"id":25,"name":"查询","value":"Search","api":"/api/messages/messagenotice/","method":0,"menu_id":8},
|
||||||
{"id":26,"name":"新增","value":"Create","api":"/api/platformsettings/lunboimg/","method":1,"menu":7},
|
{"id":26,"name":"新增","value":"Create","api":"/api/platformsettings/lunboimg/","method":1,"menu_id":7},
|
||||||
{"id":27,"name":"新增","value":"Create","api":"/api/platformsettings/other/","method":1,"menu":9},
|
{"id":27,"name":"新增","value":"Create","api":"/api/platformsettings/other/","method":1,"menu_id":9},
|
||||||
{"id":28,"name":"新增","value":"Create","api":"/api/system/operation_log/","method":1,"menu":14},
|
{"id":28,"name":"新增","value":"Create","api":"/api/system/operation_log/","method":1,"menu_id":14},
|
||||||
{"id":29,"name":"新增","value":"Create","api":"/api/system/dept/","method":1,"menu":12},
|
{"id":29,"name":"新增","value":"Create","api":"/api/system/dept/","method":1,"menu_id":12},
|
||||||
{"id":30,"name":"新增","value":"Create","api":"/api/system/button/","method":1,"menu":19},
|
{"id":30,"name":"新增","value":"Create","api":"/api/system/button/","method":1,"menu_id":19},
|
||||||
{"id":31,"name":"新增","value":"Create","api":"/api/system/role/","method":1,"menu":17},
|
{"id":31,"name":"新增","value":"Create","api":"/api/system/role/","method":1,"menu_id":17},
|
||||||
{"id":32,"name":"新增","value":"Create","api":"/api/system/user/","method":1,"menu":1},
|
{"id":32,"name":"新增","value":"Create","api":"/api/system/user/","method":1,"menu_id":1},
|
||||||
{"id":33,"name":"新增","value":"Create","api":"/api/users/users/","method":1,"menu":2},
|
{"id":33,"name":"新增","value":"Create","api":"/api/users/users/","method":1,"menu_id":2},
|
||||||
{"id":34,"name":"新增","value":"Create","api":"/api/system/menu/","method":1,"menu":15},
|
{"id":34,"name":"新增","value":"Create","api":"/api/system/menu/","method":1,"menu_id":15},
|
||||||
{"id":35,"name":"新增","value":"Create","api":"/api/system/menu_button/","method":1,"menu":16},
|
{"id":35,"name":"新增","value":"Create","api":"/api/system/menu_button/","method":1,"menu_id":16},
|
||||||
{"id":36,"name":"新增","value":"Create","api":"/api/messages/messagenotice/","method":1,"menu":8},
|
{"id":36,"name":"新增","value":"Create","api":"/api/messages/messagenotice/","method":1,"menu_id":8},
|
||||||
{"id":37,"name":"单例","value":"Retrieve","api":"/api/platformsettings/lunboimg/{id}/","method":0,"menu":7},
|
{"id":37,"name":"单例","value":"Retrieve","api":"/api/platformsettings/lunboimg/{id}/","method":0,"menu_id":7},
|
||||||
{"id":38,"name":"单例","value":"Retrieve","api":"/api/platformsettings/other/{id}/","method":0,"menu":9},
|
{"id":38,"name":"单例","value":"Retrieve","api":"/api/platformsettings/other/{id}/","method":0,"menu_id":9},
|
||||||
{"id":39,"name":"单例","value":"Retrieve","api":"/api/users/users/{id}/","method":0,"menu":2},
|
{"id":39,"name":"单例","value":"Retrieve","api":"/api/users/users/{id}/","method":0,"menu_id":2},
|
||||||
{"id":40,"name":"单例","value":"Retrieve","api":"/api/system/button/{id}/","method":0,"menu":19},
|
{"id":40,"name":"单例","value":"Retrieve","api":"/api/system/button/{id}/","method":0,"menu_id":19},
|
||||||
{"id":41,"name":"单例","value":"Retrieve","api":"/api/system/dept/{id}/","method":0,"menu":12},
|
{"id":41,"name":"单例","value":"Retrieve","api":"/api/system/dept/{id}/","method":0,"menu_id":12},
|
||||||
{"id":42,"name":"单例","value":"Retrieve","api":"/api/system/operation_log/{id}/","method":0,"menu":14},
|
{"id":42,"name":"单例","value":"Retrieve","api":"/api/system/operation_log/{id}/","method":0,"menu_id":14},
|
||||||
{"id":43,"name":"单例","value":"Retrieve","api":"/api/system/role/{id}/","method":0,"menu":17},
|
{"id":43,"name":"单例","value":"Retrieve","api":"/api/system/role/{id}/","method":0,"menu_id":17},
|
||||||
{"id":44,"name":"单例","value":"Retrieve","api":"/api/system/user/{id}/","method":0,"menu":1},
|
{"id":44,"name":"单例","value":"Retrieve","api":"/api/system/user/{id}/","method":0,"menu_id":1},
|
||||||
{"id":45,"name":"单例","value":"Retrieve","api":"/api/system/menu/{id}/","method":0,"menu":15},
|
{"id":45,"name":"单例","value":"Retrieve","api":"/api/system/menu/{id}/","method":0,"menu_id":15},
|
||||||
{"id":46,"name":"单例","value":"Retrieve","api":"/api/system/menu_button/{id}/","method":0,"menu":16},
|
{"id":46,"name":"单例","value":"Retrieve","api":"/api/system/menu_button/{id}/","method":0,"menu_id":16},
|
||||||
{"id":47,"name":"单例","value":"Retrieve","api":"/api/messages/messagenotice/{id}/","method":0,"menu":8},
|
{"id":47,"name":"单例","value":"Retrieve","api":"/api/messages/messagenotice/{id}/","method":0,"menu_id":8},
|
||||||
{"id":48,"name":"单例","value":"Retrieve","api":"/api/system/role_id_to_menu/{id}/","method":0,"menu":18},
|
{"id":48,"name":"单例","value":"Retrieve","api":"/api/system/role_id_to_menu/{id}/","method":0,"menu_id":18},
|
||||||
{"id":49,"name":"删除","value":"Delete","api":"/api/platformsettings/lunboimg/{id}/","method":3,"menu":7},
|
{"id":49,"name":"删除","value":"Delete","api":"/api/platformsettings/lunboimg/{id}/","method":3,"menu_id":7},
|
||||||
{"id":50,"name":"删除","value":"Delete","api":"/api/platformsettings/other/{id}/","method":3,"menu":9},
|
{"id":50,"name":"删除","value":"Delete","api":"/api/platformsettings/other/{id}/","method":3,"menu_id":9},
|
||||||
{"id":51,"name":"删除","value":"Delete","api":"/api/system/user/{id}/","method":3,"menu":1},
|
{"id":51,"name":"删除","value":"Delete","api":"/api/system/user/{id}/","method":3,"menu_id":1},
|
||||||
{"id":52,"name":"删除","value":"Delete","api":"/api/system/role/{id}/","method":3,"menu":17},
|
{"id":52,"name":"删除","value":"Delete","api":"/api/system/role/{id}/","method":3,"menu_id":17},
|
||||||
{"id":53,"name":"删除","value":"Delete","api":"/api/system/menu_button/{id}/","method":3,"menu":16},
|
{"id":53,"name":"删除","value":"Delete","api":"/api/system/menu_button/{id}/","method":3,"menu_id":16},
|
||||||
{"id":54,"name":"删除","value":"Delete","api":"/api/system/button/{id}/","method":3,"menu":19},
|
{"id":54,"name":"删除","value":"Delete","api":"/api/system/button/{id}/","method":3,"menu_id":19},
|
||||||
{"id":55,"name":"删除","value":"Delete","api":"/api/system/menu/{id}/","method":3,"menu":15},
|
{"id":55,"name":"删除","value":"Delete","api":"/api/system/menu/{id}/","method":3,"menu_id":15},
|
||||||
{"id":56,"name":"删除","value":"Delete","api":"/api/system/operation_log/{id}/","method":3,"menu":14},
|
{"id":56,"name":"删除","value":"Delete","api":"/api/system/operation_log/{id}/","method":3,"menu_id":14},
|
||||||
{"id":57,"name":"删除","value":"Delete","api":"/api/system/dept/{id}/","method":3,"menu":12},
|
{"id":57,"name":"删除","value":"Delete","api":"/api/system/dept/{id}/","method":3,"menu_id":12},
|
||||||
{"id":58,"name":"删除","value":"Delete","api":"/api/users/users/{id}/","method":3,"menu":2},
|
{"id":58,"name":"删除","value":"Delete","api":"/api/users/users/{id}/","method":3,"menu_id":2},
|
||||||
{"id":59,"name":"删除","value":"Delete","api":"/api/messages/messagenotice/{id}/","method":3,"menu":8},
|
{"id":59,"name":"删除","value":"Delete","api":"/api/messages/messagenotice/{id}/","method":3,"menu_id":8},
|
||||||
{"id":61,"name":"禁用","value":"Disable","api":"/api/users/users/disableuser/{id}/","method":2,"menu":2},
|
{"id":61,"name":"禁用","value":"Disable","api":"/api/users/users/disableuser/{id}/","method":2,"menu_id":2},
|
||||||
{"id":62,"name":"编辑","value":"Update","api":"/api/system/user/{id}/","method":2,"menu":4},
|
{"id":62,"name":"编辑","value":"Update","api":"/api/system/user/{id}/","method":2,"menu_id":4},
|
||||||
{"id":63,"name":"禁用","value":"Disable","api":"/api/users/users/disableuser/{id}/","method":2,"menu":4},
|
{"id":63,"name":"禁用","value":"Disable","api":"/api/users/users/disableuser/{id}/","method":2,"menu_id":4},
|
||||||
{"id":64,"name":"查询","value":"Search","api":"/api/system/user/","method":0,"menu":4},
|
{"id":64,"name":"查询","value":"Search","api":"/api/system/user/","method":0,"menu_id":4},
|
||||||
{"id":65,"name":"新增","value":"Create","api":"/api/system/user/","method":1,"menu":4},
|
{"id":65,"name":"新增","value":"Create","api":"/api/system/user/","method":1,"menu_id":4},
|
||||||
{"id":68,"name":"查询","value":"Search","api":"","method":0,"menu":23},
|
{"id":68,"name":"查询","value":"Search","api":"","method":0,"menu_id":23},
|
||||||
{"id":70,"name":"编辑","value":"Update","api":"/api/crontab/periodictask/{id}/","method":2,"menu":24},
|
{"id":70,"name":"编辑","value":"Update","api":"/api/crontab/periodictask/{id}/","method":2,"menu_id":24},
|
||||||
{"id":71,"name":"禁用","value":"Disable","api":"/api/crontab/periodictask/enabled/{id}/","method":2,"menu":24},
|
{"id":71,"name":"禁用","value":"Disable","api":"/api/crontab/periodictask/enabled/{id}/","method":2,"menu_id":24},
|
||||||
{"id":72,"name":"查询","value":"Search","api":"/api/crontab/periodictask/","method":0,"menu":24},
|
{"id":72,"name":"查询","value":"Search","api":"/api/crontab/periodictask/","method":0,"menu_id":24},
|
||||||
{"id":76,"name":"查询","value":"Search","api":"/api/monitor/getsysteminfo/","method":0,"menu":26},
|
{"id":76,"name":"查询","value":"Search","api":"/api/monitor/getsysteminfo/","method":0,"menu_id":26},
|
||||||
{"id":78,"name":"查询","value":"Search","api":"/api/terminal/terminal/","method":0,"menu":27},
|
{"id":78,"name":"查询","value":"Search","api":"/api/terminal/terminal/","method":0,"menu_id":27},
|
||||||
{"id":66,"name":"单例","value":"Retrieve","api":"/api/system/user/{id}/","method":0,"menu":4},
|
{"id":66,"name":"单例","value":"Retrieve","api":"/api/system/user/{id}/","method":0,"menu_id":4},
|
||||||
{"id":69,"name":"单例","value":"Retrieve","api":"","method":0,"menu":23},
|
{"id":69,"name":"单例","value":"Retrieve","api":"","method":0,"menu_id":23},
|
||||||
{"id":74,"name":"单例","value":"Retrieve","api":"/api/crontab/periodictask/{id}/","method":0,"menu":24},
|
{"id":74,"name":"单例","value":"Retrieve","api":"/api/crontab/periodictask/{id}/","method":0,"menu_id":24},
|
||||||
{"id":82,"name":"编辑","value":"Update","api":"/api/terminal/terminal/{id}/","method":2,"menu":27},
|
{"id":82,"name":"编辑","value":"Update","api":"/api/terminal/terminal/{id}/","method":2,"menu_id":27},
|
||||||
{"id":83,"name":"编辑","value":"Update","api":"/api/address/area/{id}/","method":2,"menu":28},
|
{"id":83,"name":"编辑","value":"Update","api":"/api/address/area/{id}/","method":2,"menu_id":28},
|
||||||
{"id":89,"name":"编辑","value":"Update","api":"/api/mall/goodsspu/{id}/","method":2,"menu":30},
|
{"id":89,"name":"编辑","value":"Update","api":"/api/mall/goodsspu/{id}/","method":2,"menu_id":30},
|
||||||
{"id":96,"name":"编辑","value":"Update","api":"/api/mall/goodstype/{id}/","method":2,"menu":31},
|
{"id":96,"name":"编辑","value":"Update","api":"/api/mall/goodstype/{id}/","method":2,"menu_id":31},
|
||||||
{"id":100,"name":"编辑","value":"Update","api":"/api/mall/goodsorder/{id}/","method":2,"menu":32},
|
{"id":100,"name":"编辑","value":"Update","api":"/api/mall/goodsorder/{id}/","method":2,"menu_id":32},
|
||||||
{"id":168,"name":"菜单配置","value":"MenuConfig","api":"/api/lyformbuilder/lyformbuilder/editMenu/","method":1,"menu":43},
|
{"id":168,"name":"菜单配置","value":"MenuConfig","api":"/api/lyformbuilder/lyformbuilder/editMenu/","method":1,"menu_id":43},
|
||||||
{"id":153,"name":"获取模型","value":"GetModels","api":"/api/platformsettings/sysconfig/get_models_info_list/","method":0,"menu":36},
|
{"id":153,"name":"获取模型","value":"GetModels","api":"/api/platformsettings/sysconfig/get_models_info_list/","method":0,"menu_id":36},
|
||||||
{"id":154,"name":"获取模型","value":"GetModels","api":"/api/platformsettings/sysconfig/get_models_info_list/","method":0,"menu":34},
|
{"id":154,"name":"获取模型","value":"GetModels","api":"/api/platformsettings/sysconfig/get_models_info_list/","method":0,"menu_id":34},
|
||||||
{"id":117,"name":"编辑","value":"Update","api":"/api/platformsettings/sysconfig/{id}/","method":2,"menu":6},
|
{"id":117,"name":"编辑","value":"Update","api":"/api/platformsettings/sysconfig/{id}/","method":2,"menu_id":6},
|
||||||
{"id":121,"name":"编辑","value":"Update","api":"/api/autocode/autocode/{id}/","method":2,"menu":36},
|
{"id":121,"name":"编辑","value":"Update","api":"/api/autocode/autocode/{id}/","method":2,"menu_id":36},
|
||||||
{"id":129,"name":"编辑","value":"Update","api":"/api/system/dictionary/{id}/","method":2,"menu":37},
|
{"id":129,"name":"编辑","value":"Update","api":"/api/system/dictionary/{id}/","method":2,"menu_id":37},
|
||||||
{"id":130,"name":"编辑","value":"Update","api":"/api/system/appversion/{id}/","method":2,"menu":11},
|
{"id":130,"name":"编辑","value":"Update","api":"/api/system/appversion/{id}/","method":2,"menu_id":11},
|
||||||
{"id":137,"name":"编辑","value":"Update","api":"/api/autocode/StudentManage/{id}/","method":2,"menu":38},
|
{"id":137,"name":"编辑","value":"Update","api":"/api/autocode/StudentManage/{id}/","method":2,"menu_id":38},
|
||||||
{"id":147,"name":"编辑","value":"Update","api":"","method":2,"menu":39},
|
{"id":147,"name":"编辑","value":"Update","api":"","method":2,"menu_id":39},
|
||||||
{"id":152,"name":"编辑","value":"Update","api":"","method":2,"menu":40},
|
{"id":152,"name":"编辑","value":"Update","api":"","method":2,"menu_id":40},
|
||||||
{"id":162,"name":"编辑","value":"Update","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":2,"menu":34},
|
{"id":162,"name":"编辑","value":"Update","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":2,"menu_id":34},
|
||||||
{"id":174,"name":"编辑","value":"Update","api":"/api/lyformbuilder/teacherManage/{id}/","method":2,"menu":44},
|
{"id":174,"name":"编辑","value":"Update","api":"/api/lyformbuilder/teacherManage/{id}/","method":2,"menu_id":44},
|
||||||
{"id":177,"name":"编辑","value":"Update","api":"/api/system/user/user_info/","method":2,"menu":47},
|
{"id":177,"name":"编辑","value":"Update","api":"/api/system/user/user_info/","method":2,"menu_id":47},
|
||||||
{"id":183,"name":"编辑","value":"Update","api":"/api/mall/freightcfg/{id}/","method":2,"menu":48},
|
{"id":183,"name":"编辑","value":"Update","api":"/api/mall/freightcfg/{id}/","method":2,"menu_id":48},
|
||||||
{"id":188,"name":"编辑","value":"Update","api":"","method":2,"menu":50},
|
{"id":188,"name":"编辑","value":"Update","api":"","method":2,"menu_id":50},
|
||||||
{"id":109,"name":"统计","value":"Statistics","api":"/api/mall/goodsorder/orderstatistics/","method":0,"menu":32},
|
{"id":109,"name":"统计","value":"Statistics","api":"/api/mall/goodsorder/orderstatistics/","method":0,"menu_id":32},
|
||||||
{"id":111,"name":"统计","value":"Statistics","api":"/api/mall/goodsforderinfo/orderstatistics/","method":0,"menu":33},
|
{"id":111,"name":"统计","value":"Statistics","api":"/api/mall/goodsforderinfo/orderstatistics/","method":0,"menu_id":33},
|
||||||
{"id":77,"name":"终端","value":"Terminal","api":"/ws/webssh/","method":5,"menu":27},
|
{"id":77,"name":"终端","value":"Terminal","api":"/ws/webssh/","method":5,"menu_id":27},
|
||||||
{"id":176,"name":"立即执行","value":"Execute","api":"/api/crontab/periodictask/exectask/","method":1,"menu":24},
|
{"id":176,"name":"立即执行","value":"Execute","api":"/api/crontab/periodictask/exectask/","method":1,"menu_id":24},
|
||||||
{"id":108,"name":"禁用","value":"Disable","api":"/api/mall/goodsspu/islaunched/{id}/","method":2,"menu":30},
|
{"id":108,"name":"禁用","value":"Disable","api":"/api/mall/goodsspu/islaunched/{id}/","method":2,"menu_id":30},
|
||||||
{"id":84,"name":"查询","value":"Search","api":"/api/address/area/area_root/","method":0,"menu":28},
|
{"id":84,"name":"查询","value":"Search","api":"/api/address/area/area_root/","method":0,"menu_id":28},
|
||||||
{"id":90,"name":"查询","value":"Search","api":"/api/mall/goodsspu/","method":0,"menu":30},
|
{"id":90,"name":"查询","value":"Search","api":"/api/mall/goodsspu/","method":0,"menu_id":30},
|
||||||
{"id":94,"name":"查询","value":"Search","api":"/api/mall/goodstype/","method":0,"menu":31},
|
{"id":94,"name":"查询","value":"Search","api":"/api/mall/goodstype/","method":0,"menu_id":31},
|
||||||
{"id":101,"name":"查询","value":"Search","api":"/api/mall/goodsorder/","method":0,"menu":32},
|
{"id":101,"name":"查询","value":"Search","api":"/api/mall/goodsorder/","method":0,"menu_id":32},
|
||||||
{"id":102,"name":"查询","value":"Search","api":"/api/mall/goodsforderinfo/","method":0,"menu":33},
|
{"id":102,"name":"查询","value":"Search","api":"/api/mall/goodsforderinfo/","method":0,"menu_id":33},
|
||||||
{"id":105,"name":"查询","value":"Search","api":"/api/platformsettings/userfeeckback/","method":0,"menu":10},
|
{"id":105,"name":"查询","value":"Search","api":"/api/platformsettings/userfeeckback/","method":0,"menu_id":10},
|
||||||
{"id":118,"name":"查询","value":"Search","api":"/api/platformsettings/sysconfig/","method":0,"menu":6},
|
{"id":118,"name":"查询","value":"Search","api":"/api/platformsettings/sysconfig/","method":0,"menu_id":6},
|
||||||
{"id":124,"name":"查询","value":"Search","api":"/api/autocode/autocode/","method":0,"menu":36},
|
{"id":124,"name":"查询","value":"Search","api":"/api/autocode/autocode/","method":0,"menu_id":36},
|
||||||
{"id":128,"name":"查询","value":"Search","api":"/api/system/dictionary/","method":0,"menu":37},
|
{"id":128,"name":"查询","value":"Search","api":"/api/system/dictionary/","method":0,"menu_id":37},
|
||||||
{"id":132,"name":"查询","value":"Search","api":"/api/system/appversion/","method":0,"menu":11},
|
{"id":132,"name":"查询","value":"Search","api":"/api/system/appversion/","method":0,"menu_id":11},
|
||||||
{"id":140,"name":"查询","value":"Search","api":"/api/autocode/StudentManage/","method":0,"menu":38},
|
{"id":140,"name":"查询","value":"Search","api":"/api/autocode/StudentManage/","method":0,"menu_id":38},
|
||||||
{"id":143,"name":"查询","value":"Search","api":"","method":0,"menu":39},
|
{"id":143,"name":"查询","value":"Search","api":"","method":0,"menu_id":39},
|
||||||
{"id":151,"name":"查询","value":"Search","api":"","method":0,"menu":40},
|
{"id":151,"name":"查询","value":"Search","api":"","method":0,"menu_id":40},
|
||||||
{"id":157,"name":"查询","value":"Search","api":"/api/system/operation_log/systemlog/","method":0,"menu":41},
|
{"id":157,"name":"查询","value":"Search","api":"/api/system/operation_log/systemlog/","method":0,"menu_id":41},
|
||||||
{"id":159,"name":"查询","value":"Search","api":"/api/platformsettings/other/functionSets/","method":0,"menu":42},
|
{"id":159,"name":"查询","value":"Search","api":"/api/platformsettings/other/functionSets/","method":0,"menu_id":42},
|
||||||
{"id":161,"name":"查询","value":"Search","api":"/api/lyformbuilder/lyformbuilder/","method":0,"menu":43},
|
{"id":161,"name":"查询","value":"Search","api":"/api/lyformbuilder/lyformbuilder/","method":0,"menu_id":43},
|
||||||
{"id":173,"name":"查询","value":"Search","api":"/api/lyformbuilder/teacherManage/","method":0,"menu":44},
|
{"id":173,"name":"查询","value":"Search","api":"/api/lyformbuilder/teacherManage/","method":0,"menu_id":44},
|
||||||
{"id":175,"name":"查询","value":"Search","api":"/api/monitor/monitor/getredisinfo/","method":0,"menu":45},
|
{"id":175,"name":"查询","value":"Search","api":"/api/monitor/monitor/getredisinfo/","method":0,"menu_id":45},
|
||||||
{"id":178,"name":"查询","value":"Search","api":"/api/system/user/user_info/","method":0,"menu":47},
|
{"id":178,"name":"查询","value":"Search","api":"/api/system/user/user_info/","method":0,"menu_id":47},
|
||||||
{"id":184,"name":"查询","value":"Search","api":"/api/mall/freightcfg/","method":0,"menu":48},
|
{"id":184,"name":"查询","value":"Search","api":"/api/mall/freightcfg/","method":0,"menu_id":48},
|
||||||
{"id":189,"name":"查询","value":"Search","api":"","method":0,"menu":50},
|
{"id":189,"name":"查询","value":"Search","api":"","method":0,"menu_id":50},
|
||||||
{"id":112,"name":"日志","value":"Logs","api":"/api/crontab/taskresult/","method":0,"menu":24},
|
{"id":112,"name":"日志","value":"Logs","api":"/api/crontab/taskresult/","method":0,"menu_id":24},
|
||||||
{"id":180,"name":"日志","value":"Logs","api":"/api/system/operation_log/getOwnerLogs/","method":0,"menu":47},
|
{"id":180,"name":"日志","value":"Logs","api":"/api/system/operation_log/getOwnerLogs/","method":0,"menu_id":47},
|
||||||
{"id":73,"name":"新增","value":"Create","api":"/api/crontab/periodictask/","method":1,"menu":24},
|
{"id":73,"name":"新增","value":"Create","api":"/api/crontab/periodictask/","method":1,"menu_id":24},
|
||||||
{"id":79,"name":"新增","value":"Create","api":"/api/terminal/terminal/","method":1,"menu":27},
|
{"id":79,"name":"新增","value":"Create","api":"/api/terminal/terminal/","method":1,"menu_id":27},
|
||||||
{"id":85,"name":"新增","value":"Create","api":"/api/address/area/","method":1,"menu":28},
|
{"id":85,"name":"新增","value":"Create","api":"/api/address/area/","method":1,"menu_id":28},
|
||||||
{"id":88,"name":"新增","value":"Create","api":"/api/mall/goodsspu/","method":1,"menu":30},
|
{"id":88,"name":"新增","value":"Create","api":"/api/mall/goodsspu/","method":1,"menu_id":30},
|
||||||
{"id":95,"name":"新增","value":"Create","api":"/api/mall/goodstype/","method":1,"menu":31},
|
{"id":95,"name":"新增","value":"Create","api":"/api/mall/goodstype/","method":1,"menu_id":31},
|
||||||
{"id":114,"name":"新增","value":"Create","api":"/api/platformsettings/sysconfig/","method":1,"menu":6},
|
{"id":114,"name":"新增","value":"Create","api":"/api/platformsettings/sysconfig/","method":1,"menu_id":6},
|
||||||
{"id":120,"name":"新增","value":"Create","api":"/api/autocode/autocode/","method":1,"menu":36},
|
{"id":120,"name":"新增","value":"Create","api":"/api/autocode/autocode/","method":1,"menu_id":36},
|
||||||
{"id":125,"name":"新增","value":"Create","api":"/api/system/dictionary/","method":1,"menu":37},
|
{"id":125,"name":"新增","value":"Create","api":"/api/system/dictionary/","method":1,"menu_id":37},
|
||||||
{"id":134,"name":"新增","value":"Create","api":"/api/system/appversion/","method":1,"menu":11},
|
{"id":134,"name":"新增","value":"Create","api":"/api/system/appversion/","method":1,"menu_id":11},
|
||||||
{"id":139,"name":"新增","value":"Create","api":"/api/autocode/StudentManage/","method":1,"menu":38},
|
{"id":139,"name":"新增","value":"Create","api":"/api/autocode/StudentManage/","method":1,"menu_id":38},
|
||||||
{"id":144,"name":"新增","value":"Create","api":"","method":1,"menu":39},
|
{"id":144,"name":"新增","value":"Create","api":"","method":1,"menu_id":39},
|
||||||
{"id":149,"name":"新增","value":"Create","api":"","method":1,"menu":40},
|
{"id":149,"name":"新增","value":"Create","api":"","method":1,"menu_id":40},
|
||||||
{"id":163,"name":"新增","value":"Create","api":"/api/lyformbuilder/lyformbuilder/","method":1,"menu":34},
|
{"id":163,"name":"新增","value":"Create","api":"/api/lyformbuilder/lyformbuilder/","method":1,"menu_id":34},
|
||||||
{"id":170,"name":"新增","value":"Create","api":"/api/lyformbuilder/teacherManage/","method":1,"menu":44},
|
{"id":170,"name":"新增","value":"Create","api":"/api/lyformbuilder/teacherManage/","method":1,"menu_id":44},
|
||||||
{"id":181,"name":"新增","value":"Create","api":"/api/mall/freightcfg/","method":1,"menu":48},
|
{"id":181,"name":"新增","value":"Create","api":"/api/mall/freightcfg/","method":1,"menu_id":48},
|
||||||
{"id":186,"name":"新增","value":"Create","api":"","method":1,"menu":50},
|
{"id":186,"name":"新增","value":"Create","api":"","method":1,"menu_id":50},
|
||||||
{"id":136,"name":"挂载同步","value":"MountSync","api":"/api/autocode/autocode/generatemount/","method":1,"menu":36},
|
{"id":136,"name":"挂载同步","value":"MountSync","api":"/api/autocode/autocode/generatemount/","method":1,"menu_id":36},
|
||||||
{"id":156,"name":"导出","value":"Export","api":"/api/mall/goodsspu/export/","method":1,"menu":30},
|
{"id":156,"name":"导出","value":"Export","api":"/api/mall/goodsspu/export/","method":1,"menu_id":30},
|
||||||
{"id":158,"name":"导出","value":"Export","api":"/api/autocode/StudentManage/export/","method":1,"menu":38},
|
{"id":158,"name":"导出","value":"Export","api":"/api/autocode/StudentManage/export/","method":1,"menu_id":38},
|
||||||
{"id":172,"name":"导出","value":"Export","api":"/api/lyformbuilder/teacherManage/export/","method":1,"menu":44},
|
{"id":172,"name":"导出","value":"Export","api":"/api/lyformbuilder/teacherManage/export/","method":1,"menu_id":44},
|
||||||
{"id":142,"name":"同步数据库","value":"Syncdb","api":"/api/autocode/autocode/syncdb/","method":1,"menu":36},
|
{"id":142,"name":"同步数据库","value":"Syncdb","api":"/api/autocode/autocode/syncdb/","method":1,"menu_id":36},
|
||||||
{"id":166,"name":"同步数据库","value":"Syncdb","api":"/api/lyformbuilder/lyformbuilder/syncdb/","method":1,"menu":43},
|
{"id":166,"name":"同步数据库","value":"Syncdb","api":"/api/lyformbuilder/lyformbuilder/syncdb/","method":1,"menu_id":43},
|
||||||
{"id":165,"name":"同步挂载","value":"MountSync","api":"/api/lyformbuilder/lyformbuilder/generatemount/","method":1,"menu":43},
|
{"id":165,"name":"同步挂载","value":"MountSync","api":"/api/lyformbuilder/lyformbuilder/generatemount/","method":1,"menu_id":43},
|
||||||
{"id":110,"name":"发货","value":"Deliver","api":"/api/mall/goodsorder/sendoutgoods/","method":1,"menu":32},
|
{"id":110,"name":"发货","value":"Deliver","api":"/api/mall/goodsorder/sendoutgoods/","method":1,"menu_id":32},
|
||||||
{"id":80,"name":"单例","value":"Retrieve","api":"/api/terminal/terminal/{id}/","method":0,"menu":27},
|
{"id":80,"name":"单例","value":"Retrieve","api":"/api/terminal/terminal/{id}/","method":0,"menu_id":27},
|
||||||
{"id":86,"name":"单例","value":"Retrieve","api":"/api/address/area/{id}/","method":0,"menu":28},
|
{"id":86,"name":"单例","value":"Retrieve","api":"/api/address/area/{id}/","method":0,"menu_id":28},
|
||||||
{"id":91,"name":"单例","value":"Retrieve","api":"/api/mall/goodsspu/{id}/","method":0,"menu":30},
|
{"id":91,"name":"单例","value":"Retrieve","api":"/api/mall/goodsspu/{id}/","method":0,"menu_id":30},
|
||||||
{"id":93,"name":"单例","value":"Retrieve","api":"/api/mall/goodstype/{id}/","method":0,"menu":31},
|
{"id":93,"name":"单例","value":"Retrieve","api":"/api/mall/goodstype/{id}/","method":0,"menu_id":31},
|
||||||
{"id":98,"name":"单例","value":"Retrieve","api":"/api/mall/goodsorder/{id}/","method":0,"menu":32},
|
{"id":98,"name":"单例","value":"Retrieve","api":"/api/mall/goodsorder/{id}/","method":0,"menu_id":32},
|
||||||
{"id":103,"name":"单例","value":"Retrieve","api":"/api/mall/goodsforderinfo/{id}/","method":0,"menu":33},
|
{"id":103,"name":"单例","value":"Retrieve","api":"/api/mall/goodsforderinfo/{id}/","method":0,"menu_id":33},
|
||||||
{"id":106,"name":"单例","value":"Retrieve","api":"/api/platformsettings/userfeeckback/{id}/","method":0,"menu":10},
|
{"id":106,"name":"单例","value":"Retrieve","api":"/api/platformsettings/userfeeckback/{id}/","method":0,"menu_id":10},
|
||||||
{"id":115,"name":"单例","value":"Retrieve","api":"/api/platformsettings/sysconfig/{id}/","method":0,"menu":6},
|
{"id":115,"name":"单例","value":"Retrieve","api":"/api/platformsettings/sysconfig/{id}/","method":0,"menu_id":6},
|
||||||
{"id":122,"name":"单例","value":"Retrieve","api":"/api/autocode/autocode/{id}/","method":0,"menu":36},
|
{"id":122,"name":"单例","value":"Retrieve","api":"/api/autocode/autocode/{id}/","method":0,"menu_id":36},
|
||||||
{"id":126,"name":"单例","value":"Retrieve","api":"/api/system/dictionary/{id}/","method":0,"menu":37},
|
{"id":126,"name":"单例","value":"Retrieve","api":"/api/system/dictionary/{id}/","method":0,"menu_id":37},
|
||||||
{"id":133,"name":"单例","value":"Retrieve","api":"/api/system/appversion/{id}/","method":0,"menu":11},
|
{"id":133,"name":"单例","value":"Retrieve","api":"/api/system/appversion/{id}/","method":0,"menu_id":11},
|
||||||
{"id":141,"name":"单例","value":"Retrieve","api":"/api/autocode/StudentManage/{id}/","method":0,"menu":38},
|
{"id":141,"name":"单例","value":"Retrieve","api":"/api/autocode/StudentManage/{id}/","method":0,"menu_id":38},
|
||||||
{"id":146,"name":"单例","value":"Retrieve","api":"","method":0,"menu":39},
|
{"id":146,"name":"单例","value":"Retrieve","api":"","method":0,"menu_id":39},
|
||||||
{"id":150,"name":"单例","value":"Retrieve","api":"","method":0,"menu":40},
|
{"id":150,"name":"单例","value":"Retrieve","api":"","method":0,"menu_id":40},
|
||||||
{"id":164,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":0,"menu":43},
|
{"id":164,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":0,"menu_id":43},
|
||||||
{"id":167,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":0,"menu":34},
|
{"id":167,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":0,"menu_id":34},
|
||||||
{"id":169,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/teacherManage/{id}/","method":0,"menu":44},
|
{"id":169,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/teacherManage/{id}/","method":0,"menu_id":44},
|
||||||
{"id":185,"name":"区域查询","value":"AreaSearch","api":"/api/mall/freightc/getAllSelect/","method":0,"menu":48},
|
{"id":185,"name":"区域查询","value":"AreaSearch","api":"/api/mall/freightc/getAllSelect/","method":0,"menu_id":48},
|
||||||
{"id":67,"name":"删除","value":"Delete","api":"/api/system/user/{id}/","method":3,"menu":4},
|
{"id":67,"name":"删除","value":"Delete","api":"/api/system/user/{id}/","method":3,"menu_id":4},
|
||||||
{"id":75,"name":"删除","value":"Delete","api":"/api/crontab/periodictask/{id}/","method":3,"menu":24},
|
{"id":75,"name":"删除","value":"Delete","api":"/api/crontab/periodictask/{id}/","method":3,"menu_id":24},
|
||||||
{"id":81,"name":"删除","value":"Delete","api":"/api/terminal/terminal/{id}/","method":3,"menu":27},
|
{"id":81,"name":"删除","value":"Delete","api":"/api/terminal/terminal/{id}/","method":3,"menu_id":27},
|
||||||
{"id":87,"name":"删除","value":"Delete","api":"/api/address/area/{id}/","method":3,"menu":28},
|
{"id":87,"name":"删除","value":"Delete","api":"/api/address/area/{id}/","method":3,"menu_id":28},
|
||||||
{"id":92,"name":"删除","value":"Delete","api":"/api/mall/goodsspu/{id}/","method":3,"menu":30},
|
{"id":92,"name":"删除","value":"Delete","api":"/api/mall/goodsspu/{id}/","method":3,"menu_id":30},
|
||||||
{"id":97,"name":"删除","value":"Delete","api":"/api/mall/goodstype/{id}/","method":3,"menu":31},
|
{"id":97,"name":"删除","value":"Delete","api":"/api/mall/goodstype/{id}/","method":3,"menu_id":31},
|
||||||
{"id":99,"name":"删除","value":"Delete","api":"/api/mall/goodsorder/{id}/","method":3,"menu":32},
|
{"id":99,"name":"删除","value":"Delete","api":"/api/mall/goodsorder/{id}/","method":3,"menu_id":32},
|
||||||
{"id":104,"name":"删除","value":"Delete","api":"/api/mall/goodsforderinfo/{id}/","method":3,"menu":33},
|
{"id":104,"name":"删除","value":"Delete","api":"/api/mall/goodsforderinfo/{id}/","method":3,"menu_id":33},
|
||||||
{"id":107,"name":"删除","value":"Delete","api":"/api/platformsettings/userfeeckback/{id}/","method":3,"menu":10},
|
{"id":107,"name":"删除","value":"Delete","api":"/api/platformsettings/userfeeckback/{id}/","method":3,"menu_id":10},
|
||||||
{"id":116,"name":"删除","value":"Delete","api":"/api/platformsettings/sysconfig/{id}/","method":3,"menu":6},
|
{"id":116,"name":"删除","value":"Delete","api":"/api/platformsettings/sysconfig/{id}/","method":3,"menu_id":6},
|
||||||
{"id":123,"name":"删除","value":"Delete","api":"/api/autocode/autocode/{id}/","method":3,"menu":36},
|
{"id":123,"name":"删除","value":"Delete","api":"/api/autocode/autocode/{id}/","method":3,"menu_id":36},
|
||||||
{"id":127,"name":"删除","value":"Delete","api":"/api/system/dictionary/{id}/","method":3,"menu":37},
|
{"id":127,"name":"删除","value":"Delete","api":"/api/system/dictionary/{id}/","method":3,"menu_id":37},
|
||||||
{"id":131,"name":"删除","value":"Delete","api":"/api/system/appversion/{id}/","method":3,"menu":11},
|
{"id":131,"name":"删除","value":"Delete","api":"/api/system/appversion/{id}/","method":3,"menu_id":11},
|
||||||
{"id":138,"name":"删除","value":"Delete","api":"/api/autocode/StudentManage/{id}/","method":3,"menu":38},
|
{"id":138,"name":"删除","value":"Delete","api":"/api/autocode/StudentManage/{id}/","method":3,"menu_id":38},
|
||||||
{"id":145,"name":"删除","value":"Delete","api":"","method":3,"menu":39},
|
{"id":145,"name":"删除","value":"Delete","api":"","method":3,"menu_id":39},
|
||||||
{"id":148,"name":"删除","value":"Delete","api":"","method":3,"menu":40},
|
{"id":148,"name":"删除","value":"Delete","api":"","method":3,"menu_id":40},
|
||||||
{"id":160,"name":"删除","value":"Delete","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":3,"menu":43},
|
{"id":160,"name":"删除","value":"Delete","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":3,"menu_id":43},
|
||||||
{"id":171,"name":"删除","value":"Delete","api":"/api/lyformbuilder/teacherManage/{id}/","method":3,"menu":44},
|
{"id":171,"name":"删除","value":"Delete","api":"/api/lyformbuilder/teacherManage/{id}/","method":3,"menu_id":44},
|
||||||
{"id":182,"name":"删除","value":"Delete","api":"/api/mall/freightcfg/{id}/","method":3,"menu":48},
|
{"id":182,"name":"删除","value":"Delete","api":"/api/mall/freightcfg/{id}/","method":3,"menu_id":48},
|
||||||
{"id":187,"name":"删除","value":"Delete","api":"","method":3,"menu":50},
|
{"id":187,"name":"删除","value":"Delete","api":"","method":3,"menu_id":50},
|
||||||
{"id":179,"name":"修改密码","value":"Changepassword","api":"/api/system/user/change_password/{id}/","method":2,"menu":47},
|
{"id":179,"name":"修改密码","value":"Changepassword","api":"/api/system/user/change_password/{id}/","method":2,"menu_id":47},
|
||||||
{"id":60,"name":"保存","value":"Save","api":"/api/system/permission/{id}/","method":2,"menu":18},
|
{"id":60,"name":"保存","value":"Save","api":"/api/system/permission/{id}/","method":2,"menu_id":18},
|
||||||
{"id":119,"name":"保存","value":"Save","api":"/api/platformsettings/sysconfig/save_content/{id}/","method":2,"menu":6},
|
{"id":119,"name":"保存","value":"Save","api":"/api/platformsettings/sysconfig/save_content/{id}/","method":2,"menu_id":6},
|
||||||
{"id":113,"name":"任务列表","value":"Tasklist","api":"/api/crontab/periodictask/tasklist/","method":0,"menu":24},
|
{"id":113,"name":"任务列表","value":"Tasklist","api":"/api/crontab/periodictask/tasklist/","method":0,"menu_id":24},
|
||||||
{"id":135,"name":"代码预览","value":"PreCode","api":"/api/autocode/autocode/previewcode/","method":0,"menu":36},
|
{"id":135,"name":"代码预览","value":"PreCode","api":"/api/autocode/autocode/previewcode/","method":0,"menu_id":36},
|
||||||
{"id":155,"name":"代码预览","value":"PreCode","api":"/api/lyformbuilder/lyformbuilder/previewcodejson/","method":1,"menu":34}
|
{"id":155,"name":"代码预览","value":"PreCode","api":"/api/lyformbuilder/lyformbuilder/previewcodejson/","method":1,"menu_id":34},
|
||||||
|
{"id":238,"name":"查询","value":"Search","api":"/api/messages/messagenotice/ownmsg/","method":0,"menu_id":59},
|
||||||
|
{"id":241,"name":"单例","value":"Retrieve","api":"/api/messages/messagenotice/readownmsg/","method":1,"menu_id":59},
|
||||||
|
{"id":242,"name":"删除","value":"Delete","api":"/api/messages/messagenotice/delownmsg/","method":1,"menu_id":59},
|
||||||
|
{"id":249,"name":"管理","value":"Manage","api":"/api/system/fileManage/","method":1,"menu_id":60},
|
||||||
|
{"id":250,"name":"上传","value":"Upload","api":"/api/system/fileManage/upload/","method":1,"menu_id":60},
|
||||||
|
{"id":251,"name":"下载","value":"Download","api":"/api/system/fileManage/download/","method":1,"menu_id":60},
|
||||||
|
{"id":252,"name":"Token","value":"Token","api":"/api/system/fileManage/getToken/","method":1,"menu_id":60},
|
||||||
]
|
]
|
||||||
self.save(MenuButton, self.menu_button_data, "菜单权限表")
|
self.save(MenuButton, self.menu_button_data, "菜单权限表")
|
||||||
|
|
||||||
@ -359,7 +372,7 @@ if object.{key}:
|
|||||||
{"id": 2, "name": "普通用户", "key": "public", "sort": 2, "status": 1,
|
{"id": 2, "name": "普通用户", "key": "public", "sort": 2, "status": 1,
|
||||||
"admin": 0, "data_range": 4,
|
"admin": 0, "data_range": 4,
|
||||||
"dept": [1],
|
"dept": [1],
|
||||||
"menu": [21, 46, 47],
|
"menu": [21, 46, 47,59],
|
||||||
"permission": []
|
"permission": []
|
||||||
},
|
},
|
||||||
#自定义
|
#自定义
|
||||||
|
|||||||
@ -16,6 +16,7 @@ from mysystem.views.user import UserViewSet
|
|||||||
from mysystem.views.dictionary import DictionaryViewSet
|
from mysystem.views.dictionary import DictionaryViewSet
|
||||||
from mysystem.views.appversion import AppVersionViewSet
|
from mysystem.views.appversion import AppVersionViewSet
|
||||||
from mysystem.views.sysfiles import FileManageViewSet,FileGroupViewSet
|
from mysystem.views.sysfiles import FileManageViewSet,FileGroupViewSet
|
||||||
|
from mysystem.views.file_manage import RYFileManageView,RYFileDownloadView,RYFileTokenView,RYFileUploadView
|
||||||
|
|
||||||
system_url = routers.SimpleRouter()
|
system_url = routers.SimpleRouter()
|
||||||
system_url.register(r'menu', MenuViewSet)
|
system_url.register(r'menu', MenuViewSet)
|
||||||
@ -43,5 +44,9 @@ urlpatterns = [
|
|||||||
re_path('operation_log/deletealllogs/',OperationLogViewSet.as_view({'delete':'deletealllogs'})),
|
re_path('operation_log/deletealllogs/',OperationLogViewSet.as_view({'delete':'deletealllogs'})),
|
||||||
path('operation_log/systemlog/',OperationLogViewSet.as_view({'get':'system_logs'})),
|
path('operation_log/systemlog/',OperationLogViewSet.as_view({'get':'system_logs'})),
|
||||||
path('operation_log/getOwnerLogs/',OperationLogViewSet.as_view({'get':'getOwnerLogs'})),
|
path('operation_log/getOwnerLogs/',OperationLogViewSet.as_view({'get':'getOwnerLogs'})),
|
||||||
|
path('fileManage/', RYFileManageView.as_view(), name='文件操作'),
|
||||||
|
path('fileManage/download/', RYFileDownloadView.as_view(), name='文件下载'),
|
||||||
|
path('fileManage/getToken/', RYFileTokenView.as_view(), name='文件token'),
|
||||||
|
path('fileManage/upload/', RYFileUploadView.as_view(), name='文件上传'),
|
||||||
]
|
]
|
||||||
urlpatterns += system_url.urls
|
urlpatterns += system_url.urls
|
||||||
|
|||||||
495
backend/mysystem/views/file_manage.py
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
#!/bin/python
|
||||||
|
#coding: utf-8
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | system: 如意面板
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | Author: lybbn
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | QQ: 1042594286
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | Date: 2024-02-29
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | EditDate: 2024-02-29
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# 文件管理
|
||||||
|
# ------------------------------
|
||||||
|
import os,re
|
||||||
|
import time
|
||||||
|
from math import ceil
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from utils.jsonResponse import SuccessResponse,ErrorResponse,DetailResponse
|
||||||
|
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from utils.permission import CustomPermission
|
||||||
|
from utils.common import get_parameter_dic,WriteFile,ast_convert,RunCommand
|
||||||
|
from utils.security.files import list_files_in_directory,get_directory_size,delete_file,delete_dir,create_file,create_dir,rename_file,copy_file,copy_dir,move_file
|
||||||
|
from utils.security.files import get_filedir_attribute,batch_operate,get_filename_ext,auto_detect_file_language
|
||||||
|
from utils.security.no_delete_list import check_in_black_list
|
||||||
|
from utils.common import ly_md5 as md5
|
||||||
|
import platform
|
||||||
|
from django.http import FileResponse,StreamingHttpResponse
|
||||||
|
from django.utils.encoding import escape_uri_path
|
||||||
|
import mimetypes
|
||||||
|
from utils.streamingmedia_response import stream_video
|
||||||
|
from django.conf import settings
|
||||||
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
is_windows = True if platform.system() == 'Windows' else False
|
||||||
|
|
||||||
|
def getIndexPath():
|
||||||
|
if is_windows:
|
||||||
|
return "c:/"
|
||||||
|
else:
|
||||||
|
return "/tmp"
|
||||||
|
|
||||||
|
#返回nginx 404 错误页,降低信息泄露风险,提升安全性
|
||||||
|
def ResponseNginx404(state = 404):
|
||||||
|
html_content = '''<html>
|
||||||
|
<head><title>404 Not Found</title></head>
|
||||||
|
<body>
|
||||||
|
<center><h1>404 Not Found</h1></center>
|
||||||
|
<hr><center>nginx/1.21.1</center>
|
||||||
|
</body>
|
||||||
|
</html>'''
|
||||||
|
return HttpResponse(html_content, content_type='text/html',status=state,charset='utf-8')
|
||||||
|
|
||||||
|
def get_type_name(type):
|
||||||
|
c_type_name = ""
|
||||||
|
if type == "copy":
|
||||||
|
c_type_name = "复制"
|
||||||
|
elif type == "move":
|
||||||
|
c_type_name = "移动"
|
||||||
|
elif type == "zip":
|
||||||
|
c_type_name = "压缩"
|
||||||
|
elif type == "unzip":
|
||||||
|
c_type_name = "解压"
|
||||||
|
return c_type_name
|
||||||
|
|
||||||
|
class RYFileManageView(APIView):
|
||||||
|
"""
|
||||||
|
get:
|
||||||
|
文件管理
|
||||||
|
post:
|
||||||
|
文件管理
|
||||||
|
"""
|
||||||
|
permission_classes = [IsAuthenticated,CustomPermission]
|
||||||
|
authentication_classes = [JWTAuthentication]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
reqData = get_parameter_dic(request)
|
||||||
|
action = reqData.get("action","")
|
||||||
|
is_windows = True if platform.system() == 'Windows' else False
|
||||||
|
|
||||||
|
#路径处理
|
||||||
|
path = reqData.get("path",getIndexPath())
|
||||||
|
if path == "default":
|
||||||
|
path = getIndexPath()
|
||||||
|
if not path:#根目录
|
||||||
|
if is_windows:
|
||||||
|
path = ""
|
||||||
|
else:
|
||||||
|
path = "/"
|
||||||
|
if is_windows:#windows 路径处理
|
||||||
|
path = path.replace("\\", "/")
|
||||||
|
|
||||||
|
#接口逻辑处理
|
||||||
|
if action == "list_dir":
|
||||||
|
containSub = reqData.get("containSub",False)
|
||||||
|
isDir = reqData.get("isDir",False)#只显示目录
|
||||||
|
search = reqData.get("search","")
|
||||||
|
if search:
|
||||||
|
search = search.strip().lower()
|
||||||
|
order = reqData.get("order","")
|
||||||
|
sort = reqData.get("sort","name")
|
||||||
|
is_reverse = True if (order and order == "desc") else False
|
||||||
|
data_info = list_files_in_directory(dst_path=path,sort=sort,is_windows=is_windows,is_reverse=is_reverse,search=search,containSub=containSub,isDir=isDir)
|
||||||
|
data_dirs = data_info.get('data',[])
|
||||||
|
page = int(reqData.get("page",1))
|
||||||
|
limit = int(reqData.get("limit",100))
|
||||||
|
#一次最大条数限制
|
||||||
|
limit = 3000 if limit > 3000 else limit
|
||||||
|
total_nums = data_info['total_nums']
|
||||||
|
file_nums = data_info['file_nums']
|
||||||
|
dir_nums = data_info['dir_nums']
|
||||||
|
total_pages = ceil(total_nums / limit)
|
||||||
|
if page > total_pages:
|
||||||
|
page = total_pages
|
||||||
|
page = 1 if page<1 else page
|
||||||
|
# 根据分页参数对结果进行切片
|
||||||
|
start_idx = (page - 1) * limit
|
||||||
|
end_idx = start_idx + limit
|
||||||
|
paginated_data = data_dirs[start_idx:end_idx]
|
||||||
|
if not is_windows:
|
||||||
|
# directory, filename = os.path.split(path)
|
||||||
|
if path == "/":
|
||||||
|
directory_dict_list = []
|
||||||
|
else:
|
||||||
|
directory_list = path.split(os.path.sep)
|
||||||
|
directory_dict_list = [{'name': directory_list[i-1], 'url': os.sep.join(directory_list[:i])} for i in range(1,len(directory_list)+1)]
|
||||||
|
else:
|
||||||
|
# 去掉盘符部分
|
||||||
|
drive_name = path.split(':')[0]
|
||||||
|
if drive_name:
|
||||||
|
drive_name.lower()
|
||||||
|
try:
|
||||||
|
path_without_drive =path.split(':')[1] if drive_name else None
|
||||||
|
except:
|
||||||
|
return ErrorResponse(msg="路径错误:%s"%path)
|
||||||
|
# 按斜杠分割路径
|
||||||
|
if not path_without_drive or path_without_drive == "/":
|
||||||
|
directory_list = []
|
||||||
|
directory_dict_list = []
|
||||||
|
else:
|
||||||
|
directory_list = path_without_drive.strip('/').strip('\\').split('/')
|
||||||
|
# 获取每个名称的路径
|
||||||
|
path_list = ['/'.join(directory_list[:i+1]) for i in range(len(directory_list))]
|
||||||
|
directory_dict_list = [{'name': name, 'url': drive_name+":/"+path_list[i]} for i, name in enumerate(directory_list)]
|
||||||
|
if drive_name:
|
||||||
|
directory_dict_list.insert(0, {'name':drive_name+"盘",'url':drive_name+":/"})
|
||||||
|
else:
|
||||||
|
directory_dict_list.insert(0, {'name':drive_name,'url':""})
|
||||||
|
data = {
|
||||||
|
'data':paginated_data,
|
||||||
|
'path':path,
|
||||||
|
'paths':directory_dict_list,
|
||||||
|
'file_nums':file_nums,
|
||||||
|
'dir_nums':dir_nums,
|
||||||
|
'is_windows':is_windows
|
||||||
|
}
|
||||||
|
return SuccessResponse(data=data,total=total_nums,page=page,limit=limit)
|
||||||
|
elif action == "get_filedir_attribute":
|
||||||
|
data = get_filedir_attribute(path,is_windows=is_windows)
|
||||||
|
if not data:
|
||||||
|
return ErrorResponse(msg="目标不存在/或不支持查看")
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
elif action == "calc_size":
|
||||||
|
size = get_directory_size(path)
|
||||||
|
data = {"size":size}
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
elif action == "batch_operate":
|
||||||
|
reqData['path'] = path
|
||||||
|
c_type = reqData.get('type',None)
|
||||||
|
c_type_name = get_type_name(c_type)
|
||||||
|
isok,msg,code,data = batch_operate(param=reqData,is_windows=is_windows)
|
||||||
|
if not isok:
|
||||||
|
return ErrorResponse(code=code,msg=msg,data=data)
|
||||||
|
return DetailResponse(msg=msg,data=data)
|
||||||
|
elif action == "create_file":
|
||||||
|
filename = reqData.get("filename","")
|
||||||
|
if not filename:
|
||||||
|
return ErrorResponse(msg="请填写文件名")
|
||||||
|
path = path+"/"+filename
|
||||||
|
create_file(path=path,is_windows=is_windows)
|
||||||
|
return DetailResponse(msg="创建成功")
|
||||||
|
elif action == "create_dir":
|
||||||
|
dirname = reqData.get("dirname","")
|
||||||
|
if not dirname:
|
||||||
|
return ErrorResponse(msg="请填写目录名")
|
||||||
|
path = path+"/"+dirname
|
||||||
|
create_dir(path=path,is_windows=is_windows)
|
||||||
|
return DetailResponse(msg="创建成功")
|
||||||
|
elif action == "delete_file":
|
||||||
|
delete_file(path=path,is_windows=is_windows)
|
||||||
|
return DetailResponse(msg="删除成功")
|
||||||
|
elif action == "delete_dir":
|
||||||
|
delete_dir(path=path,is_windows=is_windows)
|
||||||
|
return DetailResponse(msg="删除成功")
|
||||||
|
elif action == "rename_file":
|
||||||
|
sname = reqData.get("sname","")
|
||||||
|
dname = reqData.get("dname","")
|
||||||
|
if not sname or not dname:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
sPath = path+"/"+sname
|
||||||
|
dPath = path+"/"+dname
|
||||||
|
rename_file(sPath=sPath,dPath=dPath,is_windows=is_windows)
|
||||||
|
return DetailResponse(msg="重命名成功")
|
||||||
|
elif action == "copy_file":
|
||||||
|
sPath = reqData.get("spath","")
|
||||||
|
name = reqData.get("name","")
|
||||||
|
cover = reqData.get("cover",False)
|
||||||
|
if not sPath or not name:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
dPath = path+"/"+name
|
||||||
|
if not cover:
|
||||||
|
if os.path.exists(dPath):
|
||||||
|
return ErrorResponse(code=4050,msg="目标存在相同文件")
|
||||||
|
copy_file(sPath=sPath,dPath=dPath,is_windows=is_windows)
|
||||||
|
return DetailResponse(msg="复制成功")
|
||||||
|
elif action == "copy_dir":
|
||||||
|
sPath = reqData.get("spath","")
|
||||||
|
name = reqData.get("name","")
|
||||||
|
cover = reqData.get("cover",False)
|
||||||
|
if not sPath or not name:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
dPath = path+"/"+name
|
||||||
|
if not cover:
|
||||||
|
if os.path.exists(dPath):
|
||||||
|
return ErrorResponse(code=4050,msg="目标存在相同目录")
|
||||||
|
copy_dir(sPath=sPath,dPath=dPath,is_windows=is_windows,cover=cover)
|
||||||
|
return DetailResponse(msg="复制成功")
|
||||||
|
elif action == "move_file":
|
||||||
|
spath = reqData.get("spath","")
|
||||||
|
name = reqData.get("name","")
|
||||||
|
cover = reqData.get("cover",False)
|
||||||
|
if not spath or not name:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
dPath = path+"/"+name
|
||||||
|
if not cover:
|
||||||
|
if os.path.exists(dPath):
|
||||||
|
return ErrorResponse(code=4050,msg="目标存在相同目录/文件")
|
||||||
|
move_file(sPath=spath,dPath=dPath,is_windows=is_windows,cover=cover)
|
||||||
|
return DetailResponse(msg="移动成功")
|
||||||
|
elif action == "read_file_body":
|
||||||
|
ext = get_filename_ext(path)
|
||||||
|
if ext in ['msi','psd','dll','sys','gz', 'zip', 'rar','7z', 'bz2', 'exe', 'db','sqlite','sqlite3','.mdb', 'pdf', 'doc', 'xls', 'docx', 'xlsx', 'ppt','pptx','mp4','flv','avi', 'png', 'gif', 'jpg', 'jpeg', 'bmp', 'icon', 'ico', 'pyc','class', 'so', 'pyd']:
|
||||||
|
return ErrorResponse(msg="该文件不支持在线编辑")
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return ErrorResponse(msg="该文件不存在")
|
||||||
|
size = os.path.getsize(path)
|
||||||
|
if size>3145728:#大于3M不建议在线编辑
|
||||||
|
return ErrorResponse(msg="文件过大,不支持在线编辑")
|
||||||
|
content = ""
|
||||||
|
encoding = "utf-8"
|
||||||
|
try:
|
||||||
|
with open(path, 'r', encoding="utf-8", errors='ignore') as file:
|
||||||
|
content = file.read()
|
||||||
|
except PermissionError as e:
|
||||||
|
return ErrorResponse(msg="文件被占用,暂无法打开")
|
||||||
|
except OSError as e:
|
||||||
|
return ErrorResponse(msg="操作系统错误,暂无法打开")
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
with open(path, 'r', encoding="GBK", errors='ignore') as file:
|
||||||
|
content = file.read()
|
||||||
|
encoding = "GBK"
|
||||||
|
except PermissionError as e:
|
||||||
|
return ErrorResponse(msg="文件被占用,暂无法打开")
|
||||||
|
except OSError as e:
|
||||||
|
return ErrorResponse(msg="操作系统错误,暂无法打开")
|
||||||
|
except Exception as e:
|
||||||
|
return ErrorResponse(msg="文件编码不兼容")
|
||||||
|
data = {
|
||||||
|
'st_mtime':str(int(os.stat(path).st_mtime)),
|
||||||
|
'content':content,
|
||||||
|
'size':size,
|
||||||
|
'encoding':encoding,
|
||||||
|
'language':auto_detect_file_language(path)
|
||||||
|
}
|
||||||
|
return DetailResponse(data=data,msg="获取成功")
|
||||||
|
elif action == "save_file_body":
|
||||||
|
content = reqData.get("content",None)
|
||||||
|
st_mtime = reqData.get("st_mtime",None)
|
||||||
|
force = reqData.get("force",False)
|
||||||
|
if not force and st_mtime and not st_mtime == str(int(os.stat(path).st_mtime)):
|
||||||
|
return ErrorResponse(code=4050,msg="在线文件可能发生变动,是否继续保存")
|
||||||
|
WriteFile(path,content)
|
||||||
|
return DetailResponse({'st_mtime':str(int(os.stat(path).st_mtime))},msg="保存成功")
|
||||||
|
elif action == "set_file_access":
|
||||||
|
user = reqData.get("user",None)
|
||||||
|
group = reqData.get("group",None)
|
||||||
|
access = reqData.get("access",False)
|
||||||
|
issub = reqData.get("issub",True)
|
||||||
|
if not user or not group:return ErrorResponse(msg="所属组/者不能为空")
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return ErrorResponse(msg="路径不存在")
|
||||||
|
numeric_pattern = re.compile(r'^[0-7]{3}$')
|
||||||
|
if not numeric_pattern.match(str(access)):return ErrorResponse(msg="权限格式错误")
|
||||||
|
if is_windows:
|
||||||
|
return ErrorResponse(msg="windows目前还不支持此功能")
|
||||||
|
if check_in_black_list(path=path,is_windows=is_windows):
|
||||||
|
return ErrorResponse(msg="该目录不能设置权限")
|
||||||
|
issub_str ="-R" if issub else ""
|
||||||
|
command = f"chmod {issub_str} {access} {path}"
|
||||||
|
res,err = RunCommand(command)
|
||||||
|
if err:
|
||||||
|
return ErrorResponse(msg=err)
|
||||||
|
command1 = f"chown {issub_str} {user}:{group} {path}"
|
||||||
|
res1,err1 =RunCommand(command1)
|
||||||
|
if err1:
|
||||||
|
return ErrorResponse(msg=err1)
|
||||||
|
return DetailResponse({'st_mtime':str(int(os.stat(path).st_mtime))},msg="设置成功")
|
||||||
|
data = {}
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
|
||||||
|
class RYGetFileDownloadView(APIView):
|
||||||
|
"""
|
||||||
|
get:
|
||||||
|
根据文件token进行文件下载
|
||||||
|
"""
|
||||||
|
permission_classes = []
|
||||||
|
authentication_classes = []
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
reqData = get_parameter_dic(request)
|
||||||
|
filename = reqData.get("filename",None)
|
||||||
|
token = reqData.get("token",None)
|
||||||
|
expires = reqData.get("expires",0)
|
||||||
|
isok,msg = validate_file_token(filename=filename,expires=expires,token=token)
|
||||||
|
if not isok:
|
||||||
|
return ResponseNginx404()
|
||||||
|
if not filename:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
return ErrorResponse(msg="文件不存在")
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
file_size = os.path.getsize(filename)
|
||||||
|
content_type, encoding = mimetypes.guess_type(filename)
|
||||||
|
content_type = content_type or 'application/octet-stream'
|
||||||
|
response = StreamingHttpResponse(open(filename, 'rb'), content_type=content_type)
|
||||||
|
response['Content-Disposition'] = f'attachment;filename="{escape_uri_path(os.path.basename(filename))}"'
|
||||||
|
response['Content-Length'] = file_size
|
||||||
|
return response
|
||||||
|
|
||||||
|
class RYFileDownloadView(APIView):
|
||||||
|
"""
|
||||||
|
post:
|
||||||
|
文件下载
|
||||||
|
"""
|
||||||
|
permission_classes = [IsAuthenticated,CustomPermission]
|
||||||
|
authentication_classes = [JWTAuthentication]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
reqData = get_parameter_dic(request)
|
||||||
|
filename = reqData.get("filename",None)
|
||||||
|
if not filename:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
return ErrorResponse(msg="文件不存在")
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
file_size = os.path.getsize(filename)
|
||||||
|
response = FileResponse(open(filename, 'rb'))
|
||||||
|
response['content_type'] = "application/octet-stream"
|
||||||
|
response['Content-Disposition'] = f'attachment;filename="{escape_uri_path(os.path.basename(filename))}"'
|
||||||
|
# response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
||||||
|
response['Content-Length'] = file_size # 设置文件大小
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def generate_file_token(filename,expire=None):
|
||||||
|
secret_key = settings.SECRET_KEY
|
||||||
|
if not expire:
|
||||||
|
expire = 43200
|
||||||
|
expire_time = int(time.time()) + expire # 设置过期时间
|
||||||
|
# 生成安全签名 token
|
||||||
|
signature = md5(f"{filename}-{expire_time}-{secret_key}")
|
||||||
|
return {
|
||||||
|
'token':signature,
|
||||||
|
'expires':expire_time,
|
||||||
|
'filename':filename
|
||||||
|
}
|
||||||
|
|
||||||
|
def validate_file_token(filename,expires,token):
|
||||||
|
expires = int(expires)
|
||||||
|
current_time = int(time.time())
|
||||||
|
secret_key = settings.SECRET_KEY
|
||||||
|
# 校验安全签名 token
|
||||||
|
expected_signature = md5(f"{filename}-{expires}-{secret_key}")
|
||||||
|
if token == expected_signature:
|
||||||
|
if current_time <= expires:
|
||||||
|
return True,"ok"
|
||||||
|
else:
|
||||||
|
return False,"token已过期"
|
||||||
|
else:
|
||||||
|
return False,"无效的token"
|
||||||
|
|
||||||
|
class RYFileTokenView(APIView):
|
||||||
|
"""
|
||||||
|
post:
|
||||||
|
获取文件访问token
|
||||||
|
"""
|
||||||
|
permission_classes = [IsAuthenticated,CustomPermission]
|
||||||
|
authentication_classes = [JWTAuthentication]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
reqData = get_parameter_dic(request)
|
||||||
|
filename = reqData.get("filename",None)
|
||||||
|
if not filename:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
return ErrorResponse(msg="文件不存在")
|
||||||
|
data = generate_file_token(filename=filename)
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
|
||||||
|
class RYFileMediaView(APIView):
|
||||||
|
"""
|
||||||
|
get:
|
||||||
|
媒体文件
|
||||||
|
"""
|
||||||
|
permission_classes = []
|
||||||
|
authentication_classes = []
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
reqData = get_parameter_dic(request)
|
||||||
|
filename = reqData.get("filename",None)
|
||||||
|
token = reqData.get("token",None)
|
||||||
|
expires = reqData.get("expires",0)
|
||||||
|
isok,msg = validate_file_token(filename=filename,expires=expires,token=token)
|
||||||
|
if not isok:
|
||||||
|
return ResponseNginx404()
|
||||||
|
if not filename:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
return ErrorResponse(msg="文件不存在")
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
content_type, encoding = mimetypes.guess_type(filename)
|
||||||
|
content_type = content_type or 'application/octet-stream'
|
||||||
|
if content_type in ['video/mp4','video/ogg', 'video/flv', 'video/avi', 'video/wmv', 'video/rmvb','audio/mp3','audio/x-m4a','audio/mpeg','audio/ogg']:
|
||||||
|
response = stream_video(request, filename)#支持视频流媒体播放
|
||||||
|
return response
|
||||||
|
return ErrorResponse(msg="限制只能媒体文件")
|
||||||
|
|
||||||
|
class RYFileUploadView(APIView):
|
||||||
|
"""
|
||||||
|
post:
|
||||||
|
文件上传(支持分片断点续传)
|
||||||
|
"""
|
||||||
|
permission_classes = [IsAuthenticated,CustomPermission]
|
||||||
|
authentication_classes = [JWTAuthentication]
|
||||||
|
throttle_classes=[]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
reqData = get_parameter_dic(request)
|
||||||
|
file = request.FILES.get('lyfile',None)
|
||||||
|
filechunk_name = reqData.get('lyfilechunk',None)
|
||||||
|
path = reqData.get("path",None)
|
||||||
|
if not path:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
if path[-1] == '/':
|
||||||
|
path = path[:-1]
|
||||||
|
if file:
|
||||||
|
save_path = path+"/"+file.name
|
||||||
|
if path and not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
with open(save_path, 'wb+') as destination:
|
||||||
|
for ck in file.chunks():
|
||||||
|
destination.write(ck)
|
||||||
|
return DetailResponse(data=None,msg="上传成功")
|
||||||
|
elif filechunk_name:
|
||||||
|
chunk = reqData.get('chunk')
|
||||||
|
chunkIndex = int(reqData.get('chunkIndex',0))
|
||||||
|
chunkCount = int(reqData.get('chunkCount',0))
|
||||||
|
if not chunkCount:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
if path and not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
# 保存文件分片
|
||||||
|
with open(f'{path}/{filechunk_name}.part{chunkIndex}', 'wb') as destination:
|
||||||
|
for content in chunk.chunks():
|
||||||
|
destination.write(content)
|
||||||
|
|
||||||
|
# 如果所有分片上传完毕,合并文件
|
||||||
|
if chunkIndex == chunkCount - 1:
|
||||||
|
with open(f'{path}/{filechunk_name}', 'ab') as final_destination:
|
||||||
|
for i in range(chunkCount):
|
||||||
|
with open(f'{path}/{filechunk_name}.part{i}', 'rb') as part_file:
|
||||||
|
final_destination.write(part_file.read())
|
||||||
|
os.remove(f'{path}/{filechunk_name}.part{i}') # 删除临时分片文件
|
||||||
|
return DetailResponse(data=None,msg="上传成功")
|
||||||
|
return DetailResponse(data=None,msg=f'分片{chunkIndex}上传成功')
|
||||||
|
else:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
@ -22,6 +22,7 @@ from django_redis import get_redis_connection
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from config import IS_SINGLE_TOKEN,LOGIN_ERROR_RETRY_TIMES,LOGIN_ERROR_RETRY_TIMEOUT
|
from config import IS_SINGLE_TOKEN,LOGIN_ERROR_RETRY_TIMES,LOGIN_ERROR_RETRY_TIMEOUT
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
from utils.common import current_os
|
||||||
|
|
||||||
class CaptchaView(APIView):
|
class CaptchaView(APIView):
|
||||||
"""
|
"""
|
||||||
@ -131,6 +132,7 @@ class LoginSerializer(TokenObtainPairSerializer):
|
|||||||
role_list = user.role.values_list('id',"name")
|
role_list = user.role.values_list('id',"name")
|
||||||
role_names_list = [i[1] for i in role_list]
|
role_names_list = [i[1] for i in role_list]
|
||||||
role_names = ','.join(role_names_list)
|
role_names = ','.join(role_names_list)
|
||||||
|
data['current_os'] = current_os
|
||||||
data['avatar'] = self.user.avatar
|
data['avatar'] = self.user.avatar
|
||||||
data['role_names'] = role_names
|
data['role_names'] = role_names
|
||||||
data['name'] = self.user.name
|
data['name'] = self.user.name
|
||||||
|
|||||||
BIN
backend/qqwry.dat
Normal file
@ -68,4 +68,7 @@ django-celery-results
|
|||||||
eventlet
|
eventlet
|
||||||
openpyxl
|
openpyxl
|
||||||
python-barcode
|
python-barcode
|
||||||
qqwry-py3
|
qqwry-py3
|
||||||
|
chardet
|
||||||
|
natsort
|
||||||
|
pywin32 ; sys_platform == 'win32'
|
||||||
@ -71,4 +71,7 @@ uvicorn
|
|||||||
gunicorn
|
gunicorn
|
||||||
gevent
|
gevent
|
||||||
uwsgi
|
uwsgi
|
||||||
qqwry-py3
|
qqwry-py3
|
||||||
|
chardet
|
||||||
|
natsort
|
||||||
|
pywin32 ; sys_platform == 'win32'
|
||||||
@ -93,3 +93,6 @@ xlwt==1.3.0
|
|||||||
zope.interface==5.4.0
|
zope.interface==5.4.0
|
||||||
openpyxl
|
openpyxl
|
||||||
python-barcode
|
python-barcode
|
||||||
|
chardet
|
||||||
|
natsort
|
||||||
|
pywin32 ; sys_platform == 'win32'
|
||||||
|
|||||||
@ -11,7 +11,8 @@
|
|||||||
# ------------------------------
|
# ------------------------------
|
||||||
# 公用方法
|
# 公用方法
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
|
import shutil
|
||||||
|
import platform
|
||||||
import os,re
|
import os,re
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
@ -23,14 +24,17 @@ import ast
|
|||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import subprocess
|
||||||
from application.settings import SECRET_KEY
|
from application.settings import SECRET_KEY
|
||||||
|
import chardet
|
||||||
|
|
||||||
|
current_os = platform.system().lower()
|
||||||
|
|
||||||
#手机号验证正则
|
#手机号验证正则
|
||||||
REGEX_MOBILE = "^1[356789]\d{9}$|^147\d{8}$|^176\d{8}$"
|
REGEX_MOBILE = r"^1[356789]\d{9}$|^147\d{8}$|^176\d{8}$"
|
||||||
|
|
||||||
#身份证正则
|
#身份证正则
|
||||||
IDCARD_MOBILE ="^[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$"
|
IDCARD_MOBILE =r"^[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$"
|
||||||
|
|
||||||
def starts_with_digit(s):
|
def starts_with_digit(s):
|
||||||
"""
|
"""
|
||||||
@ -415,4 +419,128 @@ def verifyLyStateEncript(data,expire=300):
|
|||||||
return data,False
|
return data,False
|
||||||
return jsonData,True
|
return jsonData,True
|
||||||
except:
|
except:
|
||||||
return data,False
|
return data,False
|
||||||
|
|
||||||
|
#读取文件内容
|
||||||
|
def ReadFile(filename,mode='r'):
|
||||||
|
"""
|
||||||
|
filename:文件(包含路径)
|
||||||
|
返回:若文件不存在则返回None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(filename, mode) as file:
|
||||||
|
content = file.read()
|
||||||
|
# 处理文件内容
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
with open(filename, mode, encoding="utf-8", errors='ignore') as file:
|
||||||
|
content = file.read()
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
with open(filename, mode, encoding="GBK", errors='ignore') as file:
|
||||||
|
content = file.read()
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
return content
|
||||||
|
|
||||||
|
#写入文件内容,不存在则创建
|
||||||
|
def WriteFile(file_path,content,mode="w",write=True,encoding="utf-8"):
|
||||||
|
"""
|
||||||
|
@name 写入文件内容,不存在则创建
|
||||||
|
@author lybbn<2024-01-13>
|
||||||
|
file_path:文件(包含路径)
|
||||||
|
content:写入的内容
|
||||||
|
mode: w 覆盖写入(默认)、a 追加写入
|
||||||
|
write:是否写入
|
||||||
|
"""
|
||||||
|
if write:
|
||||||
|
# 获取文件所在的目录路径
|
||||||
|
directory = os.path.dirname(file_path)
|
||||||
|
|
||||||
|
# 检查目录是否存在,如果不存在则创建
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory)
|
||||||
|
if 'b' in mode:encoding = None
|
||||||
|
# 写入内容到文件
|
||||||
|
with open(file_path, mode,encoding=encoding) as f:
|
||||||
|
if isinstance(content, int):
|
||||||
|
content = str(content)
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
def DeleteFile(path,empty_tips=True):
|
||||||
|
"""
|
||||||
|
@name 删除文件
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
if empty_tips:
|
||||||
|
if not os.path.exists(path) and not os.path.islink(path):
|
||||||
|
raise ValueError("要删除的文件不存在")
|
||||||
|
os.remove(path)
|
||||||
|
if os.path.exists(path):
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
|
def DeleteDir(path):
|
||||||
|
"""
|
||||||
|
@name 删除目录
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return
|
||||||
|
if os.path.islink(path):
|
||||||
|
os.remove(path)
|
||||||
|
else:
|
||||||
|
shutil.rmtree(path)
|
||||||
|
|
||||||
|
def RunCommand(cmdstr,cwd=None,shell=True,bufsize=4096,returncode=False,timeout=None,env=None):
|
||||||
|
"""
|
||||||
|
@name 执行命令(输出结果)
|
||||||
|
@author lybbn<2024-02-18>
|
||||||
|
@param cmdstr 命令 [必传]
|
||||||
|
@param cwd 执行命令的工作目录
|
||||||
|
@param returncode 是否需要返回returncode 0 成功、 1 失败
|
||||||
|
@timeout 命令超时时间 单位s秒
|
||||||
|
"""
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
commands_list = cmdstr.split("\n")
|
||||||
|
commands = '&'.join(commands_list)
|
||||||
|
else:
|
||||||
|
commands_list = cmdstr.split("\n")
|
||||||
|
commands = '; '.join(commands_list)
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(commands,cwd=cwd,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=shell,bufsize=bufsize,env=env)
|
||||||
|
|
||||||
|
# 获取输出结果
|
||||||
|
stdout, stderr = process.communicate(timeout=timeout)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
process.kill() # 如果超时,则杀死子进程
|
||||||
|
stdout, stderr = process.communicate()
|
||||||
|
|
||||||
|
# 检测输出编码
|
||||||
|
stdout_encoding = chardet.detect(stdout)['encoding'] or 'utf-8'
|
||||||
|
stderr_encoding = chardet.detect(stderr)['encoding'] or 'utf-8'
|
||||||
|
try:
|
||||||
|
out = stdout.decode(stdout_encoding)
|
||||||
|
err = stderr.decode(stderr_encoding)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
out = stdout.decode('utf-8')
|
||||||
|
err = stderr.decode('utf-8')
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
out = stdout.decode('gb2312')
|
||||||
|
err = stderr.decode('gb2312')
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
out = stdout.decode('utf-16')
|
||||||
|
err = stderr.decode('utf-16')
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
out = stdout.decode('latin-1')
|
||||||
|
err = stderr.decode('latin-1')
|
||||||
|
except:
|
||||||
|
out = stdout.decode('gb2312', 'ignore')
|
||||||
|
err = stderr.decode('gb2312', 'ignore')
|
||||||
|
|
||||||
|
if returncode:
|
||||||
|
return out, err,process.returncode
|
||||||
|
return out, err
|
||||||
@ -31,6 +31,7 @@ from rest_framework_simplejwt.authentication import AUTH_HEADER_TYPE_BYTES
|
|||||||
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
|
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
|
||||||
from rest_framework_simplejwt.tokens import UntypedToken
|
from rest_framework_simplejwt.tokens import UntypedToken
|
||||||
from utils.ip_util import IPQQwry
|
from utils.ip_util import IPQQwry
|
||||||
|
from config import IS_DEMO
|
||||||
|
|
||||||
IS_ALLOW_FRONTEND = ALLOW_FRONTEND
|
IS_ALLOW_FRONTEND = ALLOW_FRONTEND
|
||||||
|
|
||||||
@ -248,6 +249,8 @@ def has_permission(user, path):
|
|||||||
method = methodList.index(method)
|
method = methodList.index(method)
|
||||||
if not hasattr(user, "role"):
|
if not hasattr(user, "role"):
|
||||||
return False
|
return False
|
||||||
|
if method == 5 and api == "/ws/msg/":
|
||||||
|
return True
|
||||||
userApiList = user.role.values('permission__api', 'permission__method') # 获取当前用户的角色拥有的所有接口
|
userApiList = user.role.values('permission__api', 'permission__method') # 获取当前用户的角色拥有的所有接口
|
||||||
for item in userApiList:
|
for item in userApiList:
|
||||||
valid = ValidationApi(api, item.get('permission__api'))
|
valid = ValidationApi(api, item.get('permission__api'))
|
||||||
|
|||||||
@ -35,9 +35,15 @@ class CustomPermission(BasePermission):
|
|||||||
|
|
||||||
# 演示模式接口白名单(演示模式不做控制)
|
# 演示模式接口白名单(演示模式不做控制)
|
||||||
# 演示模式判断
|
# 演示模式判断
|
||||||
|
api = request.path
|
||||||
|
method = request.method # 当前请求方法
|
||||||
demo_api_white_list = ['/api/lyformbuilder/lyformbuilder/previewcodejson/','/api/mall/goodsspu/export/']
|
demo_api_white_list = ['/api/lyformbuilder/lyformbuilder/previewcodejson/','/api/mall/goodsspu/export/']
|
||||||
if IS_DEMO and not request.path in demo_api_white_list:
|
if IS_DEMO and not api in demo_api_white_list:
|
||||||
if not request.method in ['GET', 'OPTIONS']:
|
if not method in ['GET', 'OPTIONS']:
|
||||||
|
if method == "POST" and api in ["/api/system/fileManage/"]:
|
||||||
|
action = request.data.get("action","")
|
||||||
|
if action in ["list_dir"]:
|
||||||
|
return True
|
||||||
raise ValueError('演示模式,不允许操作!')
|
raise ValueError('演示模式,不允许操作!')
|
||||||
# 对ViewSet下的def方法进行权限判断
|
# 对ViewSet下的def方法进行权限判断
|
||||||
# 当权限为空时,则可以访问
|
# 当权限为空时,则可以访问
|
||||||
@ -53,8 +59,8 @@ class CustomPermission(BasePermission):
|
|||||||
if request.user.is_superuser:
|
if request.user.is_superuser:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
api = request.path # 当前请求接口
|
# api = request.path # 当前请求接口
|
||||||
method = request.method # 当前请求方法
|
# method = request.method # 当前请求方法
|
||||||
methodList = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH']
|
methodList = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH']
|
||||||
method = methodList.index(method)
|
method = methodList.index(method)
|
||||||
if not hasattr(request.user, "role"):
|
if not hasattr(request.user, "role"):
|
||||||
|
|||||||
@ -11,7 +11,6 @@ from user_agents import parse
|
|||||||
|
|
||||||
from mysystem.models import LoginLog
|
from mysystem.models import LoginLog
|
||||||
|
|
||||||
|
|
||||||
def get_request_user(request):
|
def get_request_user(request):
|
||||||
"""
|
"""
|
||||||
获取请求user
|
获取请求user
|
||||||
|
|||||||
0
backend/utils/security/__init__.py
Normal file
816
backend/utils/security/files.py
Normal file
@ -0,0 +1,816 @@
|
|||||||
|
#!/bin/python
|
||||||
|
#coding: utf-8
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | system: 如意面板
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | Author: lybbn
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | QQ: 1042594286
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | Date: 2024-01-03
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# 文件/目录操作
|
||||||
|
# ------------------------------
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
import tarfile
|
||||||
|
import datetime
|
||||||
|
import mimetypes
|
||||||
|
import requests
|
||||||
|
from natsort import natsorted, ns
|
||||||
|
from utils.server.system import system
|
||||||
|
from utils.security.no_delete_list import check_no_delete,check_in_black_list
|
||||||
|
from utils.common import ast_convert,WriteFile,RunCommand,current_os
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
def get_file_name_from_url(url):
|
||||||
|
"""
|
||||||
|
@name 使用 os.path.basename() 函数获取 URL 中的文件名
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
file_name = os.path.basename(url)
|
||||||
|
return file_name
|
||||||
|
|
||||||
|
def get_file_extension(file_path):
|
||||||
|
"""
|
||||||
|
@name 获取文件后缀扩展
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
_, extension = os.path.splitext(file_path)
|
||||||
|
return extension
|
||||||
|
|
||||||
|
def detect_file_type(file_path):
|
||||||
|
"""
|
||||||
|
@name 检测文件类型
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
file_type, _ = mimetypes.guess_type(file_path)
|
||||||
|
return file_type
|
||||||
|
|
||||||
|
def auto_detect_file_language(file_path):
|
||||||
|
"""
|
||||||
|
@name 智能检测文件所属语言
|
||||||
|
@author lybbn<2024-03-08>
|
||||||
|
"""
|
||||||
|
ext = get_file_extension(file_path)
|
||||||
|
if ext in ['.readme','.md']:
|
||||||
|
return "markdown"
|
||||||
|
elif ext in ['.sh']:
|
||||||
|
return "shell"
|
||||||
|
elif ext in ['.lua']:
|
||||||
|
return "lua"
|
||||||
|
elif ext in ['.rb']:
|
||||||
|
return "ruby"
|
||||||
|
elif ext in ['.js','.ts']:
|
||||||
|
return "javascript"
|
||||||
|
elif ext in ['.html','htm']:
|
||||||
|
return "html"
|
||||||
|
elif ext in ['.css','.scss','.sass','.less']:
|
||||||
|
return "css"
|
||||||
|
elif ext in ['.json']:
|
||||||
|
return "json"
|
||||||
|
elif ext in ['.py']:
|
||||||
|
return "python"
|
||||||
|
elif ext in ['.yaml','.yml']:
|
||||||
|
return "yaml"
|
||||||
|
elif ext in ['.conf','.ini']:
|
||||||
|
if 'nginx' in file_path:
|
||||||
|
return "nginx"
|
||||||
|
return "properties"
|
||||||
|
elif ext in ['.vue']:
|
||||||
|
return "vue"
|
||||||
|
elif ext in ['.php']:
|
||||||
|
return "php"
|
||||||
|
elif ext in ['.java']:
|
||||||
|
return "java"
|
||||||
|
elif ext in ['.go']:
|
||||||
|
return "go"
|
||||||
|
elif ext in ['.sql']:
|
||||||
|
return "sql"
|
||||||
|
elif ext in ['.xml']:
|
||||||
|
return "xml"
|
||||||
|
else:
|
||||||
|
return "log"
|
||||||
|
def list_dirs(dst_path):
|
||||||
|
"""
|
||||||
|
列出指定目录下文件\目录名
|
||||||
|
返回指定目录下文件+目录名的列表
|
||||||
|
"""
|
||||||
|
if not os.path.exists(dst_path):
|
||||||
|
return []
|
||||||
|
data = []
|
||||||
|
for f in os.listdir(dst_path):
|
||||||
|
data.append(f)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_size(file_path):
|
||||||
|
"""
|
||||||
|
@name 获取文件大小
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
return os.path.getsize(file_path)
|
||||||
|
|
||||||
|
def is_link(file_path):
|
||||||
|
"""
|
||||||
|
@name 是否软链接
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
return os.path.islink(file_path)
|
||||||
|
|
||||||
|
def get_directory_size(dst_path):
|
||||||
|
"""
|
||||||
|
@name 计算指定目录大小
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
if current_os == "windows":
|
||||||
|
total_size = 0
|
||||||
|
for path, dirs, files in os.walk(dst_path):
|
||||||
|
for file in files:
|
||||||
|
if not os.path.exists(file): continue
|
||||||
|
if os.path.islink(file): continue
|
||||||
|
file_path = os.path.join(path, file)
|
||||||
|
total_size += os.path.getsize(file_path)
|
||||||
|
return total_size
|
||||||
|
else:
|
||||||
|
result,err,returncode = RunCommand(f"du -sh {dst_path}",returncode=True)
|
||||||
|
if returncode == 0:
|
||||||
|
size = result.split()[0]
|
||||||
|
return re.sub(r'(\d+)([a-zA-Z])', r'\1 \2', size)+"B"
|
||||||
|
else:
|
||||||
|
return "0 B"
|
||||||
|
|
||||||
|
def get_path_files_nums(path):
|
||||||
|
"""
|
||||||
|
@name 获取指定目录文件数量
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
if os.path.isfile(path):
|
||||||
|
return 1
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return 0
|
||||||
|
i = 0
|
||||||
|
for name in os.listdir(path):
|
||||||
|
i += 1
|
||||||
|
return i
|
||||||
|
|
||||||
|
def get_filename_ext(filename):
|
||||||
|
"""
|
||||||
|
@name 获取文件扩展名
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
tmpList = filename.split('.')
|
||||||
|
return tmpList[-1]
|
||||||
|
|
||||||
|
def windows_path_replace(path,is_windows = True):
|
||||||
|
"""
|
||||||
|
@name 把path中的\\ sep替换成 /
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
if is_windows:
|
||||||
|
path = path.replace("\\", "/")
|
||||||
|
return path
|
||||||
|
|
||||||
|
def list_files_in_directory(dst_path,sort="name",is_reverse=False,is_windows=False,search=None,containSub=False,isDir=False):
|
||||||
|
"""
|
||||||
|
@name 列出指定目录下文件\目录名列表,包含文件\目录属性(大小、路径、权限、所属者)
|
||||||
|
目录size大小默认不计算为0
|
||||||
|
owner所属者为空可能为文件/目录无权限查看或被占用等
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
@param sort 排序
|
||||||
|
@param is_reverse True 降序(desc) 、False 升序(asc)
|
||||||
|
@param search 搜索名称
|
||||||
|
@param containSub 搜索内容是否包含所有子目录
|
||||||
|
@param isDir 是否只列出目录
|
||||||
|
"""
|
||||||
|
def get_file_info(entry):
|
||||||
|
"""获取文件信息的内部函数"""
|
||||||
|
file_info = entry.stat()
|
||||||
|
modified_time = datetime.datetime.fromtimestamp(file_info.st_mtime)
|
||||||
|
formatted_time = modified_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
gid = file_info.st_gid
|
||||||
|
group_name = "" if is_windows else system.GetGroupidName(entry.path, gid)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"name": entry.name,
|
||||||
|
"type": "file" if entry.is_file() else "dir",
|
||||||
|
"path": windows_path_replace(entry.path, is_windows=is_windows),
|
||||||
|
"size": file_info.st_size if entry.is_file() else None,
|
||||||
|
"permissions": oct(file_info.st_mode)[-3:],
|
||||||
|
"owner_uid": file_info.st_uid,
|
||||||
|
"owner": system.GetUidName(entry.path, file_info.st_uid),
|
||||||
|
"gid": gid,
|
||||||
|
"group": group_name,
|
||||||
|
"modified": formatted_time
|
||||||
|
}
|
||||||
|
|
||||||
|
def process_entry(entry):
|
||||||
|
"""处理单个目录项"""
|
||||||
|
if search and search.lower() not in entry.name.lower():
|
||||||
|
return None
|
||||||
|
if isDir and entry.is_file():
|
||||||
|
return None
|
||||||
|
return get_file_info(entry)
|
||||||
|
|
||||||
|
# 处理Windows磁盘根目录情况
|
||||||
|
if is_windows and not dst_path:
|
||||||
|
disk_paths = system().GetDiskInfo()
|
||||||
|
datainfo = {
|
||||||
|
'data':[],
|
||||||
|
'file_nums':0,
|
||||||
|
'dir_nums':0,
|
||||||
|
'total_nums':0
|
||||||
|
}
|
||||||
|
for d in disk_paths:
|
||||||
|
if search and d['path'].lower().find(search) == -1:
|
||||||
|
continue
|
||||||
|
datainfo['data'].append({
|
||||||
|
"name": d['path'].lower(),
|
||||||
|
"type": "pan",
|
||||||
|
"path": windows_path_replace(d['path'].lower(), is_windows=is_windows),
|
||||||
|
"size": d['size'][0],
|
||||||
|
"permissions": "",
|
||||||
|
"owner_uid": None,
|
||||||
|
"owner": "",
|
||||||
|
"modified": ""
|
||||||
|
})
|
||||||
|
datainfo['total_nums'] = len(disk_paths)
|
||||||
|
return datainfo
|
||||||
|
|
||||||
|
# 检查路径有效性
|
||||||
|
if not os.path.exists(dst_path):
|
||||||
|
return {
|
||||||
|
'data': [],
|
||||||
|
'file_nums': 0,
|
||||||
|
'dir_nums': 0,
|
||||||
|
'total_nums': 0
|
||||||
|
}
|
||||||
|
if not os.path.isdir(dst_path):
|
||||||
|
raise ValueError("错误:非目录")
|
||||||
|
|
||||||
|
# 处理不包含子目录的情况
|
||||||
|
if not containSub:
|
||||||
|
dirData = []
|
||||||
|
fileData = []
|
||||||
|
for entry in os.scandir(dst_path):
|
||||||
|
item = process_entry(entry)
|
||||||
|
if item:
|
||||||
|
if item["type"] == "dir":
|
||||||
|
dirData.append(item)
|
||||||
|
else:
|
||||||
|
fileData.append(item)
|
||||||
|
|
||||||
|
# 对目录和文件分别排序
|
||||||
|
if sort == "name":
|
||||||
|
dirData = natsorted(dirData, key=lambda x: x["name"], alg=ns.PATH, reverse=is_reverse)
|
||||||
|
fileData = natsorted(fileData, key=lambda x: x["name"], alg=ns.PATH, reverse=is_reverse)
|
||||||
|
elif sort == "modified":
|
||||||
|
dirData = sorted(dirData, key=lambda x: x["modified"], reverse=is_reverse)
|
||||||
|
fileData = sorted(fileData, key=lambda x: x["modified"], reverse=is_reverse)
|
||||||
|
elif sort == "size":
|
||||||
|
dirData = sorted(dirData, key=lambda x: x["size"] if x["size"] is not None else 0, reverse=is_reverse)
|
||||||
|
fileData = sorted(fileData, key=lambda x: x["size"] if x["size"] is not None else 0, reverse=is_reverse)
|
||||||
|
|
||||||
|
data = dirData + fileData
|
||||||
|
else:
|
||||||
|
# 处理包含子目录的情况
|
||||||
|
data = []
|
||||||
|
count_limit = 0
|
||||||
|
max_limit = 3000
|
||||||
|
for root, dirs, files in os.walk(dst_path):
|
||||||
|
if count_limit >= max_limit:
|
||||||
|
break
|
||||||
|
for entry in chain((os.path.join(root, f) for f in files),
|
||||||
|
(os.path.join(root, d) for d in dirs)):
|
||||||
|
if count_limit >= max_limit:
|
||||||
|
break
|
||||||
|
info = process_entry(os.DirEntry(entry))
|
||||||
|
if info:
|
||||||
|
data.append(info)
|
||||||
|
count_limit += 1
|
||||||
|
|
||||||
|
# 对结果进行排序
|
||||||
|
if sort == "name":
|
||||||
|
data = natsorted(data, key=lambda x: x["name"], alg=ns.PATH, reverse=is_reverse)
|
||||||
|
elif sort == "modified":
|
||||||
|
data = sorted(data, key=lambda x: x["modified"], reverse=is_reverse)
|
||||||
|
elif sort == "size":
|
||||||
|
data = sorted(data, key=lambda x: x["size"] if x["size"] is not None else 0, reverse=is_reverse)
|
||||||
|
|
||||||
|
# 统计文件数量
|
||||||
|
file_nums = sum(1 for item in data if item["type"] == "file")
|
||||||
|
dir_nums = sum(1 for item in data if item["type"] == "dir")
|
||||||
|
|
||||||
|
return {
|
||||||
|
'data': data,
|
||||||
|
'file_nums': file_nums,
|
||||||
|
'dir_nums': dir_nums,
|
||||||
|
'total_nums': file_nums + dir_nums
|
||||||
|
}
|
||||||
|
def list_files_in_directory_old(dst_path,sort="name",is_reverse=False,is_windows=False,search=None,containSub=False,isDir=False):
|
||||||
|
"""
|
||||||
|
@name 列出指定目录下文件\目录名列表,包含文件\目录属性(大小、路径、权限、所属者)
|
||||||
|
目录size大小默认不计算为0
|
||||||
|
owner所属者为空可能为文件/目录无权限查看或被占用等
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
@param sort 排序
|
||||||
|
@param is_reverse True 降序(desc) 、False 升序(asc)
|
||||||
|
@param search 搜索名称
|
||||||
|
@param containSub 搜索内容是否包含所有子目录
|
||||||
|
@param isDir 是否只列出目录
|
||||||
|
"""
|
||||||
|
is_dir_first = True#是否把目录放在前面
|
||||||
|
if is_windows:
|
||||||
|
if not dst_path:
|
||||||
|
disk_paths = system().GetDiskInfo()
|
||||||
|
datainfo = {
|
||||||
|
'data':[],
|
||||||
|
'file_nums':0,
|
||||||
|
'dir_nums':0,
|
||||||
|
'total_nums':0
|
||||||
|
}
|
||||||
|
for d in disk_paths:
|
||||||
|
if search:
|
||||||
|
if d['path'].lower().find(search) == -1:
|
||||||
|
continue
|
||||||
|
datainfo['data'].append({"name":d['path'].lower(),"type":"pan","path":windows_path_replace(d['path'].lower(),is_windows=is_windows),"size":d['size'][0],"permissions":"","owner_uid":None,"owner":"","modified":""})
|
||||||
|
datainfo['total_nums'] = len(disk_paths)
|
||||||
|
return datainfo
|
||||||
|
if not os.path.exists(dst_path):
|
||||||
|
return {
|
||||||
|
'data':[],
|
||||||
|
'file_nums':0,
|
||||||
|
'dir_nums':0,
|
||||||
|
'total_nums':0
|
||||||
|
}
|
||||||
|
if not os.path.isdir(dst_path):
|
||||||
|
raise ValueError("错误:非目录")
|
||||||
|
data = []
|
||||||
|
dirData = []
|
||||||
|
fileData = []
|
||||||
|
file_nums = 0
|
||||||
|
dir_nums = 0
|
||||||
|
if not containSub:
|
||||||
|
for entry in os.scandir(dst_path):
|
||||||
|
if entry.is_file():
|
||||||
|
if isDir:
|
||||||
|
continue
|
||||||
|
if search:
|
||||||
|
if entry.name.lower().find(search) == -1:
|
||||||
|
continue
|
||||||
|
file_nums = file_nums + 1
|
||||||
|
file_info = entry.stat()
|
||||||
|
modified_time = datetime.datetime.fromtimestamp(file_info.st_mtime)
|
||||||
|
formatted_time = modified_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
gid=file_info.st_gid
|
||||||
|
group_name=""
|
||||||
|
if not is_windows:
|
||||||
|
group_name = system.GetGroupidName(entry.path,gid)
|
||||||
|
tempData = {"name":entry.name,"type":"file","path":windows_path_replace(entry.path,is_windows=is_windows),"size":file_info.st_size,"permissions":oct(file_info.st_mode)[-3:],"owner_uid":file_info.st_uid,"owner":system.GetUidName(entry.path,file_info.st_uid),"gid":gid,"group":group_name,"modified":formatted_time}
|
||||||
|
data.append(tempData)
|
||||||
|
if is_dir_first:
|
||||||
|
fileData.append(tempData)
|
||||||
|
elif entry.is_dir():
|
||||||
|
if search:
|
||||||
|
if entry.name.lower().find(search) == -1:
|
||||||
|
continue
|
||||||
|
dir_nums = dir_nums + 1
|
||||||
|
dir_info = entry.stat()
|
||||||
|
modified_time = datetime.datetime.fromtimestamp(dir_info.st_mtime)
|
||||||
|
formatted_time = modified_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
gid=dir_info.st_gid
|
||||||
|
group_name=""
|
||||||
|
if not is_windows:
|
||||||
|
group_name = system.GetGroupidName(entry.path,gid)
|
||||||
|
tempData = {"name":entry.name,"type":"dir","path":windows_path_replace(entry.path,is_windows=is_windows),"size":None,"permissions":oct(dir_info.st_mode)[-3:],"owner_uid":dir_info.st_uid,"owner":system.GetUidName(entry.path,dir_info.st_uid),"gid":gid,"group":group_name,"modified":formatted_time}
|
||||||
|
data.append(tempData)
|
||||||
|
if is_dir_first:
|
||||||
|
dirData.append(tempData)
|
||||||
|
else:
|
||||||
|
count_limit = 0
|
||||||
|
max_limit = 3000
|
||||||
|
for root, dirs, files in os.walk(dst_path):
|
||||||
|
if count_limit >= max_limit:
|
||||||
|
break
|
||||||
|
# 在当前目录下搜索文件
|
||||||
|
for file in files:
|
||||||
|
if count_limit >= max_limit:
|
||||||
|
break
|
||||||
|
if search:
|
||||||
|
if file.lower().find(search) == -1:
|
||||||
|
continue
|
||||||
|
file_nums = file_nums + 1
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
file_info = os.stat(file_path)
|
||||||
|
modified_time = datetime.datetime.fromtimestamp(file_info.st_mtime)
|
||||||
|
formatted_time = modified_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
tempData = {"name":file,"type":"file","path":windows_path_replace(file_path,is_windows=is_windows),"size":file_info.st_size,"permissions":oct(file_info.st_mode)[-3:],"owner_uid":file_info.st_uid,"owner":system.GetUidName(file_path,file_info.st_uid),"modified":formatted_time}
|
||||||
|
data.append(tempData)
|
||||||
|
if is_dir_first:
|
||||||
|
fileData.append(tempData)
|
||||||
|
count_limit += 1
|
||||||
|
# 在当前目录下搜索目录
|
||||||
|
for dir in dirs:
|
||||||
|
if count_limit >= max_limit:
|
||||||
|
break
|
||||||
|
if search:
|
||||||
|
if dir.lower().find(search) == -1:
|
||||||
|
continue
|
||||||
|
dir_nums = dir_nums + 1
|
||||||
|
dir_path = os.path.join(root, dir)
|
||||||
|
dir_info = os.stat(dir_path)
|
||||||
|
modified_time = datetime.datetime.fromtimestamp(dir_info.st_mtime)
|
||||||
|
formatted_time = modified_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
tempData = {"name":dir,"type":"dir","path":windows_path_replace(dir_path,is_windows=is_windows),"size":None,"permissions":oct(dir_info.st_mode)[-3:],"owner_uid":dir_info.st_uid,"owner":system.GetUidName(dir_path,dir_info.st_uid),"modified":formatted_time}
|
||||||
|
data.append(tempData)
|
||||||
|
if is_dir_first:
|
||||||
|
dirData.append(tempData)
|
||||||
|
count_limit += 1
|
||||||
|
if is_dir_first:
|
||||||
|
data = []
|
||||||
|
temp_dir_date1 = dirData[:4000]
|
||||||
|
temp_dir_date2 = natsorted(temp_dir_date1,key=lambda x: x.get("name", ""), alg=ns.PATH, reverse=is_reverse)
|
||||||
|
temp_dir_data = temp_dir_date2 + dirData[4000:]
|
||||||
|
temp_file_date1 = fileData[:4000]
|
||||||
|
temp_file_date2 = natsorted(temp_file_date1,key=lambda x: x.get("name", ""), alg=ns.PATH, reverse=is_reverse)
|
||||||
|
temp_file_data = temp_file_date2 + fileData[4000:]
|
||||||
|
data.extend(temp_dir_data)
|
||||||
|
data.extend(temp_file_data)
|
||||||
|
|
||||||
|
# if sort == "name":
|
||||||
|
# # 根据 sort 参数对结果进行排序,海量文件可能导致排序缓慢,因此限制排序前4000个
|
||||||
|
# temp_date1 = data[:4000]
|
||||||
|
# temp_date2 = natsorted(temp_date1,key=lambda x: x.get("name", ""), alg=ns.PATH, reverse=is_reverse)
|
||||||
|
# data = temp_date2 + data[4000:]
|
||||||
|
if sort == "modified":
|
||||||
|
# 根据 sort 参数对结果进行排序,海量文件可能导致排序缓慢,因此限制排序前4000个
|
||||||
|
temp_date1 = data[:4000]
|
||||||
|
temp_date2 = sorted(temp_date1, key=lambda x: x["modified"], reverse=is_reverse)
|
||||||
|
data = temp_date2 + data[4000:]
|
||||||
|
elif sort == "size":
|
||||||
|
# 根据 sort 参数对结果进行排序,海量文件可能导致排序缓慢,因此限制排序前4000个
|
||||||
|
temp_date1 = data[:4000]
|
||||||
|
temp_date2 = sorted(temp_date1, key=lambda x: x["size"] if x["size"] is not None else 0, reverse=is_reverse)
|
||||||
|
data = temp_date2 + data[4000:]
|
||||||
|
|
||||||
|
data_info = {
|
||||||
|
'data':data,
|
||||||
|
'file_nums':file_nums,
|
||||||
|
'dir_nums':dir_nums,
|
||||||
|
'total_nums':file_nums+dir_nums
|
||||||
|
}
|
||||||
|
return data_info
|
||||||
|
|
||||||
|
def get_filedir_attribute(path,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 获取文件/目录属性
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
if not path:
|
||||||
|
return None
|
||||||
|
if os.path.isfile(path):
|
||||||
|
name = os.path.basename(path)
|
||||||
|
file_info = os.stat(path)
|
||||||
|
modified_time = datetime.datetime.fromtimestamp(file_info.st_mtime)
|
||||||
|
formatted_time = modified_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
access_time = datetime.datetime.fromtimestamp(file_info.st_atime)
|
||||||
|
formatted_at = access_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
return {"name":name,"type":"file","is_link":is_link(path),"path":windows_path_replace(path,is_windows=is_windows),"size":file_info.st_size,"permissions":oct(file_info.st_mode)[-3:],"owner_uid":file_info.st_uid,"owner":system.GetUidName(path,file_info.st_uid),"gid":file_info.st_gid,"group":system.GetGroupidName(path,file_info.st_gid),"modified":formatted_time,"access_at":formatted_at}
|
||||||
|
elif os.path.isdir(path):
|
||||||
|
name = os.path.basename(path)
|
||||||
|
dir_info = os.stat(path)
|
||||||
|
modified_time = datetime.datetime.fromtimestamp(dir_info.st_mtime)
|
||||||
|
formatted_time = modified_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
access_time = datetime.datetime.fromtimestamp(dir_info.st_atime)
|
||||||
|
formatted_at = access_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
return {"name":name,"type":"dir","is_link":is_link(path),"path":windows_path_replace(path,is_windows=is_windows),"size":get_directory_size(path),"permissions":oct(dir_info.st_mode)[-3:],"owner_uid":dir_info.st_uid,"owner":system.GetUidName(path,dir_info.st_uid),"gid":dir_info.st_gid,"group":system.GetGroupidName(path,dir_info.st_gid),"modified":formatted_time,"access_at":formatted_at}
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_file(path,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 创建文件
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
#去除干扰
|
||||||
|
filename = os.path.basename(path).strip()
|
||||||
|
filepath = os.path.dirname(path).strip()
|
||||||
|
if not filename:
|
||||||
|
raise ValueError("请填写文件名")
|
||||||
|
if not is_windows:
|
||||||
|
path = os.path.join(filepath, filename)
|
||||||
|
else:
|
||||||
|
filepath = filepath.replace("//", "/").replace("/", "\\")
|
||||||
|
path = os.path.join(filepath, filename)
|
||||||
|
if path[-1] == '.':
|
||||||
|
raise ValueError("文件名不能以'.'点结尾")
|
||||||
|
if len(filename)>100 or len(filename) < 1:
|
||||||
|
raise ValueError("长度在1到100个字符之间")
|
||||||
|
black_list = ['\\','/', '&', '*', '|', ';', '"', "'", '<', '>']
|
||||||
|
for black in black_list:
|
||||||
|
if black in filename:
|
||||||
|
raise ValueError("文件名不能包含指定特殊字符")
|
||||||
|
if os.path.exists(path):
|
||||||
|
return ValueError("该文件已存在")
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
os.makedirs(filepath)
|
||||||
|
# 创建空文件
|
||||||
|
open(path, 'w+').close()
|
||||||
|
|
||||||
|
def create_dir(path,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 创建目录
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
path = path.replace("//", "/")
|
||||||
|
if path[-1] == '.':
|
||||||
|
raise ValueError("目录名不能以'.'点结尾")
|
||||||
|
dirname = os.path.basename(path)
|
||||||
|
if len(dirname)>100 or len(dirname) < 1:
|
||||||
|
raise ValueError("长度在1到100个字符之间")
|
||||||
|
black_list = ['\\','/', '&', '*', '|', ';', '"', "'", '<', '>']
|
||||||
|
for black in black_list:
|
||||||
|
if black in dirname:
|
||||||
|
raise ValueError("文件名不能包含指定特殊字符")
|
||||||
|
if os.path.exists(path):
|
||||||
|
return ValueError("该目录已存在")
|
||||||
|
os.makedirs(path)
|
||||||
|
|
||||||
|
def delete_dir(path,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 删除目录
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
if not os.path.exists(path) and not os.path.islink(path):
|
||||||
|
raise ValueError("目录不存在")
|
||||||
|
|
||||||
|
#检查哪些目录不能被删除
|
||||||
|
check_no_delete(path,is_windows)
|
||||||
|
|
||||||
|
if os.path.islink(path):
|
||||||
|
os.remove(path)
|
||||||
|
else:
|
||||||
|
shutil.rmtree(path)
|
||||||
|
|
||||||
|
def delete_file(path,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 删除文件
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
if not os.path.exists(path) and not os.path.islink(path):
|
||||||
|
raise ValueError("文件不存在")
|
||||||
|
|
||||||
|
#检查哪些目录不能被删除
|
||||||
|
check_no_delete(path,is_windows)
|
||||||
|
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
|
def rename_file(sPath,dPath,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 重命名文件或目录
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
@params sPath 源路径
|
||||||
|
@params dPath 新路径
|
||||||
|
"""
|
||||||
|
if sPath == dPath:
|
||||||
|
return ValueError("源目标名称相同,已忽略")
|
||||||
|
dPath = dPath.replace("//", "/")
|
||||||
|
sPath = sPath.replace('//', '/')
|
||||||
|
if dPath[-1] == '.':
|
||||||
|
raise ValueError("不能以'.'点结尾")
|
||||||
|
if not os.path.exists(sPath):
|
||||||
|
raise ValueError("源文件/目录不存在")
|
||||||
|
if os.path.exists(dPath):
|
||||||
|
raise ValueError("目标存在相同名称")
|
||||||
|
if dPath[-1] == '/':
|
||||||
|
dPath = dPath[:-1]
|
||||||
|
|
||||||
|
#安全检查
|
||||||
|
if check_in_black_list(dPath,is_windows):
|
||||||
|
raise ValueError("与系统内置冲突,请更换名称")
|
||||||
|
|
||||||
|
os.rename(sPath, dPath)
|
||||||
|
|
||||||
|
def copy_file(sPath,dPath,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 复制文件
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
@params sPath 源路径
|
||||||
|
@params dPath 新路径
|
||||||
|
"""
|
||||||
|
# if sPath == dPath:
|
||||||
|
# raise ValueError("源目标相同,已忽略")
|
||||||
|
dPath = dPath.replace("//", "/")
|
||||||
|
sPath = sPath.replace('//', '/')
|
||||||
|
if dPath[-1] == '.':
|
||||||
|
raise ValueError("不能以'.'点结尾")
|
||||||
|
if not os.path.exists(sPath):
|
||||||
|
raise ValueError("源文件不存在")
|
||||||
|
# if os.path.exists(dPath):
|
||||||
|
# raise ValueError("目标存在相同名称")
|
||||||
|
|
||||||
|
shutil.copyfile(sPath, dPath)
|
||||||
|
|
||||||
|
def copy_dir(sPath,dPath,is_windows=False,cover=False):
|
||||||
|
"""
|
||||||
|
@name 复制目录
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
@params sPath 源路径
|
||||||
|
@params dPath 新路径
|
||||||
|
"""
|
||||||
|
if sPath == dPath:
|
||||||
|
raise ValueError("源和目标相同,已忽略")
|
||||||
|
dPath = dPath.replace("//", "/")
|
||||||
|
sPath = sPath.replace('//', '/')
|
||||||
|
if dPath[-1] == '.':
|
||||||
|
raise ValueError("不能以'.'点结尾")
|
||||||
|
if not os.path.exists(sPath):
|
||||||
|
raise ValueError("源目录不存在")
|
||||||
|
if cover and os.path.exists(dPath):
|
||||||
|
shutil.rmtree(dPath)
|
||||||
|
# if os.path.exists(dPath):
|
||||||
|
# raise ValueError("目标存在相同名称")
|
||||||
|
# if not os.path.exists(dPath):
|
||||||
|
# os.makedirs(dPath)
|
||||||
|
shutil.copytree(sPath, dPath)
|
||||||
|
|
||||||
|
def move_file(sPath,dPath,is_windows=False,cover=False):
|
||||||
|
"""
|
||||||
|
@name 移动文件/目录
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
@params sPath 源路径
|
||||||
|
@params dPath 新路径
|
||||||
|
"""
|
||||||
|
if sPath == dPath:
|
||||||
|
raise ValueError("源和目标相同,已忽略")
|
||||||
|
dPath = dPath.replace("//", "/")
|
||||||
|
sPath = sPath.replace('//', '/')
|
||||||
|
if dPath[-1] == '.':
|
||||||
|
raise ValueError("不能以'.'点结尾")
|
||||||
|
if dPath[-1] == '/':
|
||||||
|
dPath = dPath[:-1]
|
||||||
|
if not os.path.exists(sPath):
|
||||||
|
raise ValueError("源目录不存在")
|
||||||
|
#安全检查
|
||||||
|
if check_in_black_list(dPath,is_windows):
|
||||||
|
raise ValueError("与系统内置冲突,请更换名称")
|
||||||
|
is_dir = os.path.isdir(sPath)
|
||||||
|
if cover and os.path.exists(dPath):
|
||||||
|
if is_dir:
|
||||||
|
shutil.rmtree(dPath)
|
||||||
|
else:
|
||||||
|
os.remove(dPath)
|
||||||
|
|
||||||
|
shutil.move(sPath, dPath)
|
||||||
|
|
||||||
|
def batch_operate(param,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 批量操作(移动、复制、压缩、权限、删除)
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
@params param 请求参数
|
||||||
|
"""
|
||||||
|
type = param.get('type',None)
|
||||||
|
if type in ['copy','move']:
|
||||||
|
confirm = param.get('confirm',False)
|
||||||
|
skip_list = ast_convert(param.get('skipList',[]))#需要跳过覆盖的文件列表
|
||||||
|
dPath = param.get('path',"")
|
||||||
|
sPath = ast_convert(param.get('spath',[]))
|
||||||
|
if not dPath or not sPath:
|
||||||
|
return False,"参数错误",4000,None
|
||||||
|
dPath = dPath.replace('//', '/')
|
||||||
|
# if dPath[-1] == '/':
|
||||||
|
# dPath = dPath[:-1]
|
||||||
|
conflict_list = []
|
||||||
|
if not confirm:#初次先检查有冲突则返回确认
|
||||||
|
for d in sPath:
|
||||||
|
if d[-1] == '.':
|
||||||
|
raise ValueError("%s不能以'.'点结尾"%d)
|
||||||
|
if d[-1] == '/':
|
||||||
|
d = d[:-1]
|
||||||
|
dfile = dPath + '/' + os.path.basename(d)
|
||||||
|
if os.path.exists(dfile):
|
||||||
|
conflict_list.append({
|
||||||
|
'path':d,
|
||||||
|
'name':os.path.basename(d)
|
||||||
|
})
|
||||||
|
if conflict_list:
|
||||||
|
return False,"文件冲突",4050,conflict_list
|
||||||
|
|
||||||
|
if skip_list:
|
||||||
|
for s in skip_list:
|
||||||
|
if s['path'] in sPath:
|
||||||
|
sPath.remove(s)
|
||||||
|
|
||||||
|
if type == 'copy':
|
||||||
|
for sf in sPath:
|
||||||
|
dfile = dPath + '/' + os.path.basename(sf)
|
||||||
|
if os.path.commonpath([dfile, sf]) == sf:
|
||||||
|
return False,'从{}复制到{}有包含关系,请更换目标目录!'.format(sf, dfile),4000,None
|
||||||
|
for sf in sPath:
|
||||||
|
dfile = dPath + '/' + os.path.basename(sf)
|
||||||
|
if os.path.isdir(sf):
|
||||||
|
shutil.copytree(sf, dfile)
|
||||||
|
else:
|
||||||
|
shutil.copyfile(sf, dfile)
|
||||||
|
return True,"批量复制成功",2000,None
|
||||||
|
else:
|
||||||
|
for sf in sPath:
|
||||||
|
dfile = dPath + '/' + os.path.basename(sf)
|
||||||
|
move_file(sPath=sf,dPath=dfile,is_windows=is_windows,cover=True)
|
||||||
|
return True,"批量剪切成功",2000,None
|
||||||
|
elif type == 'zip':
|
||||||
|
dPath = param.get('path',"")
|
||||||
|
sPath = ast_convert(param.get('spath',[]))
|
||||||
|
zip_type = param.get('zip_type',"")
|
||||||
|
if not dPath or not sPath:
|
||||||
|
return False,"参数错误",4000,None
|
||||||
|
if not zip_type in ["tar","zip"]:
|
||||||
|
return False,"不支持的压缩格式",4000,None
|
||||||
|
dPath = dPath.replace('//', '/')
|
||||||
|
# if dPath[-1] == '/':
|
||||||
|
# dPath = dPath[:-1]
|
||||||
|
func_zip(zip_filename=dPath,items=sPath,zip_type=zip_type)
|
||||||
|
return True,"压缩成功",2000,None
|
||||||
|
elif type == 'unzip':
|
||||||
|
dPath = param.get('path',"")
|
||||||
|
sPath = param.get('spath',"")
|
||||||
|
zip_type = param.get('zip_type',"")
|
||||||
|
if not dPath or not sPath:
|
||||||
|
return False,"参数错误",4000,None
|
||||||
|
dPath = dPath.replace('//', '/')
|
||||||
|
func_unzip(zip_filename=sPath,extract_path=dPath)
|
||||||
|
return True,"解压成功",2000,None
|
||||||
|
elif type == 'pms':
|
||||||
|
pass
|
||||||
|
elif type == 'del':
|
||||||
|
sPath = ast_convert(param.get('spath',[]))
|
||||||
|
for sf in sPath:
|
||||||
|
if os.path.isdir(sf):
|
||||||
|
delete_dir(path=sf,is_windows=is_windows)
|
||||||
|
else:
|
||||||
|
delete_file(path=sf,is_windows=is_windows)
|
||||||
|
return True,"批量删除成功",2000,None
|
||||||
|
else:
|
||||||
|
return False,"类型错误",4000,None
|
||||||
|
|
||||||
|
|
||||||
|
def func_zip(zip_filename,items,zip_type):
|
||||||
|
"""
|
||||||
|
@name 压缩
|
||||||
|
@author lybbn<2024-03-07>
|
||||||
|
@param zip_filename 压缩后的文件名(含路径)
|
||||||
|
@param items 需要压缩的文件或目录列表['/var/log/ruyi.log']
|
||||||
|
@param zip_type 压缩类型(tar 格式.tar.gz、zip 格式 .zip)
|
||||||
|
"""
|
||||||
|
if not items:
|
||||||
|
raise ValueError("需要压缩的文件或目录不能为空")
|
||||||
|
if zip_type == "zip":
|
||||||
|
zip_directories_and_files(zip_filename,items)
|
||||||
|
elif zip_type == "tar":
|
||||||
|
create_tar_gz(zip_filename,items)
|
||||||
|
else:
|
||||||
|
raise ValueError("不支持的压缩格式")
|
||||||
|
|
||||||
|
def func_unzip(zip_filename,extract_path):
|
||||||
|
"""
|
||||||
|
@name 解压
|
||||||
|
@author lybbn<2024-03-07>
|
||||||
|
@param zip_filename 压缩文件名(含路径)
|
||||||
|
@param extract_path 需要解压的目标目录
|
||||||
|
"""
|
||||||
|
if current_os == "windows":
|
||||||
|
#解除占用
|
||||||
|
from utils.server.windows import kill_cmd_if_working_dir
|
||||||
|
kill_cmd_if_working_dir(extract_path)
|
||||||
|
_, ext = os.path.splitext(zip_filename)
|
||||||
|
if ext in ['.tar.gz','.tgz','.tar.bz2','.tbz']:
|
||||||
|
with tarfile.open(zip_filename, 'r') as tar:
|
||||||
|
tar.extractall(extract_path)
|
||||||
|
elif ext == '.zip':
|
||||||
|
with zipfile.ZipFile(zip_filename, 'r') as zipf:
|
||||||
|
zipf.extractall(extract_path)
|
||||||
|
else:
|
||||||
|
raise ValueError("不支持的文件格式")
|
||||||
|
|
||||||
|
|
||||||
|
def zip_directories_and_files(zip_filename, items):
|
||||||
|
with zipfile.ZipFile(zip_filename, 'w') as zipf:
|
||||||
|
for item in items:
|
||||||
|
if os.path.isfile(item):
|
||||||
|
zipf.write(item, os.path.basename(item))
|
||||||
|
elif os.path.isdir(item):
|
||||||
|
for root, _, files in os.walk(item):
|
||||||
|
for file in files:
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
zipf.write(file_path, os.path.relpath(file_path, os.path.dirname(item)))
|
||||||
|
|
||||||
|
def create_tar_gz(tar_filename, items):
|
||||||
|
with tarfile.open(tar_filename, "w:gz") as tar:
|
||||||
|
for item in items:
|
||||||
|
if os.path.isfile(item):
|
||||||
|
tar.add(item, arcname=os.path.basename(item))
|
||||||
|
elif os.path.isdir(item):
|
||||||
|
tar.add(item, arcname=os.path.basename(item))
|
||||||
97
backend/utils/security/no_delete_list.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#!/bin/python
|
||||||
|
#coding: utf-8
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | system: 如意面板
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | Author: lybbn
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | QQ: 1042594286
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | Date: 2024-01-03
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# 操作系统/项目 不能删除目录
|
||||||
|
# ------------------------------
|
||||||
|
|
||||||
|
#linux 系统目录
|
||||||
|
Linux_System_List = [
|
||||||
|
{'path':'/dev','desc':'系统设备目录'},
|
||||||
|
{'path':'/mnt','desc':'系统挂载目录'},
|
||||||
|
{'path':'/media','desc':'系统多媒体目录'},
|
||||||
|
{'path':'/tmp','desc':'系统临时目录'},
|
||||||
|
{'path':'/sys','desc':'系统目录'},
|
||||||
|
{'path':'/proc','desc':'系统进程目录'},
|
||||||
|
{'path': '/etc', 'desc': '系统配置目录'},
|
||||||
|
{'path': '/boot', 'desc': '系统引导目录'},
|
||||||
|
{'path': '/root', 'desc': '根用户家目录'},
|
||||||
|
{'path': '/home', 'desc': '用户家目录'},
|
||||||
|
{'path': '/var', 'desc': '系统目录'},
|
||||||
|
{'path': '/', 'desc': '系统根目录'},
|
||||||
|
{'path': '/*', 'desc': '系统根目录'},
|
||||||
|
{'path': '/bin', 'desc': '系统命令目录'},
|
||||||
|
{'path': '/usr/bin', 'desc': '用户命令目录'},
|
||||||
|
{'path': '/sbin', 'desc': '系统管理员命令目录'},
|
||||||
|
{'path': '/usr/sbin', 'desc': '系统管理员命令目录'},
|
||||||
|
{'path': '/lib', 'desc': '系统动态库目录'},
|
||||||
|
{'path': '/lib32', 'desc': '系统动态库目录'},
|
||||||
|
{'path': '/lib64', 'desc': '系统动态库目录'},
|
||||||
|
{'path': '/usr/lib', 'desc': '用户库目录'},
|
||||||
|
{'path': '/usr/lib64', 'desc': '用户库目录'},
|
||||||
|
{'path': '/usr/local/lib', 'desc': '用户库目录'},
|
||||||
|
{'path': '/usr/local/lib64', 'desc': '用户库目录'},
|
||||||
|
{'path': '/usr/local/libexec', 'desc': '用户库目录'},
|
||||||
|
{'path': '/usr/local/sbin', 'desc': '系统脚本目录'},
|
||||||
|
{'path': '/usr/local/bin', 'desc': '系统脚本目录'},
|
||||||
|
{'path': '/var/log', 'desc': '系统日志目录'},
|
||||||
|
]
|
||||||
|
|
||||||
|
#Windows 系统目录
|
||||||
|
Windows_System_List = [
|
||||||
|
{'path': 'c:/', 'desc': 'C盘禁止删除'},
|
||||||
|
{'path': 'c:/Windows', 'desc': 'Windows 操作系统核心文件目录'},
|
||||||
|
{'path': 'c:/Program Files', 'desc': '应用程序安装目录(64 位系统)'},
|
||||||
|
{'path': 'c:/Program Files (x86)', 'desc': '应用程序安装目录(32 位系统)'},
|
||||||
|
{'path': 'c:/Users', 'desc': '用户个人文件目录'},
|
||||||
|
{'path': 'c:/ProgramData', 'desc': '应用程序共享数据目录'},
|
||||||
|
{'path': 'c:/Windows/System32', 'desc': 'Windows 系统关键系统文件目录'},
|
||||||
|
{'path': 'c:/Users/Public', 'desc': '公共用户文件目录'},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def check_in_black_list(path,is_windows=False):
|
||||||
|
|
||||||
|
if is_windows:
|
||||||
|
for wd in Windows_System_List:
|
||||||
|
if path == wd['path']:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
for lx in Linux_System_List:
|
||||||
|
if path == lx['path']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_no_delete(path,is_windows=False):
|
||||||
|
"""
|
||||||
|
@name 检查哪些目录不能被删除
|
||||||
|
@author lybbn<2024-02-22>
|
||||||
|
"""
|
||||||
|
path = path.replace('//', '/')
|
||||||
|
if path[-1:] == '/' or path[-1:] == '\\':
|
||||||
|
path = path[:-1]
|
||||||
|
if is_windows:
|
||||||
|
drive_name = path.split(':')[0]
|
||||||
|
if not drive_name:
|
||||||
|
raise ValueError("路径错误")
|
||||||
|
drive_name = drive_name.lower()
|
||||||
|
path_without_drive =path.split(':')[1] if drive_name else None
|
||||||
|
if not path_without_drive:
|
||||||
|
raise ValueError("不能直接删除磁盘")
|
||||||
|
path = drive_name+":"+path_without_drive
|
||||||
|
for wd in Windows_System_List:
|
||||||
|
if path == wd['path']:
|
||||||
|
raise ValueError("【%s】不可删除!"%wd['desc'])
|
||||||
|
if not is_windows:
|
||||||
|
for lx in Linux_System_List:
|
||||||
|
if path == lx['path']:
|
||||||
|
raise ValueError("【%s】不可删除!"%lx['desc'])
|
||||||
54
backend/utils/security/safe_filter.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/python
|
||||||
|
#coding: utf-8
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | system: 如意面板
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | Author: lybbn
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | QQ: 1042594286
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
# | Date: 2024-02-03
|
||||||
|
# +-------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# 字符安全过滤
|
||||||
|
# ------------------------------
|
||||||
|
|
||||||
|
import re
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
def filter_xss1(content):
|
||||||
|
"""
|
||||||
|
@name xss过滤,只替换xss相关关键字符
|
||||||
|
@author lybbn<2024-02-08>
|
||||||
|
"""
|
||||||
|
dic_str = {
|
||||||
|
'<':'<',
|
||||||
|
'>':'>',
|
||||||
|
'"':'"',
|
||||||
|
"'":'''
|
||||||
|
}
|
||||||
|
for i in dic_str.keys():
|
||||||
|
content = content.replace(i,dic_str[i])
|
||||||
|
return content
|
||||||
|
|
||||||
|
def filter_xss2(content):
|
||||||
|
"""
|
||||||
|
@name xss过滤,替换script中的尖括号为html转义
|
||||||
|
@author lybbn<2024-02-08>
|
||||||
|
"""
|
||||||
|
return content.replace('<', '<').replace('>', '>')
|
||||||
|
|
||||||
|
def is_validate_db_passwd(passwd):
|
||||||
|
"""
|
||||||
|
@name 是否有效数据库密码
|
||||||
|
@author lybbn<2024-08-24>
|
||||||
|
"""
|
||||||
|
if not passwd:
|
||||||
|
return False,"密码不能为空"
|
||||||
|
pattern = r"[\'\"`]|%27|%22|%60"
|
||||||
|
encoded_password = urllib.parse.quote(passwd)
|
||||||
|
match = re.search(pattern, encoded_password)
|
||||||
|
if match:
|
||||||
|
return False,"密码不能包含特殊字符"
|
||||||
|
return True,"ok"
|
||||||
@ -11,6 +11,8 @@
|
|||||||
# ------------------------------
|
# ------------------------------
|
||||||
# linux系统命令工具类封装
|
# linux系统命令工具类封装
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
|
import pwd
|
||||||
|
import grp
|
||||||
import os, sys, re, time, json
|
import os, sys, re, time, json
|
||||||
import psutil
|
import psutil
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
@ -390,4 +392,36 @@ def RestartServer():
|
|||||||
try:
|
try:
|
||||||
os.system("sync && init 6 &")
|
os.system("sync && init 6 &")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def GetUidName(file_path,uid=0):
|
||||||
|
"""
|
||||||
|
通过系统uid获取对应名称
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return pwd.getpwuid(uid).pw_name
|
||||||
|
except Exception as e:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def GetGroupidName(file_path,gid=0):
|
||||||
|
"""
|
||||||
|
通过系统goup id(所属组id)获取对应名称
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return grp.getgrgid(gid).gr_name
|
||||||
|
except Exception as e:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def ForceRemoveDir(directory):
|
||||||
|
"""
|
||||||
|
强制删除目录
|
||||||
|
"""
|
||||||
|
sys_dir = ['/root','/','/proc','/*','/root/*']
|
||||||
|
if directory in sys_dir:
|
||||||
|
raise ValueError("受保护的目录,无法删除!!!")
|
||||||
|
if os.path.exists(directory):
|
||||||
|
try:
|
||||||
|
# 执行 rm -rf 命令
|
||||||
|
subprocess.run(['rm', '-rf', directory], check=True)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise ValueError(f"强制删除目录错误: {e}")
|
||||||
@ -83,3 +83,13 @@ class system:
|
|||||||
def GetFileLastNumsLines(cls,path,num=1000):
|
def GetFileLastNumsLines(cls,path,num=1000):
|
||||||
data = myos.GetFileLastNumsLines(path=path,num=num)
|
data = myos.GetFileLastNumsLines(path=path,num=num)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def GetUidName(cls,file_path,uid=0):
|
||||||
|
data = myos.GetUidName(file_path,uid)
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def GetGroupidName(cls,file_path,gid=0):
|
||||||
|
data = myos.GetGroupidName(file_path,gid=gid)
|
||||||
|
return data
|
||||||
|
|||||||
@ -20,8 +20,7 @@ import winreg
|
|||||||
from config import EXEC_LOG_PATH, TEMP_EXEC_PATH
|
from config import EXEC_LOG_PATH, TEMP_EXEC_PATH
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import win32security
|
||||||
BASE_DIR = Path(__file__).resolve().parent
|
|
||||||
|
|
||||||
|
|
||||||
def ReadReg(path, key):
|
def ReadReg(path, key):
|
||||||
@ -71,13 +70,13 @@ def to_size(size):
|
|||||||
if not size: return '0.00 b'
|
if not size: return '0.00 b'
|
||||||
size = float(size)
|
size = float(size)
|
||||||
|
|
||||||
d = ('b', 'KB', 'MB', 'GB', 'TB');
|
d = ('b', 'KB', 'MB', 'GB', 'TB')
|
||||||
s = d[0];
|
s = d[0]
|
||||||
for b in d:
|
for b in d:
|
||||||
if size < 1024: return ("%.2f" % size) + ' ' + b;
|
if size < 1024: return ("%.2f" % size) + ' ' + b
|
||||||
size = size / 1024;
|
size = size / 1024
|
||||||
s = b;
|
s = b
|
||||||
return ("%.2f" % size) + ' ' + b;
|
return ("%.2f" % size) + ' ' + b
|
||||||
|
|
||||||
|
|
||||||
def is_64bitos():
|
def is_64bitos():
|
||||||
@ -310,11 +309,11 @@ def GetBootTime():
|
|||||||
if sys_time: return sys_time
|
if sys_time: return sys_time
|
||||||
import math
|
import math
|
||||||
tStr = time.time() - psutil.boot_time()
|
tStr = time.time() - psutil.boot_time()
|
||||||
min = tStr / 60;
|
min = tStr / 60
|
||||||
hours = min / 60;
|
hours = min / 60
|
||||||
days = math.floor(hours / 24);
|
days = math.floor(hours / 24)
|
||||||
hours = math.floor(hours - (days * 24));
|
hours = math.floor(hours - (days * 24))
|
||||||
min = math.floor(min - (days * 60 * 24) - (hours * 60));
|
min = math.floor(min - (days * 60 * 24) - (hours * 60))
|
||||||
sys_time = "{}天".format(int(days))
|
sys_time = "{}天".format(int(days))
|
||||||
cache.set(key, sys_time, 1800)
|
cache.set(key, sys_time, 1800)
|
||||||
return sys_time
|
return sys_time
|
||||||
@ -352,10 +351,10 @@ def GetCpuInfo(interval=1):
|
|||||||
arrs = ret.strip().split('\n\n')
|
arrs = ret.strip().split('\n\n')
|
||||||
for x in arrs:
|
for x in arrs:
|
||||||
val = x.strip()
|
val = x.strip()
|
||||||
if not val: continue;
|
if not val: continue
|
||||||
try:
|
try:
|
||||||
val = int(val)
|
val = int(val)
|
||||||
cpuW += 1;
|
cpuW += 1
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -365,7 +364,7 @@ def GetCpuInfo(interval=1):
|
|||||||
if not cpu_name:
|
if not cpu_name:
|
||||||
try:
|
try:
|
||||||
cpu_name = '{} * {}'.format(
|
cpu_name = '{} * {}'.format(
|
||||||
ReadReg(r'HARDWARE\DESCRIPTION\System\CentralProcessor\0', 'ProcessorNameString').strip(), cpuW);
|
ReadReg(r'HARDWARE\DESCRIPTION\System\CentralProcessor\0', 'ProcessorNameString').strip(), cpuW)
|
||||||
except:
|
except:
|
||||||
cpu_name = ''
|
cpu_name = ''
|
||||||
cache.set('lybbn_cpu_cpu_name', cpu_name, 86400)
|
cache.set('lybbn_cpu_cpu_name', cpu_name, 86400)
|
||||||
@ -388,7 +387,7 @@ def GetDiskInfo():
|
|||||||
diskInfo = cache.get(key)
|
diskInfo = cache.get(key)
|
||||||
if diskInfo: return diskInfo
|
if diskInfo: return diskInfo
|
||||||
try:
|
try:
|
||||||
diskIo = psutil.disk_partitions();
|
diskIo = psutil.disk_partitions()
|
||||||
except:
|
except:
|
||||||
import string
|
import string
|
||||||
diskIo = []
|
diskIo = []
|
||||||
@ -453,4 +452,28 @@ def RestartServer():
|
|||||||
try:
|
try:
|
||||||
os.system("shutdown /r /f /t 0")
|
os.system("shutdown /r /f /t 0")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def GetUidName(file_path,uid=0):
|
||||||
|
"""
|
||||||
|
通过系统uid获取对应名称
|
||||||
|
"""
|
||||||
|
#如果运行GetUidName在file_path所在的分区,可能导致文件占用导致获取失败
|
||||||
|
try:
|
||||||
|
security_descriptor = win32security.GetFileSecurity(file_path, win32security.OWNER_SECURITY_INFORMATION)
|
||||||
|
owner_sid = security_descriptor.GetSecurityDescriptorOwner()
|
||||||
|
owner_name, domain_name, type = win32security.LookupAccountSid(None, owner_sid)
|
||||||
|
return owner_name
|
||||||
|
except Exception as e:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def GetGroupidName(file_path,gid=0):
|
||||||
|
"""
|
||||||
|
通过系统goup id(所属组id)获取对应名称
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
group_sid = win32security.GetFileSecurity(file_path, win32security.GROUP_SECURITY_INFORMATION).GetSecurityDescriptorGroup()
|
||||||
|
group_name, domain, type = win32security.LookupAccountSid(None, group_sid)
|
||||||
|
return group_name
|
||||||
|
except Exception as e:
|
||||||
|
return ""
|
||||||
7437
frontend/package-lock.json
generated
@ -5,7 +5,7 @@
|
|||||||
"email": "1042594286@qq.com",
|
"email": "1042594286@qq.com",
|
||||||
"url": "https://gitee.com/lybbn"
|
"url": "https://gitee.com/lybbn"
|
||||||
},
|
},
|
||||||
"version": "1.3.6",
|
"version": "1.3.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run dev",
|
"start": "npm run dev",
|
||||||
@ -16,10 +16,19 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.5.1",
|
"@codemirror/autocomplete": "^6.5.1",
|
||||||
"@codemirror/lang-css": "^6.1.1",
|
"@codemirror/lang-css": "^6.1.1",
|
||||||
|
"@codemirror/lang-go": "^6.0.1",
|
||||||
|
"@codemirror/lang-java": "^6.0.1",
|
||||||
"@codemirror/lang-javascript": "^6.1.6",
|
"@codemirror/lang-javascript": "^6.1.6",
|
||||||
"@codemirror/lang-json": "^6.0.1",
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
|
"@codemirror/lang-markdown": "^6.3.2",
|
||||||
|
"@codemirror/lang-php": "^6.0.1",
|
||||||
"@codemirror/lang-python": "^6.1.2",
|
"@codemirror/lang-python": "^6.1.2",
|
||||||
|
"@codemirror/lang-sql": "^6.8.0",
|
||||||
"@codemirror/lang-vue": "^0.1.1",
|
"@codemirror/lang-vue": "^0.1.1",
|
||||||
|
"@codemirror/lang-xml": "^6.1.0",
|
||||||
|
"@codemirror/lang-yaml": "^6.1.2",
|
||||||
|
"@codemirror/language": "^6.11.0",
|
||||||
|
"@codemirror/legacy-modes": "^6.5.0",
|
||||||
"@codemirror/theme-one-dark": "^6.1.1",
|
"@codemirror/theme-one-dark": "^6.1.1",
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@tinymce/tinymce-vue": "^5.0.0",
|
"@tinymce/tinymce-vue": "^5.0.0",
|
||||||
@ -32,8 +41,8 @@
|
|||||||
"core-js": "^3.32.2",
|
"core-js": "^3.32.2",
|
||||||
"cropper": "^4.1.0",
|
"cropper": "^4.1.0",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "5.6.0",
|
||||||
"element-plus": "^2.7.8",
|
"element-plus": "2.9.6",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"js-base64": "^3.7.5",
|
"js-base64": "^3.7.5",
|
||||||
@ -45,25 +54,25 @@
|
|||||||
"screenfull": "^6.0.2",
|
"screenfull": "^6.0.2",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"tinymce": "5.10.2",
|
"tinymce": "5.10.2",
|
||||||
"vue": "^3.3.4",
|
"vue": "3.5.13",
|
||||||
"vue-axios": "^3.5.2",
|
"vue-axios": "^3.5.2",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
"vue-codemirror": "^6.1.1",
|
"vue-codemirror": "^6.1.1",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^10.0.4",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.4.5",
|
||||||
"xe-utils": "^3.5.13"
|
"xe-utils": "^3.5.31"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.22.20",
|
"@babel/core": "^7.26.0",
|
||||||
"@babel/eslint-parser": "^7.22.15",
|
"@babel/eslint-parser": "^7.25.9",
|
||||||
"@vue/babel-plugin-jsx": "^1.1.5",
|
"@vue/babel-plugin-jsx": "^1.2.5",
|
||||||
"@vue/cli-plugin-babel": "~5.0.8",
|
"@vue/cli-plugin-babel": "~5.0.8",
|
||||||
"@vue/cli-plugin-router": "~5.0.8",
|
"@vue/cli-plugin-router": "~5.0.8",
|
||||||
"@vue/cli-service": "~5.0.8",
|
"@vue/cli-service": "~5.0.8",
|
||||||
"@vue/compiler-sfc": "^3.3.4",
|
"@vue/compiler-sfc": "^3.5.12",
|
||||||
"compression-webpack-plugin": "^10.0.0",
|
"compression-webpack-plugin": "^11.1.0",
|
||||||
"sass": "^1.68.0",
|
"sass": "^1.80.6",
|
||||||
"sass-loader": "^13.3.2",
|
"sass-loader": "^16.0.3",
|
||||||
"svg-sprite-loader": "^6.0.11"
|
"svg-sprite-loader": "^6.0.11"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|||||||
@ -4,18 +4,56 @@
|
|||||||
</el-config-provider>
|
</el-config-provider>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref, onMounted,watch,computed } from 'vue'
|
import {ref, onMounted,watch,computed,onBeforeUnmount } from 'vue'
|
||||||
import {useSiteThemeStore} from "@/store/siteTheme";
|
import {useSiteThemeStore} from "@/store/siteTheme";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
import config from '@/config'
|
import config from '@/config'
|
||||||
|
import WebSocket from '@/utils/websocket';
|
||||||
|
import { useMutitabsStore } from "@/store/mutitabs";
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { ElNotification } from 'element-plus'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const siteThemeStore = useSiteThemeStore()
|
const siteThemeStore = useSiteThemeStore()
|
||||||
const colorPrimary = siteThemeStore.colorPrimary
|
const colorPrimary = siteThemeStore.colorPrimary
|
||||||
const menuHeaderColor = siteThemeStore.menuHeaderColor
|
const menuHeaderColor = siteThemeStore.menuHeaderColor
|
||||||
|
|
||||||
onMounted(()=>{
|
const wsReceive = (message) => {
|
||||||
|
const data = JSON.parse(message.data);
|
||||||
|
const { unread } = data;
|
||||||
|
useMutitabsStore().setUnread(unread);
|
||||||
|
if (data.msg_type === 'SYS') {
|
||||||
|
ElNotification({
|
||||||
|
title: '系统消息',
|
||||||
|
message: data.content,
|
||||||
|
type: 'success',
|
||||||
|
position: 'bottom-right',
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function initWebSocket() {
|
||||||
|
WebSocket.initWebSocket(wsReceive)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch( () => route.path, () => {
|
||||||
|
if (!WebSocket.websocket) {
|
||||||
|
try {
|
||||||
|
initWebSocket()
|
||||||
|
} catch (e) {
|
||||||
|
console.log('websocket错误');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
siteThemeStore.setColorPrimary(colorPrimary)
|
siteThemeStore.setColorPrimary(colorPrimary)
|
||||||
if (siteThemeStore.siteTheme === 'dark') {
|
if (siteThemeStore.siteTheme === 'dark') {
|
||||||
document.documentElement.classList.add('dark')
|
document.documentElement.classList.add('dark')
|
||||||
@ -31,11 +69,14 @@
|
|||||||
|
|
||||||
//此内容不能删除
|
//此内容不能删除
|
||||||
console.info(`%cDjango-Vue-Lyadmin 专业版 %cVer${config.APP_VER} %chttps://doc.lybbn.cn/`,
|
console.info(`%cDjango-Vue-Lyadmin 专业版 %cVer${config.APP_VER} %chttps://doc.lybbn.cn/`,
|
||||||
"color:#409EFF;font-size: 22px;font-weight:bolder",
|
"color:#409EFF;font-size: 22px;font-weight:bolder",
|
||||||
"color:#999;font-size: 12px",
|
"color:#999;font-size: 12px",
|
||||||
"color:#333"
|
"color:#333"
|
||||||
)
|
)
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
// 关闭连接
|
||||||
|
WebSocket.closeWebSocket();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
#app {
|
#app {
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import axios from 'axios';
|
import {reqExpost,ajaxGet,ajaxPost,ajaxDelete,ajaxPut,ajaxPatch,uploadImg,ajaxGetDetailByID,ajaxDownloadExcel,uploadFilev2,getDownloadFile,downloadFile} from './request';
|
||||||
import {reqExpost,ajaxGet,ajaxPost,ajaxDelete,ajaxPut,ajaxPatch,uploadImg,ajaxGetDetailByID,ajaxDownloadExcel,uploadFilev2} from './request';
|
|
||||||
import {url} from './url';
|
|
||||||
|
|
||||||
// 获取登录页的信息
|
// 获取登录页的信息
|
||||||
export const login = params => ajaxPost({url: `token/`,params})
|
export const login = params => ajaxPost({url: `token/`,params})
|
||||||
@ -257,7 +255,12 @@ export const messagesMessagenoticeEdit = params => ajaxPut({url: `messages/messa
|
|||||||
//消息公告-删除
|
//消息公告-删除
|
||||||
export const messagesMessagenoticeDelete = params => ajaxDelete({url: `messages/messagenotice/`,params})
|
export const messagesMessagenoticeDelete = params => ajaxDelete({url: `messages/messagenotice/`,params})
|
||||||
|
|
||||||
|
//我的消息 列表
|
||||||
|
export const getOwnMessage = params => ajaxGet({url: `messages/messagenotice/ownmsg/`,params})
|
||||||
|
//我的消息 删除
|
||||||
|
export const delOwnMessage = params => ajaxPost({url: `messages/messagenotice/delownmsg/`,params})
|
||||||
|
//我的消息 设置已读
|
||||||
|
export const readOwnMessage = params => ajaxPost({url: `messages/messagenotice/readownmsg/`,params})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*省市区选择
|
*省市区选择
|
||||||
@ -487,6 +490,18 @@ export const systemFiles= params => ajaxGet({url: `system/files/`,params})
|
|||||||
// 文件管理 新增
|
// 文件管理 新增
|
||||||
export const systemFilesAdd= (params,config) => uploadFilev2({url: `system/files/`,params,config})
|
export const systemFilesAdd= (params,config) => uploadFilev2({url: `system/files/`,params,config})
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*文件管理
|
||||||
|
* */
|
||||||
|
// 文件管理 - 文件管理
|
||||||
|
export const sysFileManage = params => ajaxPost({url: `system/fileManage/`,params})
|
||||||
|
// 文件管理 - 文件管理-下载
|
||||||
|
export const sysdownloadFile = params => getDownloadFile({url: `download/`,params})
|
||||||
|
export const sysFileDownload = params => downloadFile({url: `system/fileManage/download/`,params})
|
||||||
|
// 文件管理 - 文件管理-生成token
|
||||||
|
export const sysFileGetToken = params => ajaxPost({url: `system/fileManage/getToken/`,params})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*流程管理
|
*流程管理
|
||||||
* */
|
* */
|
||||||
|
|||||||
@ -5,11 +5,20 @@ import router from "@/router";
|
|||||||
import {setStorage,getStorage,getToken} from '@/utils/util'
|
import {setStorage,getStorage,getToken} from '@/utils/util'
|
||||||
import sysConfig from "@/config"
|
import sysConfig from "@/config"
|
||||||
import {useMutitabsStore} from "@/store/mutitabs";
|
import {useMutitabsStore} from "@/store/mutitabs";
|
||||||
|
import { cancelRequestState } from "@/store/cancelRequest";
|
||||||
|
|
||||||
var request = axios.create({
|
var request = axios.create({
|
||||||
timeout: sysConfig.TIMEOUT,
|
timeout: sysConfig.TIMEOUT,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
request.interceptors.request.use(config => {
|
||||||
|
const cancelRequest = cancelRequestState()
|
||||||
|
config.cancelToken = new axios.CancelToken(cancel => {
|
||||||
|
cancelRequest.addCancelToken(cancel)
|
||||||
|
})
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
|
||||||
// http response 拦截器
|
// http response 拦截器
|
||||||
request.interceptors.response.use(
|
request.interceptors.response.use(
|
||||||
response => {
|
response => {
|
||||||
@ -189,6 +198,26 @@ export function ajaxDownloadExcel (opt) {
|
|||||||
return ajax(opt,"excel")
|
return ajax(opt,"excel")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDownloadFile (param) {
|
||||||
|
const urlParams = new URLSearchParams(param.params);
|
||||||
|
let urls = url + param.url;
|
||||||
|
urls += '?' + urlParams.toString()
|
||||||
|
window.location.href = urls
|
||||||
|
}
|
||||||
|
|
||||||
|
export function downloadFile (param) {
|
||||||
|
let token= getToken()
|
||||||
|
return axios({
|
||||||
|
method: "post",
|
||||||
|
url: url+param.url,
|
||||||
|
headers: {
|
||||||
|
Authorization: 'JWT ' + token,
|
||||||
|
},
|
||||||
|
data:JSON.parse(JSON.stringify(param.params)),
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(res => res);
|
||||||
|
}
|
||||||
|
|
||||||
//websocket获取jwt请求token
|
//websocket获取jwt请求token
|
||||||
export function getJWTAuthorization() {
|
export function getJWTAuthorization() {
|
||||||
var token= getToken()
|
var token= getToken()
|
||||||
@ -377,4 +406,30 @@ export async function uploadFilev2(param){
|
|||||||
let data = param.params//formData
|
let data = param.params//formData
|
||||||
let config = param.params.config || {}//额外配置
|
let config = param.params.config || {}//额外配置
|
||||||
return await http.post(newurl, data, config);
|
return await http.post(newurl, data, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uploadFile (param,onProgress) {
|
||||||
|
const token= getToken()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios({
|
||||||
|
method: 'post',
|
||||||
|
url: url+param.url ,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
Authorization: sysConfig.TOKEN_PREFIX + token,
|
||||||
|
},
|
||||||
|
data:param.formData,
|
||||||
|
onUploadProgress: (progressEvent) => {
|
||||||
|
if (onProgress) {
|
||||||
|
onProgress(progressEvent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
resolve(response.data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
BIN
frontend/src/assets/img/fileicons/file.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
frontend/src/assets/img/fileicons/folder.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
frontend/src/assets/img/fileicons/go.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
frontend/src/assets/img/fileicons/image.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
frontend/src/assets/img/fileicons/linux.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
frontend/src/assets/img/fileicons/nodejs.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
frontend/src/assets/img/fileicons/php.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
frontend/src/assets/img/fileicons/python.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
frontend/src/assets/img/fileicons/video.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
frontend/src/assets/img/fileicons/vue.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
frontend/src/assets/img/fileicons/wordpress.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
frontend/src/assets/img/fileicons/zip.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
<template>
|
<template>
|
||||||
<div :class="isBackgroud?'lyPagination-page-bk':'lyPagination-page'">
|
<div :class="isBackgroud?'lyPagination-page-bk':'lyPagination-page'">
|
||||||
<el-pagination class="page-box" @size-change="handleSizeChange" @current-change="handleCurrentChange" background :size="small?'small':'default'" :current-page="childMsg.page" :page-sizes="pageSizes" :page-size="childMsg.limit" :layout="layout" :total="childMsg.total"></el-pagination>
|
<el-pagination class="page-box" :class="'page-box-'+position" @size-change="handleSizeChange" @current-change="handleCurrentChange" background :size="small?'small':'default'" :current-page="childMsg.page" :page-sizes="pageSizes" :page-size="childMsg.limit" :layout="layout" :total="childMsg.total"></el-pagination>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -18,15 +18,21 @@
|
|||||||
return siteThemeStore.pagingLayout
|
return siteThemeStore.pagingLayout
|
||||||
})
|
})
|
||||||
|
|
||||||
const isBackgroud = computed(()=>{
|
|
||||||
return pagingLayout.value == 'backgroud'?true:false
|
|
||||||
})
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
childMsg: { type: Object, default: () => {} },
|
childMsg: { type: Object, default: () => {} },
|
||||||
pageSizes: { type: Array, default: [10,20,30,40,50,100] },
|
pageSizes: { type: Array, default: [10,20,30,40,50,100] },
|
||||||
layout: { type: String, default: "total, sizes, prev, pager, next, jumper" },
|
layout: { type: String, default: "total, sizes, prev, pager, next, jumper" },
|
||||||
small: {type:Boolean, default:false}
|
small: {type:Boolean, default:false},
|
||||||
|
position:{type:String, default:"center"},
|
||||||
|
border: {type:Boolean, default:true},
|
||||||
|
})
|
||||||
|
|
||||||
|
const isBackgroud = computed(()=>{
|
||||||
|
if(props.border){
|
||||||
|
return pagingLayout.value == 'backgroud'?true:false
|
||||||
|
}else{
|
||||||
|
return true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let pageparm = ref({
|
let pageparm = ref({
|
||||||
@ -70,4 +76,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.page-box-center {
|
||||||
|
margin: 10px auto;
|
||||||
|
}
|
||||||
|
.page-box-left {
|
||||||
|
margin: 10px 0 auto;
|
||||||
|
}
|
||||||
|
.page-box-right {
|
||||||
|
margin: 10px 10px 10px auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
430
frontend/src/components/codeEditor/index.vue
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
<!--
|
||||||
|
/**
|
||||||
|
* 代码编辑器
|
||||||
|
* version: 1.1
|
||||||
|
* author: lybbn
|
||||||
|
* program 如意面板
|
||||||
|
* email: 1042594286@qq.com
|
||||||
|
* date: 2024-02-08
|
||||||
|
* EditDate: 2024-03-08
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<codemirror
|
||||||
|
ref="lyCodemirrorRef"
|
||||||
|
class="lyCodemirror"
|
||||||
|
:class="!showLineNums?'lyCodemirrorDynamic':''"
|
||||||
|
v-model="code"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:style="{ height: _height,fontSize:fontSize}"
|
||||||
|
:autofocus="true"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tab-size="4"
|
||||||
|
:extensions="extensions"
|
||||||
|
@ready="handleReady"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {ref,reactive, onMounted,computed,watch, shallowRef,nextTick,onBeforeUnmount} from 'vue'
|
||||||
|
import { Codemirror } from 'vue-codemirror'
|
||||||
|
import { EditorState,Compartment } from "@codemirror/state";
|
||||||
|
import { basicSetup,minimalSetup } from "codemirror";
|
||||||
|
import { defaultKeymap,standardKeymap, insertTab } from "@codemirror/commands";
|
||||||
|
import { EditorView,keymap, drawSelection,highlightSpecialChars,highlightWhitespace,highlightActiveLine,dropCursor,highlightActiveLineGutter,rectangularSelection,crosshairCursor } from "@codemirror/view";
|
||||||
|
import { syntaxHighlighting,StreamLanguage } from "@codemirror/language";
|
||||||
|
import { autocompletion } from "@codemirror/autocomplete";
|
||||||
|
// import { historyKeymap } from "@codemirror/history";
|
||||||
|
import { ruby } from "@codemirror/legacy-modes/mode/ruby"; // Ruby
|
||||||
|
import { shell } from "@codemirror/legacy-modes/mode/shell"; // Bash
|
||||||
|
import { nginx } from "@codemirror/legacy-modes/mode/nginx";
|
||||||
|
import { lua } from "@codemirror/legacy-modes/mode/lua";
|
||||||
|
import { properties } from "@codemirror/legacy-modes/mode/properties";
|
||||||
|
|
||||||
|
import { oneDark } from "@codemirror/theme-one-dark";
|
||||||
|
import { javascript } from "@codemirror/lang-javascript";
|
||||||
|
|
||||||
|
|
||||||
|
import { json } from "@codemirror/lang-json";
|
||||||
|
import { css } from "@codemirror/lang-css";
|
||||||
|
import { python } from "@codemirror/lang-python";
|
||||||
|
import { vue } from "@codemirror/lang-vue";
|
||||||
|
import { yaml } from "@codemirror/lang-yaml";
|
||||||
|
import { php } from "@codemirror/lang-php";
|
||||||
|
import { java } from "@codemirror/lang-java";
|
||||||
|
import { go } from "@codemirror/lang-go";
|
||||||
|
import { html } from "@codemirror/lang-html";
|
||||||
|
import { sql } from "@codemirror/lang-sql";
|
||||||
|
import { markdown } from "@codemirror/lang-markdown";
|
||||||
|
import { xml } from "@codemirror/lang-xml";
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:modelValue","change","scrollBottomChange"])
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: "javascript",//mode=log时会无主题
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: [String,Number],
|
||||||
|
default: "auto",
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default:""
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
default: "dark"
|
||||||
|
},
|
||||||
|
readOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
bottom:{
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
//超出宽度是否自动换行
|
||||||
|
lineWrapping:{
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
//显示行号
|
||||||
|
showLineNums:{
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
fontSize:{
|
||||||
|
type: String,
|
||||||
|
default: "14px"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let lyCodemirrorRef = ref(null)
|
||||||
|
let _height = computed(() => {
|
||||||
|
return Number(props.height)?Number(props.height)+'px':props.height
|
||||||
|
})
|
||||||
|
//code和modelValue双向绑定
|
||||||
|
const code = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit('change', value)
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//移动到指定行
|
||||||
|
function moveToLine(view,line) {
|
||||||
|
if(view == null || view.state == null){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!/^\d+$/.test(line) || +line <= 0 || +line > view.state.doc.lines){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let pos = view.state.doc.line(+line).from
|
||||||
|
view.dispatch({selection: {anchor: pos}, userEvent: "select",scrollIntoView:true})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
let view = ref(null)
|
||||||
|
const handleReady = (payload) => {
|
||||||
|
view.value = payload.view
|
||||||
|
// const state = view.value.state
|
||||||
|
// const ranges = state.selection.ranges
|
||||||
|
// const selected = ranges.reduce((r, range) => r + range.to - range.from, 0)
|
||||||
|
// const cursor = ranges[0].anchor
|
||||||
|
// const length = state.doc.length
|
||||||
|
// const lines = state.doc.lines
|
||||||
|
view.value.contentDOM.addEventListener('mousedown', () => {
|
||||||
|
const selectionChangeHandler = () => {
|
||||||
|
if(props.mode == 'log'){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const hasSelection = !view.value.state.selection.main.empty;
|
||||||
|
const cmHighlightSpaceElements = view.value.dom.querySelectorAll('.cm-highlightSpace');
|
||||||
|
cmHighlightSpaceElements.forEach((element) => {
|
||||||
|
if (hasSelection) {
|
||||||
|
element.classList.add('show-ryspace');
|
||||||
|
} else {
|
||||||
|
element.classList.remove('show-ryspace');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const cmHighlightTabElements = view.value.dom.querySelectorAll('.cm-highlightTab');
|
||||||
|
cmHighlightTabElements.forEach((element) => {
|
||||||
|
if (hasSelection) {
|
||||||
|
element.classList.add('show-rytab');
|
||||||
|
} else {
|
||||||
|
element.classList.remove('show-rytab');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const mouseupHandler = () => {
|
||||||
|
view.value.contentDOM.removeEventListener('selectionchange', selectionChangeHandler);
|
||||||
|
view.value.contentDOM.removeEventListener('mouseup', mouseupHandler);
|
||||||
|
// 手动触发一次选择状态检测
|
||||||
|
selectionChangeHandler();
|
||||||
|
};
|
||||||
|
|
||||||
|
view.value.contentDOM.addEventListener('selectionchange', selectionChangeHandler);
|
||||||
|
view.value.contentDOM.addEventListener('mouseup', mouseupHandler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取总行数
|
||||||
|
const getDocLines = ()=>{
|
||||||
|
if(!!view.value){
|
||||||
|
return view.value.state.doc.lines
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
let allLines = ref(getDocLines())
|
||||||
|
|
||||||
|
let languageConf = new Compartment, tabSize = new Compartment
|
||||||
|
|
||||||
|
let lang = computed(() => {
|
||||||
|
const langMap = {
|
||||||
|
javascript: javascript,
|
||||||
|
json: json,
|
||||||
|
css: css,
|
||||||
|
python: python,
|
||||||
|
vue: vue,
|
||||||
|
php: php,
|
||||||
|
java: java,
|
||||||
|
go: go,
|
||||||
|
yaml: yaml,
|
||||||
|
xml: xml,
|
||||||
|
markdown: markdown,
|
||||||
|
html: html,
|
||||||
|
sql: sql,
|
||||||
|
shell: () => StreamLanguage.define(shell),
|
||||||
|
ruby: () => StreamLanguage.define(ruby),
|
||||||
|
nginx: () => StreamLanguage.define(nginx),
|
||||||
|
lua: () => StreamLanguage.define(lua),
|
||||||
|
properties: () => StreamLanguage.define(properties),
|
||||||
|
}
|
||||||
|
return langMap[props.mode]?.() || null
|
||||||
|
})
|
||||||
|
//自定义代码提示
|
||||||
|
function myCompletions(context) {
|
||||||
|
let word = context.matchBefore(/\w*/)
|
||||||
|
if (word.from == word.to && !context.explicit) return null;
|
||||||
|
return {
|
||||||
|
from: word.from,
|
||||||
|
options: [
|
||||||
|
// { label: "getWidgetRef", type: "variable" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let isReadOnly = computed(() => {
|
||||||
|
return EditorState.readOnly.of(props.readOnly)
|
||||||
|
})
|
||||||
|
|
||||||
|
let extensions = shallowRef([
|
||||||
|
// basicSetup,
|
||||||
|
// keymap.of([...standardKeymap, {key: "Tab", run: insertTab}]),
|
||||||
|
// languageConf.of(lang.value),
|
||||||
|
oneDark,
|
||||||
|
// autocompletion({ override: [myCompletions] }),
|
||||||
|
isReadOnly.value,
|
||||||
|
// EditorState.readOnly.of(props.readOnly),
|
||||||
|
// EditorView.editable.of(!props.readOnly),
|
||||||
|
// placeholder(props.placeholder),
|
||||||
|
// EditorView.lineWrapping,
|
||||||
|
drawSelection(),
|
||||||
|
highlightSpecialChars(),
|
||||||
|
highlightWhitespace({
|
||||||
|
space: '·',
|
||||||
|
tab: '→'
|
||||||
|
}),
|
||||||
|
highlightActiveLine(), // 高亮当前行
|
||||||
|
// dropCursor(),
|
||||||
|
highlightActiveLineGutter(),
|
||||||
|
rectangularSelection(),
|
||||||
|
crosshairCursor()
|
||||||
|
])
|
||||||
|
|
||||||
|
function reloadConfig(){
|
||||||
|
const newConfig = [
|
||||||
|
oneDark,
|
||||||
|
isReadOnly.value,
|
||||||
|
drawSelection(),
|
||||||
|
highlightSpecialChars(),
|
||||||
|
highlightWhitespace({
|
||||||
|
space: '·',
|
||||||
|
tab: '→'
|
||||||
|
}),
|
||||||
|
highlightActiveLine(), // 高亮当前行
|
||||||
|
// dropCursor(),
|
||||||
|
highlightActiveLineGutter(),
|
||||||
|
rectangularSelection(),
|
||||||
|
crosshairCursor()
|
||||||
|
]
|
||||||
|
if(!!lang.value && lang.value !== null){
|
||||||
|
newConfig.push(lang.value)
|
||||||
|
}
|
||||||
|
if(props.lineWrapping){
|
||||||
|
newConfig.push(EditorView.lineWrapping)
|
||||||
|
}
|
||||||
|
extensions.value = newConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
function debounce(func, wait = 300) {
|
||||||
|
let timeout;
|
||||||
|
return function(...args) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
func.apply(this, args);
|
||||||
|
}, wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(props,
|
||||||
|
debounce((newProps, oldProps) => { // 添加防抖
|
||||||
|
reloadConfig()
|
||||||
|
}, 300),
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
flush: 'post' // 添加flush配置
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// watch(()=>props.mode,(nval)=>{
|
||||||
|
// if(!!lang.value && lang.value !== null){
|
||||||
|
// if (!extensions.value.some(ext => ext === lang.value)) {
|
||||||
|
// extensions.value.push(lang.value);
|
||||||
|
// // 强制更新编辑器组件
|
||||||
|
// const temp = [...extensions.value];
|
||||||
|
// extensions.value = [];
|
||||||
|
// extensions.value = temp;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },{deep:true})
|
||||||
|
|
||||||
|
let scrollDOM = ref(null)
|
||||||
|
let isAtBottom = ref(false)
|
||||||
|
onMounted(()=>{
|
||||||
|
if(props.bottom && !!view.value){
|
||||||
|
nextTick(()=>{
|
||||||
|
moveToLine(view.value,getDocLines())
|
||||||
|
// view.value.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if(!!lang.value && lang.value !== null){
|
||||||
|
if (!extensions.value.some(ext => ext === lang.value)) {
|
||||||
|
extensions.value.push(lang.value);
|
||||||
|
// 强制更新编辑器组件
|
||||||
|
const temp = [...extensions.value];
|
||||||
|
extensions.value = [];
|
||||||
|
extensions.value = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(props.lineWrapping){
|
||||||
|
extensions.value.push(EditorView.lineWrapping)
|
||||||
|
}
|
||||||
|
if(!!view.value){
|
||||||
|
scrollDOM.value = view.value.scrollDOM
|
||||||
|
// 绑定滚动事件
|
||||||
|
scrollDOM.value.addEventListener('scroll', handleDomScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleDomScroll(e) {
|
||||||
|
// 使用requestAnimationFrame优化滚动性能
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const { scrollHeight, scrollTop, clientHeight } = scrollDOM.value
|
||||||
|
const isBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 1
|
||||||
|
if(isAtBottom.value !== isBottom) {
|
||||||
|
isAtBottom.value = isBottom
|
||||||
|
emit("scrollBottomChange", isBottom)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function isScrollAtBottom(){
|
||||||
|
return isAtBottom.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToBottom(){
|
||||||
|
if(!!view.value){
|
||||||
|
moveToLine(view.value,getDocLines())
|
||||||
|
// view.value.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(()=>code.value,newValue=>{
|
||||||
|
if(props.bottom && !!view.value){
|
||||||
|
nextTick(()=>{
|
||||||
|
moveToLine(view.value,getDocLines())
|
||||||
|
// view.value.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
// 移除滚动事件监听
|
||||||
|
if (scrollDOM.value) {
|
||||||
|
scrollDOM.value.removeEventListener('scroll', handleDomScroll)
|
||||||
|
}
|
||||||
|
// 清理大型对象引用
|
||||||
|
view.value = null
|
||||||
|
scrollDOM.value = null
|
||||||
|
languageConf = null
|
||||||
|
tabSize = null
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
scrollToBottom,
|
||||||
|
reloadConfig,
|
||||||
|
isScrollAtBottom
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.cm-content {
|
||||||
|
/* font-family: 'Consolas', 'Monaco', 'monospace'; */
|
||||||
|
font-family: 'JetBrains Mono', Consolas, 'Courier New', monospace;
|
||||||
|
/* font: 14px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; */
|
||||||
|
}
|
||||||
|
.ͼo {
|
||||||
|
color: #edede1;
|
||||||
|
background-color: #272821;
|
||||||
|
}
|
||||||
|
.ͼo .cm-gutters {
|
||||||
|
background-color: #2F3128;
|
||||||
|
color: #8F908A;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
/* .ͼ1 .cm-highlightSpace{
|
||||||
|
background-image: radial-gradient(circle at 50% 55%, #666 10%, transparent 8%);
|
||||||
|
background-size: 12px 20px;
|
||||||
|
} */
|
||||||
|
.ͼ1 .cm-highlightSpace {
|
||||||
|
/* 默认不显示空格符号 */
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.ͼ1 .cm-highlightTab {
|
||||||
|
/* 默认不显示Tab符号 */
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 当编辑器聚焦时,选中代码显示空格符号 */
|
||||||
|
.ͼ1 .cm-highlightSpace.show-ryspace {
|
||||||
|
background-image: radial-gradient(circle at 50% 55%, #666 10%, transparent 8%);
|
||||||
|
background-size: 12px 20px;
|
||||||
|
}
|
||||||
|
.ͼ1 .cm-highlightTab.show-rytab {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='20'%3E%3Cpath stroke='%23888' stroke-width='1' fill='none' d='M1 10H196L190 5M190 15L196 10M197 4L197 16'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style scoped>
|
||||||
|
.lyCodemirrorDynamic:deep(.cm-gutters) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
198
frontend/src/components/dialog/dialogv2.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<!--
|
||||||
|
* @Descripttion: 弹窗扩展组件
|
||||||
|
* @version: 1.0
|
||||||
|
* @program: 如意面板
|
||||||
|
* @Author: lybbn
|
||||||
|
* @Email:1042594286@qq.com
|
||||||
|
* @Date: 2024.01.11
|
||||||
|
* @EditDate: 2024.01.11
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="ly-dialog">
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
:close-on-click-modal="closeOnClickModal"
|
||||||
|
:title="title"
|
||||||
|
:width="width"
|
||||||
|
:top="top"
|
||||||
|
:fullscreen="screeFull"
|
||||||
|
:center="center"
|
||||||
|
:before-close="beforeClose"
|
||||||
|
:append-to-body="appendToBody"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:draggable="draggable"
|
||||||
|
:show-close="false"
|
||||||
|
@closed="closed"
|
||||||
|
ref="lyDialogRef"
|
||||||
|
>
|
||||||
|
<template #header="{ close, titleId, titleClass }">
|
||||||
|
<div>
|
||||||
|
<slot name="header">
|
||||||
|
<div :id="titleId" :class="titleClass" style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;width: 90%;">{{ title }}</div>
|
||||||
|
</slot>
|
||||||
|
<div class="ly-dialog__headerbtn">
|
||||||
|
<button aria-label="fullscreen" type="button" @click="handleFullScreenClick" v-if="showFullScreen">
|
||||||
|
<el-icon v-if="screeFull" class="el-dialog__close"><Minus /></el-icon>
|
||||||
|
<el-icon v-else class="el-dialog__close"><full-screen /></el-icon>
|
||||||
|
</button>
|
||||||
|
<button aria-label="close" type="button" @click="close" v-if="showClose">
|
||||||
|
<el-icon class="el-dialog__close"><close /></el-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-loading="loading" style="height: 100%;">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<template v-if="$slots.footer" #footer>
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref,watch,onMounted } from 'vue';
|
||||||
|
import 'element-plus/es/components/dialog/style/css'
|
||||||
|
const emits = defineEmits(['closed','onChangeFullScreen'])
|
||||||
|
|
||||||
|
let lyDialogRef = ref(null)
|
||||||
|
let visible = ref(false)
|
||||||
|
let screeFull = ref(false)
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '50%'
|
||||||
|
},
|
||||||
|
center: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
top: {
|
||||||
|
type: String,
|
||||||
|
default: '10vh'
|
||||||
|
},
|
||||||
|
draggable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
appendToBody: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
closeOnClickModal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
fullscreen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
showFullScreen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
showClose: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
beforeClose:Function// 关闭回调函数
|
||||||
|
});
|
||||||
|
|
||||||
|
function openDialog() {
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDialog() {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function closed() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFullScreenClick(){
|
||||||
|
screeFull.value = !screeFull.value
|
||||||
|
emits('onChangeFullScreen',screeFull.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRef(){
|
||||||
|
return lyDialogRef.value
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
screeFull.value = props.fullscreen
|
||||||
|
visible.value = props.modelValue
|
||||||
|
emits('onChangeFullScreen',screeFull.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(()=>props.modelValue,(nval)=>{
|
||||||
|
visible.value = nval; // modelValue改变是同步子组件visible的值
|
||||||
|
},{deep:true})
|
||||||
|
watch(()=>props.fullscreen,(nval)=>{
|
||||||
|
screeFull.value = nval
|
||||||
|
},{deep:true})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getRef
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ly-dialog__headerbtn {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--el-dialog-padding-primary);
|
||||||
|
right: var(--el-dialog-padding-primary);
|
||||||
|
}
|
||||||
|
.ly-dialog__headerbtn button {
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--el-message-close-size,16px);
|
||||||
|
margin-left: 15px;
|
||||||
|
color: var(--el-color-info);
|
||||||
|
}
|
||||||
|
.ly-dialog__headerbtn button:hover .el-dialog__close {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
.ly-dialog:deep(.el-dialog) .el-dialog__body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.ly-dialog:deep(.el-dialog).is-fullscreen {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
top:0px !important;
|
||||||
|
left:0px !important;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.ly-dialog:deep(.el-dialog).is-fullscreen .el-dialog__header {
|
||||||
|
border-bottom:var(--el-border);
|
||||||
|
margin-right:0 !important;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.ly-dialog:deep(.el-dialog).is-fullscreen .el-dialog__body {
|
||||||
|
flex:1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.ly-dialog:deep(.el-dialog).is-fullscreen .el-dialog__footer {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-top:var(--el-border);
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
279
frontend/src/components/file/fileEditor.vue
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
<!--
|
||||||
|
* @Descripttion: 在线文件编辑器
|
||||||
|
* @version: 1.0
|
||||||
|
* @program: 如意面板
|
||||||
|
* @Author: lybbn
|
||||||
|
* @Email:1042594286@qq.com
|
||||||
|
* @Date: 2024.03.03
|
||||||
|
* @EditDate: 2024.03.03
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<LyDialog ref="codeMirrorDialogRef" :loading="loadingPage" v-model="dialogVisible" :title="loadingTitle" :width="width" :top="top" :before-close="handleClose" :fullscreen="fullscreen" @onChangeFullScreen="handleChangeFullScreen">
|
||||||
|
<div class="handlecaoz" ref="handlecaozRef">
|
||||||
|
<el-button :disabled="loadingPage" size="small" type="info" color="#2F3128" dark icon="Refresh" @click="getData">刷新</el-button>
|
||||||
|
<el-button :disabled="loadingPage||codeMirrorReadOnly" size="small" type="info" color="#2F3128" dark @click="handleSave(false)">
|
||||||
|
<template #icon>
|
||||||
|
<el-icon v-if="saveIcon=='InfoFilled'" style="color: var(--el-color-warning);"><InfoFilled /></el-icon>
|
||||||
|
<el-icon v-else><FolderChecked /></el-icon>
|
||||||
|
</template>
|
||||||
|
保存
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<lyCodeEditor @keydown="handleKeydown" v-model="content" @change="handleContentChange" fontSize="15px" :placeholder="!!content?'':''" :showLineNums="true" :lineWrapping="true" :mode="codeMirrorMode" :height="codeMirrorHeight" :read-only="codeMirrorReadOnly" ref="lyCodemirror"></lyCodeEditor>
|
||||||
|
<div class="handlestausbar" ref="handlestausbarRef">
|
||||||
|
文件位置:{{ fileState.path }}
|
||||||
|
</div>
|
||||||
|
</LyDialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { nextTick, ref,onMounted,onUnmounted } from 'vue';
|
||||||
|
import LyDialog from "@/components/dialog/dialogv2.vue";
|
||||||
|
import lyCodeEditor from '@/components/codeEditor/index.vue'
|
||||||
|
import { ElMessage,ElMessageBox } from 'element-plus'
|
||||||
|
import { deepClone } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['closed'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
apiObj: { type: Function, default: null },
|
||||||
|
successCode: { type: Number, default: 2000 },//网络请求完成代码
|
||||||
|
fullscreen: { type: Boolean, default: false },
|
||||||
|
width: { type: String, default: '60%' },//dialog宽度
|
||||||
|
top: { type: String, default: '10vh' },
|
||||||
|
})
|
||||||
|
|
||||||
|
let codeMirrorDialogRef = ref(null)
|
||||||
|
let codeMirrorHeight = ref("55vh")
|
||||||
|
let codeMirrorMode = ref("log")
|
||||||
|
let codeMirrorReadOnly = ref(false)
|
||||||
|
let lyCodemirror = ref(null)
|
||||||
|
let handlecaozRef = ref(null)
|
||||||
|
let handlestausbarRef = ref(null)
|
||||||
|
let content = ref("")
|
||||||
|
let oldContent = ref("")
|
||||||
|
let saveIcon = ref("FolderChecked")
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingTitle = ref("")
|
||||||
|
let loadingPage = ref(false)
|
||||||
|
let fileState = ref({
|
||||||
|
name:"",
|
||||||
|
path:"",
|
||||||
|
st_mtime:"",
|
||||||
|
})
|
||||||
|
let isFullScress = ref(false)
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
if(oldContent.value != content.value){
|
||||||
|
ElMessageBox.confirm('检测到文件变动,是否保存更改?', '提示', {
|
||||||
|
confirmButtonText: '保存',
|
||||||
|
cancelButtonText: '不保存',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
handleSave(true)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
emits('closed')
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(params,flag){
|
||||||
|
loadingTitle.value = flag
|
||||||
|
dialogVisible.value = true
|
||||||
|
const tempdata = deepClone(params)
|
||||||
|
fileState.value.name = tempdata.name
|
||||||
|
fileState.value.path = tempdata.path
|
||||||
|
nextTick(()=>{
|
||||||
|
getData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleContentChange(value){
|
||||||
|
if(value == oldContent.value){
|
||||||
|
saveIcon.value = "FolderChecked"
|
||||||
|
}else{
|
||||||
|
saveIcon.value = "InfoFilled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChangeFullScreen(isfull){
|
||||||
|
isFullScress.value = isfull
|
||||||
|
nextTick(()=>{
|
||||||
|
if(codeMirrorDialogRef.value){
|
||||||
|
const dialogRef = codeMirrorDialogRef.value.getRef()
|
||||||
|
if(dialogRef.dialogContentRef){
|
||||||
|
const dialogHeaderRef = dialogRef.dialogContentRef.$refs.headerRef
|
||||||
|
const dialogHeaderHeight = dialogHeaderRef.offsetHeight
|
||||||
|
const contentHeight = document.body.offsetHeight
|
||||||
|
if(isfull){
|
||||||
|
codeMirrorHeight.value = contentHeight - dialogHeaderHeight - handlecaozRef.value.offsetHeight - handlestausbarRef.value.offsetHeight
|
||||||
|
}else{
|
||||||
|
codeMirrorHeight.value = "55vh"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatJsonBody(value){
|
||||||
|
try {
|
||||||
|
return JSON.stringify(JSON.parse(value),null,4)
|
||||||
|
} catch (err) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData(){
|
||||||
|
loadingPage.value = true;
|
||||||
|
var reqData = {path:fileState.value.path,action:"read_file_body"}
|
||||||
|
if (!!props.apiObj && props.apiObj != {}) {
|
||||||
|
props.apiObj(reqData).then(res => {
|
||||||
|
loadingPage.value = false;
|
||||||
|
if (res.code == props.successCode) {
|
||||||
|
codeMirrorMode.value = res.data.language
|
||||||
|
if(codeMirrorMode.value == "json"){
|
||||||
|
content.value = formatJsonBody(res.data.content)
|
||||||
|
}else{
|
||||||
|
content.value = res.data.content
|
||||||
|
}
|
||||||
|
oldContent.value = content.value
|
||||||
|
fileState.st_mtime = res.data.st_mtime
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
codeMirrorReadOnly.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
loadingPage.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSave(closeDialog=false){
|
||||||
|
loadingPage.value = true;
|
||||||
|
var reqData = {path:fileState.value.path,content:content.value,action:"save_file_body",st_mtime:fileState.value.st_mtime,force:false}
|
||||||
|
if (!!props.apiObj && props.apiObj != {}) {
|
||||||
|
props.apiObj(reqData).then(res => {
|
||||||
|
loadingPage.value = false;
|
||||||
|
if (res.code == props.successCode) {
|
||||||
|
oldContent.value = content.value
|
||||||
|
ElMessage.success("保存成功")
|
||||||
|
fileState.st_mtime = res.data.st_mtime
|
||||||
|
saveIcon.value = "FolderChecked"
|
||||||
|
if(closeDialog){
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
}else if(res.code == 4050){
|
||||||
|
ElMessageBox.confirm(`在线文件可能发生变动,与当前版本不符,是否强制保存`, '提醒', {
|
||||||
|
closeOnClickModal: false,
|
||||||
|
cancelButtonText:"不保存",
|
||||||
|
confirmButtonText:"强制保存",
|
||||||
|
type:"warning"
|
||||||
|
}).then(ret => {
|
||||||
|
loadingPage.value = true;
|
||||||
|
let param = {path:fileState.value.path,content:content.value,action:"save_file_body",st_mtime:fileState.value.st_mtime,force:true}
|
||||||
|
props.apiObj(param).then(res2 => {
|
||||||
|
loadingPage.value = false;
|
||||||
|
if(res2.code == props.successCode){
|
||||||
|
oldContent.value = content.value
|
||||||
|
ElMessage.success("保存成功")
|
||||||
|
fileState.st_mtime = res2.data.st_mtime
|
||||||
|
saveIcon.value = "FolderChecked"
|
||||||
|
if(closeDialog){
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
ElMessage.warning(res2.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}).catch(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
loadingPage.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算搜索栏的高度
|
||||||
|
function listenResize() {
|
||||||
|
nextTick(() => {
|
||||||
|
if(codeMirrorDialogRef.value){
|
||||||
|
const dialogRef = codeMirrorDialogRef.value.getRef()
|
||||||
|
if(dialogRef.dialogContentRef){
|
||||||
|
const dialogHeaderRef = dialogRef.dialogContentRef.$refs.headerRef
|
||||||
|
const dialogHeaderHeight = dialogHeaderRef.offsetHeight
|
||||||
|
const contentHeight = document.body.offsetHeight
|
||||||
|
if(isFullScress.value){
|
||||||
|
codeMirrorHeight.value = contentHeight - dialogHeaderHeight - handlecaozRef.value.offsetHeight - handlestausbarRef.value.offsetHeight
|
||||||
|
}else{
|
||||||
|
codeMirrorHeight.value = "55vh"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeydown(event){
|
||||||
|
if (event.ctrlKey && event.key.toLowerCase() === 's') {
|
||||||
|
event.preventDefault();
|
||||||
|
if(oldContent.value == content.value){
|
||||||
|
ElMessage.warning("检测到文件未变动,无需保存")
|
||||||
|
}else{
|
||||||
|
handleSave(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
// 监听页面宽度变化搜索框的高度
|
||||||
|
window.addEventListener('resize', listenResize);
|
||||||
|
listenResize()
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(()=>{
|
||||||
|
// 页面销毁,去掉监听事件
|
||||||
|
window.removeEventListener("resize", listenResize);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
:deep(.el-dialog) .el-dialog__body {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
:deep(.el-dialog){
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
:deep(.el-dialog) .el-dialog__header{
|
||||||
|
padding: 15px !important;
|
||||||
|
}
|
||||||
|
.handlecaoz{
|
||||||
|
background: #2F3128;
|
||||||
|
}
|
||||||
|
.handlecaoz:deep(.el-button){
|
||||||
|
border-radius: 0;
|
||||||
|
border-right-color:#272821;
|
||||||
|
}
|
||||||
|
.el-button + .el-button {
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
.handlestausbar{
|
||||||
|
background: #2F3128;
|
||||||
|
overflow-x: auto;
|
||||||
|
height:25px;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
405
frontend/src/components/file/fileList.vue
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
<!--
|
||||||
|
* @Descripttion: 目录选择组件
|
||||||
|
* @version: 1.0
|
||||||
|
* @program: 如意面板
|
||||||
|
* @Author: lybbn
|
||||||
|
* @Email:1042594286@qq.com
|
||||||
|
* @Date: 2024.03.02
|
||||||
|
* @EditDate: 2024.03.02
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="lyfileselect">
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="680px" :before-close="handleClose" :showFullScreen="false" :draggable="false">
|
||||||
|
<div ref="tableSelect">
|
||||||
|
<el-row class="handle-header" >
|
||||||
|
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="handle-path-filter">
|
||||||
|
<div>
|
||||||
|
<el-button icon="Refresh" @click="handleSearch" size="small"/>
|
||||||
|
</div>
|
||||||
|
<div class="handle-path-filter-right">
|
||||||
|
<span class="handle-path-filter-right-inner">
|
||||||
|
<span>
|
||||||
|
<el-link @click.stop="jumpRootClickPath">
|
||||||
|
<el-icon :size="20"><HomeFilled /></el-icon>
|
||||||
|
</el-link>
|
||||||
|
</span>
|
||||||
|
<span v-for="path in paths" :key="path.url" class="flex-center">
|
||||||
|
<span class="right-arrow" v-if="!!path.url">></span>
|
||||||
|
<el-link @click.stop="jumpClickPath(path.url)">
|
||||||
|
{{ path.name }}
|
||||||
|
</el-link>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<el-table ref="ruyFileTtableRef" :header-cell-class-name="cellselectionclass" v-loading="loadingPage" :data="tableData" height="40vh" style="width: 100%;border-top: 1px solid var(--el-border-color-lighter);" @selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="30"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="名称" min-width="200" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="flex-center">
|
||||||
|
<img class="ruyi-fileicons" v-if="scope.row.type == 'dir'" src="@/assets/img/fileicons/folder.png" />
|
||||||
|
<img class="ruyi-fileicons" v-else-if="scope.row.type == 'file' && getFileExt(scope.row.path) === 'py'" src="@/assets/img/fileicons/python.png" />
|
||||||
|
<img class="ruyi-fileicons" v-else-if="scope.row.type == 'file' && getFileExt(scope.row.path) === 'php'" src="@/assets/img/fileicons/php.png" />
|
||||||
|
<img class="ruyi-fileicons" v-else-if="scope.row.type == 'file' && isImage(scope.row.path)" src="@/assets/img/fileicons/image.png" />
|
||||||
|
<img class="ruyi-fileicons" v-else-if="scope.row.type == 'file' && isVideo(scope.row.path)" src="@/assets/img/fileicons/video.png" />
|
||||||
|
<el-icon :size="20" v-else-if="scope.row.type == 'pan'"><MessageBox /></el-icon>
|
||||||
|
<img class="ruyi-fileicons" v-else src="@/assets/img/fileicons/file.png" />
|
||||||
|
<!-- <el-icon :size="20" v-if="scope.row.type == 'dir'"><Folder /></el-icon>
|
||||||
|
<el-icon :size="20" v-else-if="scope.row.type == 'file'"><Document /></el-icon>
|
||||||
|
<el-icon :size="20" v-else-if="scope.row.type == 'pan'"><MessageBox /></el-icon> -->
|
||||||
|
<el-link type="primary" @click.stop="nameJumpClick(scope.row)" style="margin-left: 5px;">{{ scope.row.name }}</el-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="权限" width="70" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.permissions">{{ scope.row.permissions}}</span>
|
||||||
|
<span v-else></span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="用户" width="130" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.owner">{{scope.row.owner}}</span>
|
||||||
|
<span v-else>失败</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="modified" label="修改时间" width="170" />
|
||||||
|
</el-table>
|
||||||
|
<Pagination :small="true" v-bind:child-msg="pageparm" @callFather="callFather" :border="false" position="center"></Pagination>
|
||||||
|
<template #footer>
|
||||||
|
<div style="float: left;font-size: 14px;">请勾选复选框</div>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">关闭</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave" :disabled="((onlyFileSelect && !selectIsFile))">选择</el-button>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref,computed } from 'vue';
|
||||||
|
import {sysFileGetToken,sysFileDownload,sysdownloadFile,sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone,downloadFileContent,formatUnitSize,canEditOnline } from "@/utils/util"
|
||||||
|
import { usePathSearchStatus } from '@/views/systemManage/files/hooks/pathSearchStatus';
|
||||||
|
import {useSiteThemeStore} from "@/store/siteTheme";
|
||||||
|
|
||||||
|
const emits = defineEmits(['change','closed'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
//是否多选,默认单选
|
||||||
|
multiple:{
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
//只选择文件模式
|
||||||
|
onlyFileSelect:{
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const siteThemeStore = useSiteThemeStore()
|
||||||
|
const ismobile = computed(() => {
|
||||||
|
return siteThemeStore.ismobile
|
||||||
|
})
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingPage = ref(false)
|
||||||
|
let loadingTitle = ref('')
|
||||||
|
let isWindows = ref(false)
|
||||||
|
|
||||||
|
let tableData = ref([])
|
||||||
|
let file_nums = ref(0)
|
||||||
|
let dir_nums = ref(0)
|
||||||
|
let paths = ref([])
|
||||||
|
const { pathSearchFilterStatus, searchPath, searchPathInputRef, searchPathInputBlur } = usePathSearchStatus(paths);
|
||||||
|
|
||||||
|
let formInline = ref({
|
||||||
|
path:"default",
|
||||||
|
sort:"name",
|
||||||
|
order:"asc",
|
||||||
|
search:"",
|
||||||
|
isDir:false,//只显示目录,不显示文件
|
||||||
|
containSub:false,
|
||||||
|
page:1,
|
||||||
|
limit:100
|
||||||
|
})
|
||||||
|
let pageparm = ref({
|
||||||
|
page: 1,
|
||||||
|
limit: 100,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
if(item){
|
||||||
|
const tempdata = deepClone(item)
|
||||||
|
formInline.value.path = tempdata.path
|
||||||
|
formInline.value.isDir = tempdata.isDir === undefined?false:tempdata.isDir
|
||||||
|
getData("list_dir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ruyFileTtableRef = ref(null)
|
||||||
|
let mutipleSelect = ref([])
|
||||||
|
function handleSelectionChange(selection){
|
||||||
|
mutipleSelect.value = [];
|
||||||
|
if (selection.length > 1) {
|
||||||
|
//移除上一次选中行数据
|
||||||
|
selection.shift();
|
||||||
|
//修改选中图标为未选中状态
|
||||||
|
ruyFileTtableRef.value.clearSelection();
|
||||||
|
//将当前选中行改为选中状态
|
||||||
|
ruyFileTtableRef.value.toggleRowSelection(selection[0]);
|
||||||
|
}
|
||||||
|
mutipleSelect.value = selection
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectIsFile = computed(() => {
|
||||||
|
let isFile = true
|
||||||
|
mutipleSelect.value.forEach(ele=>{
|
||||||
|
if(ele.type != "file"){
|
||||||
|
isFile = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if(mutipleSelect.value.length<1){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isFile
|
||||||
|
})
|
||||||
|
|
||||||
|
function callFather(parm) {
|
||||||
|
formInline.value.page = parm.page
|
||||||
|
formInline.value.limit = parm.limit
|
||||||
|
getData("list_dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData(action="list_dir"){
|
||||||
|
loadingPage.value = true;
|
||||||
|
let tempParams = {
|
||||||
|
...formInline.value
|
||||||
|
}
|
||||||
|
tempParams.action = action
|
||||||
|
sysFileManage(tempParams).then(res => {
|
||||||
|
loadingPage.value = false;
|
||||||
|
if (res.code == 2000) {
|
||||||
|
tableData.value = res.data.data.data
|
||||||
|
paths.value = res.data.data.paths
|
||||||
|
file_nums.value = res.data.data.file_nums
|
||||||
|
dir_nums.value = res.data.data.dir_nums
|
||||||
|
pageparm.value.page = res.data.page
|
||||||
|
pageparm.value.limit = res.data.limit
|
||||||
|
pageparm.value.total = res.data.total
|
||||||
|
isWindows.value = res.data.data.is_windows
|
||||||
|
formInline.value.path = res.data.data.path
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpClickPath(path){
|
||||||
|
formInline.value.sort = "name"
|
||||||
|
formInline.value.order = "asc"
|
||||||
|
formInline.value.path = path
|
||||||
|
getData("list_dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpRootClickPath(){
|
||||||
|
formInline.value.sort = "name"
|
||||||
|
formInline.value.order = "asc"
|
||||||
|
formInline.value.path = ""
|
||||||
|
getData("list_dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
function nameJumpClick(row){
|
||||||
|
if(row.type == 'dir' || row.type == 'pan'){
|
||||||
|
jumpClickPath(row.path)
|
||||||
|
}else if(row.type == 'file'){
|
||||||
|
//处理图片
|
||||||
|
if(isImage(row.name)){
|
||||||
|
sysFileDownload({filename:row.path}).then(res=>{
|
||||||
|
if(res.headers['content-type'] == 'application/json'){
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsText(res.data) // 读取文件, 用字符串显示
|
||||||
|
reader.onload = () => {
|
||||||
|
const jsonData = JSON.parse(reader.result);
|
||||||
|
ElMessage.warning(jsonData.msg)
|
||||||
|
};
|
||||||
|
}else{
|
||||||
|
const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
|
||||||
|
imageViewerUrlList.value = [downloadUrl]
|
||||||
|
showImageViewer.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//处理媒体文件预览打开
|
||||||
|
else if(isVideo(row.name)){
|
||||||
|
isVideoDialogShow.value = true
|
||||||
|
nextTick(()=>{
|
||||||
|
moduleLyVideoFlag.value.handleOpen(row.path,'视频播放')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//处理文件编辑
|
||||||
|
else{
|
||||||
|
if(canEditOnline(row.name)){
|
||||||
|
|
||||||
|
}else{
|
||||||
|
ElMessage.warning("该文件不支持在线编辑")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileExt(filename){
|
||||||
|
const fileExtension = filename.split('.').pop(); // 获取文件扩展名
|
||||||
|
const ext = fileExtension == filename?"":fileExtension
|
||||||
|
return ext.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImage(filename){
|
||||||
|
let ext = getFileExt(filename)
|
||||||
|
if(ext === 'png' || ext === 'jpg' || ext === 'jpeg' || ext === 'gif' || ext === 'webp'){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVideo(filename){
|
||||||
|
let ext = getFileExt(filename)
|
||||||
|
if(ext === 'mp4' || ext === 'flv' || ext === 'm4a' || ext === 'avi'){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMediaFile(row){
|
||||||
|
if(row.type == 'file'){
|
||||||
|
if(isImage(row.path)){
|
||||||
|
return true
|
||||||
|
}else if(isVideo(row.path)){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBack(){
|
||||||
|
let tempPaths = deepClone(paths.value)
|
||||||
|
tempPaths.unshift({name:"",url:""})
|
||||||
|
let path = ""
|
||||||
|
let tempLength = tempPaths.length - 2>=0 ?tempPaths.length - 2 :0
|
||||||
|
path = tempPaths[tempLength].url;
|
||||||
|
jumpClickPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearch(){
|
||||||
|
formInline.value.page = 1
|
||||||
|
formInline.value.limit = 100
|
||||||
|
getData("list_dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
function cellselectionclass({ row, column, rowIndex, columnIndex }) {
|
||||||
|
if (columnIndex === 0) {
|
||||||
|
return "chooseAllbtn"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitData(){
|
||||||
|
if(mutipleSelect.value.length>0){
|
||||||
|
//选择了目录
|
||||||
|
emits('change',mutipleSelect.value[0].path)
|
||||||
|
handleClose()
|
||||||
|
}else{
|
||||||
|
if(isWindows && formInline.value.path === ""){
|
||||||
|
ElMessage.warning("请选择具体目录或磁盘")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emits('change',formInline.value.path)
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.lyfileselect:deep(.el-dialog__body){
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.handle-header{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.handle-path-filter{
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
.handle-path-filter-left{
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
.handle-path-filter-right{
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
background-color: rgb(var(--el-color-white) / 1);
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-items: center;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.handle-path-filter-right-inner{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.handle-path-filter-right-2{
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.el-input:deep(.el-input-group__append){
|
||||||
|
color: var(--el-color-white);
|
||||||
|
background-color: var(--el-color-primary) !important;
|
||||||
|
box-shadow:unset;
|
||||||
|
}
|
||||||
|
.el-input:deep(.el-input-group__append):hover{
|
||||||
|
background-color: var(--el-color-primary-light-1) !important;
|
||||||
|
}
|
||||||
|
.search-sites-input{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.handel-button-groups{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.flex-center{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.right-arrow{
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-right: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.ruyi-fileicons{
|
||||||
|
width: 21px;
|
||||||
|
height: 21px;
|
||||||
|
}
|
||||||
|
:deep(.chooseAllbtn) .cell {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<el-icon v-if="isEleIcon" :style="style">
|
<el-icon v-if="isEleIcon" :style="style" class="lyadminfixlag">
|
||||||
<component
|
<component
|
||||||
v-if="iconName"
|
v-if="iconName"
|
||||||
:is="iconName"
|
:is="iconName"
|
||||||
@ -73,6 +73,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.lyadminfixlag {
|
||||||
|
/*transform: translateZ(0);
|
||||||
|
will-change: transform;*/
|
||||||
|
font-size: 1.3em !important;
|
||||||
|
}
|
||||||
.svg-icon-lyicon{
|
.svg-icon-lyicon{
|
||||||
height: 1em;
|
height: 1em;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
|
|||||||
@ -9,12 +9,9 @@
|
|||||||
@tab-remove="removeTab"
|
@tab-remove="removeTab"
|
||||||
@tab-click="tabClick($event)"
|
@tab-click="tabClick($event)"
|
||||||
@contextmenu.prevent.native="openContextMenu($event)">
|
@contextmenu.prevent.native="openContextMenu($event)">
|
||||||
<el-tab-pane
|
<template v-for="(item,index) in editableTabs" :key="index">
|
||||||
:key="item.name"
|
<el-tab-pane :label="item.title" :name="item.name" v-if="index<100"></el-tab-pane>
|
||||||
v-for="item in editableTabs"
|
</template>
|
||||||
:label="item.title"
|
|
||||||
:name="item.name">
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<transition name="el-zoom-in-top">
|
<transition name="el-zoom-in-top">
|
||||||
<ul v-show="contextMenuVisible" :style="{left:left+'px',top:top+'px'}" class="contextmenu" id="lycontextmenu">
|
<ul v-show="contextMenuVisible" :style="{left:left+'px',top:top+'px'}" class="contextmenu" id="lycontextmenu">
|
||||||
@ -246,6 +243,7 @@
|
|||||||
relogin()//重新登录
|
relogin()//重新登录
|
||||||
}
|
}
|
||||||
mutitabsstore.tabsPage = JSON.parse(lytabsPage);
|
mutitabsstore.tabsPage = JSON.parse(lytabsPage);
|
||||||
|
// mutitabsstore.tabsPage = Object.freeze(JSON.parse(lytabsPage))
|
||||||
const currentRouteName = route.name
|
const currentRouteName = route.name
|
||||||
const currentRouteQuery = route.query
|
const currentRouteQuery = route.query
|
||||||
if(currentRouteName == 'login' || currentRouteName == 'root'){
|
if(currentRouteName == 'login' || currentRouteName == 'root'){
|
||||||
|
|||||||
@ -213,7 +213,6 @@
|
|||||||
window.removeEventListener("resize", handleResize);
|
window.removeEventListener("resize", handleResize);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.divleft{
|
.divleft{
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
//API DOMAIN
|
//API DOMAIN
|
||||||
const API_DOMAIN = process.env.NODE_ENV === 'development' ? "127.0.0.1:8000" : "47.112.174.207:7070"
|
const API_DOMAIN = process.env.NODE_ENV === 'development' ? "127.0.0.1:8000" : "django-vue-lyadmin-pro.lybbn.cn"
|
||||||
// 接口地址
|
// 接口地址
|
||||||
const API_BASEURL = process.env.NODE_ENV === 'development' ? "http://"+ API_DOMAIN +"/api/" : "http://"+ API_DOMAIN +"/api/"
|
const API_BASEURL = process.env.NODE_ENV === 'development' ? "http://"+ API_DOMAIN +"/api/" : "https://"+ API_DOMAIN +"/api/"
|
||||||
//版本号
|
//版本号
|
||||||
const APP_VER = require('../../package.json').version
|
const APP_VER = require('../../package.json').version
|
||||||
//是否开启代理
|
//是否开启代理
|
||||||
@ -40,6 +40,9 @@ module.exports = {
|
|||||||
//是否开启多标签
|
//是否开启多标签
|
||||||
ISMULTITABS: true,
|
ISMULTITABS: true,
|
||||||
|
|
||||||
|
//Token前缀,注意最后有个空格,如不需要需设置空字符串
|
||||||
|
TOKEN_PREFIX: "JWT ",
|
||||||
|
|
||||||
//语言 简体中文 zh-cn、 英文 en(此功能只是示例)
|
//语言 简体中文 zh-cn、 英文 en(此功能只是示例)
|
||||||
LANG: 'zh-cn',
|
LANG: 'zh-cn',
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import '../assets/css/nprogress.scss'//自定义样式
|
|||||||
NProgress.inc(0.4)
|
NProgress.inc(0.4)
|
||||||
NProgress.configure({ easing: 'ease', speed: 500, showSpinner: true })
|
NProgress.configure({ easing: 'ease', speed: 500, showSpinner: true })
|
||||||
import {setStorage,getStorage} from '@/utils/util'
|
import {setStorage,getStorage} from '@/utils/util'
|
||||||
|
import { cancelRequestState } from "@/store/cancelRequest";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@ -242,7 +243,7 @@ const autoRouters = getAutoRouterList(names)
|
|||||||
function getAutoRouterList(names) {
|
function getAutoRouterList(names) {
|
||||||
const routerList = [];
|
const routerList = [];
|
||||||
names.forEach((name, index) => {
|
names.forEach((name, index) => {
|
||||||
if(name.indexOf("/components/")==-1 && name !='./index.vue' && name !='./login.vue' && name !='./lyterminal.vue'){
|
if(name.indexOf("/components/")==-1 && name !='./index.vue' && name !='./login.vue' && name !='./lyterminal.vue'){
|
||||||
let isSame = false
|
let isSame = false
|
||||||
const componentConfig = requireComponent(name)
|
const componentConfig = requireComponent(name)
|
||||||
const componentName = name.split('/').pop()?.split('.')[0]//根据路径截取name文件名(去除后缀和前面目录)
|
const componentName = name.split('/').pop()?.split('.')[0]//根据路径截取name文件名(去除后缀和前面目录)
|
||||||
@ -348,6 +349,8 @@ router.beforeEach((to, from, next) => {
|
|||||||
const whiteList = ['buttonConfig', 'menuManage', 'lyterminal', 'buttonManage','lyFilePreview']
|
const whiteList = ['buttonConfig', 'menuManage', 'lyterminal', 'buttonManage','lyFilePreview']
|
||||||
// 进度条
|
// 进度条
|
||||||
NProgress.start()
|
NProgress.start()
|
||||||
|
const cancelReques = cancelRequestState();
|
||||||
|
cancelReques.clearAllCancelToken()
|
||||||
let userId = store.userId ? store.userId : ''
|
let userId = store.userId ? store.userId : ''
|
||||||
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
|
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
|
||||||
if (userId) { // 通过vuex state获取当前的token是否存在
|
if (userId) { // 通过vuex state获取当前的token是否存在
|
||||||
|
|||||||
31
frontend/src/store/cancelRequest.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const cancelRequestState = defineStore('cancelRequest', {
|
||||||
|
state:() => {
|
||||||
|
return {
|
||||||
|
cancelTokenList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getters:{
|
||||||
|
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
addCancelToken(val) {
|
||||||
|
if (!this.cancelTokenList) {
|
||||||
|
this.cancelTokenList = []
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
this.cancelTokenList.push(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 取消所有请求
|
||||||
|
clearAllCancelToken() {
|
||||||
|
this.cancelTokenList.forEach(cancel => {
|
||||||
|
if (cancel) {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.cancelTokenList = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
@ -22,6 +22,13 @@ export const useMutitabsStore = defineStore('mutitabs', {
|
|||||||
//控制是否支持多选项卡
|
//控制是否支持多选项卡
|
||||||
isMultiTabs:config.ISMULTITABS,
|
isMultiTabs:config.ISMULTITABS,
|
||||||
isFullscreen:false,//是否全屏
|
isFullscreen:false,//是否全屏
|
||||||
|
isWebSocketOpen: false,
|
||||||
|
// 未读消息
|
||||||
|
unread: 0,
|
||||||
|
currentOs:getStorage('currentOs')||"windows",
|
||||||
|
fileInfo:{
|
||||||
|
currentDir:"",//文件管理当前所在文件夹path路径
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters:{
|
getters:{
|
||||||
@ -69,6 +76,10 @@ export const useMutitabsStore = defineStore('mutitabs', {
|
|||||||
this.refresh = val;
|
this.refresh = val;
|
||||||
setStorage('refresh',val)
|
setStorage('refresh',val)
|
||||||
},
|
},
|
||||||
|
setCurrentOS(val) {
|
||||||
|
this.currentOs = val;
|
||||||
|
setStorage('currentOs',val)
|
||||||
|
},
|
||||||
refreshUserinfo(val){
|
refreshUserinfo(val){
|
||||||
this.roleNames = val.role_names
|
this.roleNames = val.role_names
|
||||||
this.userName = val.name
|
this.userName = val.name
|
||||||
@ -295,5 +306,11 @@ export const useMutitabsStore = defineStore('mutitabs', {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
setWebSocketState(socketState) {
|
||||||
|
this.isWebSocketOpen = socketState
|
||||||
|
},
|
||||||
|
setUnread (number) {
|
||||||
|
this.unread = number
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
42
frontend/src/utils/message.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
let messageDom = null;
|
||||||
|
const messageTypeList = ['success', 'error', 'warning', 'info'];
|
||||||
|
const Message = (options) => {
|
||||||
|
if (messageDom) messageDom.close();
|
||||||
|
messageDom = ElMessage(options);
|
||||||
|
};
|
||||||
|
messageTypeList.forEach((type) => {
|
||||||
|
Message[type] = (options) => {
|
||||||
|
if (typeof options === 'string') options = { message: options };
|
||||||
|
options.type = type;
|
||||||
|
return Message(options);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const MsgOk = (message) => {
|
||||||
|
Message.success({
|
||||||
|
message: message,
|
||||||
|
type: 'success',
|
||||||
|
showClose: true,
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MsgWarn = (message) => {
|
||||||
|
Message.warning({
|
||||||
|
message: message,
|
||||||
|
type: 'warning',
|
||||||
|
showClose: true,
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MsgError = (message) => {
|
||||||
|
Message.error({
|
||||||
|
message: message,
|
||||||
|
type: 'error',
|
||||||
|
showClose: true,
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -42,6 +42,7 @@ Print.prototype = {
|
|||||||
}
|
}
|
||||||
str += "<style>" + (this.options.noPrint ? this.options.noPrint : '.no-print') + "{display:none;}</style>";
|
str += "<style>" + (this.options.noPrint ? this.options.noPrint : '.no-print') + "{display:none;}</style>";
|
||||||
str += "<style>html,body{background-color:#fff;}</style>";
|
str += "<style>html,body{background-color:#fff;}</style>";
|
||||||
|
str += "<style> @media print {.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:#f5f7fa;border-color:#e4e7ed;}.el-radio__input.is-disabled.is-checked .el-radio__inner::after{background-color:#999999} .el-radio{color:#999999}.el-radio__input.is-checked .el-radio__inner, .el-checkbox__input.is-checked .el-checkbox__inner { background-color: #333333; } }</style>";
|
||||||
// str += "<style>body{zoom: 90%;}</style>";//设置默认打印缩放比
|
// str += "<style>body{zoom: 90%;}</style>";//设置默认打印缩放比
|
||||||
str += "<style>html,body,div{height: auto!important;}</style>";//打印多页
|
str += "<style>html,body,div{height: auto!important;}</style>";//打印多页
|
||||||
// str += "<style>@page {size: auto;margin: 0 6mm 10mm 6mm;}</style>";//去除页眉,其中size是纸张尺寸如:A4 size:A4; portrait 或 landscape 为纸张方向, 如A4横向 size:A4 landscape;
|
// str += "<style>@page {size: auto;margin: 0 6mm 10mm 6mm;}</style>";//去除页眉,其中size是纸张尺寸如:A4 size:A4; portrait 或 landscape 为纸张方向, 如A4横向 size:A4 landscape;
|
||||||
|
|||||||
@ -473,6 +473,126 @@ function isEmpty(nstr){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFileExt = function (fileName) {
|
||||||
|
// 从文件名中获取扩展名
|
||||||
|
const lastDotIndex = fileName.lastIndexOf('.');
|
||||||
|
if (lastDotIndex === -1) {
|
||||||
|
// 如果没有找到点,返回空字符串或其他默认值
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
// 使用 substring 方法获取最后一个点之后的部分作为扩展名
|
||||||
|
return fileName.substring(lastDotIndex + 1).toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFileTypeDesc = function (fileName) {
|
||||||
|
const ext = getFileExt(fileName);
|
||||||
|
switch (ext) {
|
||||||
|
case 'py':
|
||||||
|
return [ext,'Python源文件'];
|
||||||
|
case 'php':
|
||||||
|
return [ext,'Php源文件'];
|
||||||
|
case 'java':
|
||||||
|
return [ext,'Java源文件'];
|
||||||
|
case 'go':
|
||||||
|
return [ext,'Go源文件'];
|
||||||
|
case 'js':
|
||||||
|
return [ext,'JavaScript源文件'];
|
||||||
|
case 'ts':
|
||||||
|
return [ext,'TypeScript源文件'];
|
||||||
|
case 'vue':
|
||||||
|
return [ext,'Vue源文件'];
|
||||||
|
case 'json':
|
||||||
|
return [ext,'Json源文件'];
|
||||||
|
case 'css':
|
||||||
|
return [ext,'CSS样式表'];
|
||||||
|
case 'html':
|
||||||
|
case 'htm':
|
||||||
|
return [ext,'HTML文件'];
|
||||||
|
case 'pdf':
|
||||||
|
return [ext,'PDF文件'];
|
||||||
|
case 'jpg':
|
||||||
|
case 'jpeg':
|
||||||
|
case 'png':
|
||||||
|
case 'gif':
|
||||||
|
return [ext,'Image图片'];
|
||||||
|
case 'mp4':
|
||||||
|
case 'flv':
|
||||||
|
case 'm4a':
|
||||||
|
case 'avi':
|
||||||
|
return [ext,'Video视频文件'];
|
||||||
|
case 'sql':
|
||||||
|
return [ext,'SQL脚本文件'];
|
||||||
|
case 'txt':
|
||||||
|
return [ext,'文本格式'];
|
||||||
|
default:
|
||||||
|
return [ext,'未知文件'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const canEditOnline = function (fileName) {
|
||||||
|
let ext = getFileExt(fileName);
|
||||||
|
let black_list = ['msi','psd','dll','sys','gz', 'zip', 'rar','7z', 'bz2', 'exe', 'db','sqlite','sqlite3','.mdb', 'pdf', 'doc', 'xls', 'docx', 'xlsx', 'ppt','pptx','mp4','flv','avi', 'png', 'gif', 'jpg', 'jpeg', 'bmp', 'icon', 'ico', 'pyc','class', 'so', 'pyd']
|
||||||
|
if (black_list.includes(ext)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成指定长度随机字符串
|
||||||
|
const generateRandomString = function (length) {
|
||||||
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * characters.length);
|
||||||
|
result += characters.charAt(randomIndex);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取路径中的文件名,如果为目录则获取目录名
|
||||||
|
const getFileNameFromPath = function(path) {
|
||||||
|
const isWindowsPath = /^[A-Za-z]:\//.test(path);
|
||||||
|
if(isWindowsPath){
|
||||||
|
if(path.length>3){
|
||||||
|
path = path.replace(/\/$/, '');
|
||||||
|
}else{
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(path.length>1){
|
||||||
|
path = path.replace(/\/$/, '');
|
||||||
|
}
|
||||||
|
if(path === "/"){
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 使用 split('/') 将路径按照斜杠分割成数组,并取最后一个元素作为文件名
|
||||||
|
const parts = path.split('/');
|
||||||
|
return parts[parts.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadFileContent = function (content, fileName) {
|
||||||
|
const downloadUrl = window.URL.createObjectURL(new Blob([content]));
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.style.display = 'none';
|
||||||
|
a.href = downloadUrl;
|
||||||
|
a.download = fileName;
|
||||||
|
const event = new MouseEvent('click');
|
||||||
|
a.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
const addCopySuffix = function (name) {
|
||||||
|
const parts = name.split('.');
|
||||||
|
if (parts.length > 1) {
|
||||||
|
// 如果有扩展名,则在扩展名之前加上'-副本'
|
||||||
|
return parts.slice(0, -1).join('.') + '-副本.' + parts[parts.length - 1];
|
||||||
|
} else {
|
||||||
|
// 如果没有扩展名,则在最后加上'-副本'
|
||||||
|
return name + '-副本';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export{
|
export{
|
||||||
timestampToTime,
|
timestampToTime,
|
||||||
dateFormats,
|
dateFormats,
|
||||||
@ -506,5 +626,11 @@ export{
|
|||||||
removeStorage,
|
removeStorage,
|
||||||
getToken,
|
getToken,
|
||||||
getDefaultWorkflowConfig,
|
getDefaultWorkflowConfig,
|
||||||
isEmpty
|
isEmpty,
|
||||||
|
getFileNameFromPath,
|
||||||
|
canEditOnline,
|
||||||
|
getFileTypeDesc,
|
||||||
|
generateRandomString,
|
||||||
|
downloadFileContent,
|
||||||
|
addCopySuffix
|
||||||
}
|
}
|
||||||
111
frontend/src/utils/websocket.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { ElNotification as message } from 'element-plus';
|
||||||
|
import { getToken } from "@/utils/util";
|
||||||
|
import { domain } from '@/api/url';
|
||||||
|
import { useMutitabsStore } from "@/store/mutitabs";
|
||||||
|
|
||||||
|
function getJWTAuthorization() {
|
||||||
|
const token = getToken();
|
||||||
|
const jwt = 'JWTlybbn' + token;
|
||||||
|
return jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lyWebSocket = {
|
||||||
|
websocket:null,
|
||||||
|
socketOpen:false,
|
||||||
|
hearbeatTimer:null,
|
||||||
|
hearbeatInterval:10 * 1000,
|
||||||
|
isReconnect :true,
|
||||||
|
reconnectCount:3,
|
||||||
|
reconnectCurrent:1,
|
||||||
|
reconnectTimer:null,
|
||||||
|
reconnectInterval:5 * 1000,
|
||||||
|
|
||||||
|
initWebSocket(revMessage){
|
||||||
|
if (!('WebSocket' in window)) {
|
||||||
|
message.warning('该浏览器不支持WebSocket');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = getToken();
|
||||||
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wsUrl = (window.location.protocol === 'http:' ? 'ws://' : 'wss://') + `${domain}/ws/msg/`;
|
||||||
|
lyWebSocket.websocket = new WebSocket(wsUrl, ['JWTLYADMIN', getJWTAuthorization()]);
|
||||||
|
|
||||||
|
lyWebSocket.websocket.onmessage = (e) => {
|
||||||
|
if (revMessage) {
|
||||||
|
revMessage(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
lyWebSocket.websocket.onclose = (e) => {
|
||||||
|
lyWebSocket.socketOpen = false;
|
||||||
|
useMutitabsStore().setWebSocketState(lyWebSocket.socketOpen);
|
||||||
|
|
||||||
|
if (lyWebSocket.isReconnect) {
|
||||||
|
lyWebSocket.reconnectTimer = setTimeout(() => {
|
||||||
|
if (lyWebSocket.reconnectCurrent > lyWebSocket.reconnectCount) {
|
||||||
|
clearTimeout(lyWebSocket.reconnectTimer);
|
||||||
|
lyWebSocket.isReconnect = false;
|
||||||
|
lyWebSocket.socketOpen = false;
|
||||||
|
useMutitabsStore().setWebSocketState(lyWebSocket.socketOpen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lyWebSocket.reconnectCurrent++;
|
||||||
|
lyWebSocket.reconnectWebSocket(revMessage); // 传递 revMessage 参数
|
||||||
|
}, lyWebSocket.reconnectInterval);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
lyWebSocket.websocket.onopen = () => {
|
||||||
|
lyWebSocket.socketOpen = true;
|
||||||
|
useMutitabsStore().setWebSocketState(lyWebSocket.socketOpen);
|
||||||
|
lyWebSocket.isReconnect = true;
|
||||||
|
lyWebSocket.startHeartbeat();
|
||||||
|
};
|
||||||
|
|
||||||
|
lyWebSocket.websocket.onerror = (e) => {
|
||||||
|
};
|
||||||
|
},
|
||||||
|
startHeartbeat(){
|
||||||
|
if (lyWebSocket.hearbeatTimer) {
|
||||||
|
clearInterval(lyWebSocket.hearbeatTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
lyWebSocket.hearbeatTimer = setInterval(() => {
|
||||||
|
const data = {
|
||||||
|
time: new Date().getTime()
|
||||||
|
};
|
||||||
|
lyWebSocket.sendWebSocketMessage(data);
|
||||||
|
}, lyWebSocket.hearbeatInterval);
|
||||||
|
},
|
||||||
|
sendWebSocketMessage(data, callback = null){
|
||||||
|
if (lyWebSocket.websocket && lyWebSocket.websocket.readyState === lyWebSocket.websocket.OPEN) {
|
||||||
|
lyWebSocket.websocket.send(JSON.stringify(data));
|
||||||
|
callback && callback();
|
||||||
|
} else {
|
||||||
|
clearInterval(lyWebSocket.hearbeatTimer);
|
||||||
|
lyWebSocket.socketOpen = false;
|
||||||
|
useMutitabsStore().setWebSocketState(lyWebSocket.socketOpen);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closeWebSocket(){
|
||||||
|
lyWebSocket.isReconnect = false;
|
||||||
|
lyWebSocket.websocket && lyWebSocket.websocket.close();
|
||||||
|
lyWebSocket.websocket = null;
|
||||||
|
lyWebSocket.socketOpen = false;
|
||||||
|
useMutitabsStore().setWebSocketState(lyWebSocket.socketOpen);
|
||||||
|
},
|
||||||
|
reconnectWebSocket(){
|
||||||
|
if (lyWebSocket.websocket && !lyWebSocket.isReconnect) {
|
||||||
|
lyWebSocket.closeWebSocket();
|
||||||
|
}
|
||||||
|
lyWebSocket.initWebSocket(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default lyWebSocket
|
||||||
@ -195,6 +195,7 @@
|
|||||||
mutitabsstore.refreshUserinfo(res.data)
|
mutitabsstore.refreshUserinfo(res.data)
|
||||||
mutitabsstore.setUserId(res.data.userId)
|
mutitabsstore.setUserId(res.data.userId)
|
||||||
mutitabsstore.setRefresh(res.data.refresh)
|
mutitabsstore.setRefresh(res.data.refresh)
|
||||||
|
mutitabsstore.setCurrentOS(res.data.current_os)
|
||||||
getMenu()
|
getMenu()
|
||||||
} else {
|
} else {
|
||||||
getCaptchas()
|
getCaptchas()
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="login_bg">
|
<div class="login_bg">
|
||||||
<div class="login_adv" style="background-image: url(static/img/auth_banner.jpg);">
|
<div class="login_adv" style="background-image: url(static/img/auth_banner.jpg);">
|
||||||
<div class="login_adv__title">
|
<div class="login_adv__title">
|
||||||
<h2>django-vue-lyadmin pro-V2test版</h2>
|
<h2>django-vue-lyadmin pro版</h2>
|
||||||
<p>{{ $t('login.describe') }}</p>
|
<p>{{ $t('login.describe') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="login_adv__mask"></div>
|
<div class="login_adv__mask"></div>
|
||||||
|
|||||||
@ -0,0 +1,139 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ly-dialog v-model="dialogVisible" :title="loadingTitle" width="50%" :before-close="handleClose">
|
||||||
|
<el-form :inline="false" :model="formData" :rules="rules" ref="rulesForm" label-position="right" label-width="auto" :disabled="loadingTitle=='详情'">
|
||||||
|
<el-form-item label="公告标题:">
|
||||||
|
{{formData.messageid.msg_title}}
|
||||||
|
</el-form-item>
|
||||||
|
<!-- <el-form-item label="跳转路径:" prop="to_path">
|
||||||
|
<el-input type="text" v-model.trim="formData.to_path"></el-input>
|
||||||
|
</el-form-item> -->
|
||||||
|
<el-form-item label="目标类型:">
|
||||||
|
<el-radio-group v-model="formData.messageid.target_type">
|
||||||
|
<el-radio :value="1" border>平台公告</el-radio>
|
||||||
|
<el-radio :value="2" border>按用户</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<!-- <el-form-item label="发送对象:" prop="target_user" v-if="formData.target_type == 2" class="is-required">
|
||||||
|
<ly-table-select v-model="formData.target_user" :apiObj="getUserList" :table-width="800" multiple clearable collapse-tags collapse-tags-tooltip :props="tableSelectProps" @change="selectChange">
|
||||||
|
<template #header="{form, submit}">
|
||||||
|
<el-form :inline="true" :model="form">
|
||||||
|
<el-form-item>
|
||||||
|
<el-input type="text" v-model="form.username" placeholder="请输入用户名"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="submit">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
<el-table-column prop="id" label="ID" width="100" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="username" label="用户名" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="nickname" label="昵称" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="mobile" label="手机号" width="150"></el-table-column>
|
||||||
|
<el-table-column prop="create_datetime" label="注册时间"></el-table-column>
|
||||||
|
</ly-table-select>
|
||||||
|
</el-form-item> -->
|
||||||
|
<el-form-item label="公告内容:">
|
||||||
|
<div v-html="formData.messageid.msg_content"></div>
|
||||||
|
<!-- <TEditor v-model="formData.messageid.msg_content"></TEditor> -->
|
||||||
|
</el-form-item>
|
||||||
|
<!-- <el-form-item label="是否发布:" prop="status">-->
|
||||||
|
<!-- <el-switch-->
|
||||||
|
<!-- v-model="formData.status"-->
|
||||||
|
<!-- active-color="#13ce66"-->
|
||||||
|
<!-- inactive-color="#ff4949">-->
|
||||||
|
<!-- </el-switch>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
</el-form>
|
||||||
|
<!-- <template #footer>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave">确定</el-button>
|
||||||
|
</template> -->
|
||||||
|
</ly-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TEditor from '@/components/TEditor'
|
||||||
|
import LyDialog from "@/components/dialog/dialog";
|
||||||
|
import {deepClone} from "@/utils/util";
|
||||||
|
import LyTableSelect from "@/components/lyTableSelect";
|
||||||
|
import {readOwnMessage} from '@/api/api'
|
||||||
|
export default {
|
||||||
|
components: {LyDialog, TEditor,LyTableSelect},
|
||||||
|
emits: ['refreshData'],
|
||||||
|
name: "addModuleNotice",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible:false,
|
||||||
|
loadingSave:false,
|
||||||
|
loadingTitle:'',
|
||||||
|
formData:{
|
||||||
|
messageid:{
|
||||||
|
msg_title:'',
|
||||||
|
to_path:'',
|
||||||
|
msg_content:'',
|
||||||
|
target_type:1,
|
||||||
|
target_user:[],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules:{
|
||||||
|
},
|
||||||
|
//指定表格选择器的回显映射
|
||||||
|
tableSelectProps: {
|
||||||
|
label: 'username',
|
||||||
|
value: 'id',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener("focusin", this.onFocusIn,true);
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
window.removeEventListener("focusin", this.onFocusIn);
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
onFocusIn(e){
|
||||||
|
e.stopImmediatePropagation()//阻止当前和后面的一系列事件
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.dialogVisible=false
|
||||||
|
this.loadingSave=false
|
||||||
|
this.formData = {
|
||||||
|
messageid:{
|
||||||
|
msg_title:'',
|
||||||
|
to_path:'',
|
||||||
|
msg_content:'',
|
||||||
|
target_type:1,
|
||||||
|
target_user:[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$emit('refreshData')
|
||||||
|
},
|
||||||
|
addModuleFn(item,flag) {
|
||||||
|
this.loadingTitle=flag
|
||||||
|
this.dialogVisible=true
|
||||||
|
if(item){
|
||||||
|
this.formData=deepClone(item)
|
||||||
|
this.submitData()
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
submitData() {
|
||||||
|
readOwnMessage({id:this.formData.id}).then(res => {
|
||||||
|
if(res.code ==2000) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.set-specs .el-form-item__content{
|
||||||
|
background: #e6e6e6 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
207
frontend/src/views/messageCenter/myMessage.vue
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="{'ly-is-full':isFull}">
|
||||||
|
<div class="tableSelect" ref="tableSelect">
|
||||||
|
<el-form :inline="true" :model="formInline" label-position="left">
|
||||||
|
<el-form-item label="标题:">
|
||||||
|
<el-input size="default" v-model.trim="formInline.search" maxlength="60" clearable placeholder="消息标题" @change="search" style="width:200px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" v-show="hasPermission(this.$route.name,'Search')"><el-button @click="search" type="primary" icon="Search">查询</el-button></el-form-item>
|
||||||
|
<el-form-item label=""><el-button @click="handleEdit('','reset')" icon="Refresh">重置</el-button></el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-table :height="'calc('+(tableHeight)+'px)'" border :data="tableData" ref="tableref" v-loading="loadingPage" style="width: 100%">
|
||||||
|
<el-table-column type="index" width="60" align="center" label="序号">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-text="getIndex(scope.$index)"></span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column min-width="90" prop="messageid.msg_title" label="公告标题"></el-table-column>
|
||||||
|
<!-- <el-table-column min-width="120" prop="to_path" label="跳转路径"></el-table-column> -->
|
||||||
|
<!-- <el-table-column min-width="120" prop="image" label="封面图">-->
|
||||||
|
<!-- <template #default="scope">-->
|
||||||
|
<!-- <el-image :src=scope.row.image :preview-src-list="[scope.row.image]" style="width: 60px;height: 60px"></el-image>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </el-table-column>-->
|
||||||
|
<!-- <el-table-column min-width="180" prop="msg_content" show-overflow-tooltip label="内容">
|
||||||
|
<template #default="scope">
|
||||||
|
<div v-html="customEllipsis(scope.row.msg_content)" class="ellipsis"></div>
|
||||||
|
</template>
|
||||||
|
</el-table-column> -->
|
||||||
|
<!-- <el-table-column min-width="80" prop="sort" label="排序"></el-table-column>-->
|
||||||
|
<el-table-column min-width="100" label="目标类型">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.messageid.target_type == 1">平台公告</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.messageid.target_type == 2" type="warning">按用户</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column min-width="90" label="是否已读">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.is_read">已读</el-tag>
|
||||||
|
<el-tag v-else type="danger">未读</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column min-width="150" prop="create_datetime" label="创建时间"></el-table-column>
|
||||||
|
<el-table-column label="操作" fixed="right" width="180">
|
||||||
|
<template #header>
|
||||||
|
<div style="display: flex;justify-content: space-between;align-items: center;">
|
||||||
|
<div>操作</div>
|
||||||
|
<div @click="setFull">
|
||||||
|
<el-tooltip content="全屏" placement="bottom">
|
||||||
|
<el-icon ><full-screen /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="table-operate-btn" @click="handleEdit(scope.row,'detail')" v-show="hasPermission(this.$route.name,'Retrieve')">详情</span>
|
||||||
|
<span class="table-operate-btn" @click="handleEdit(scope.row,'delete')" v-show="hasPermission(this.$route.name,'Delete')">删除</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<Pagination v-bind:child-msg="pageparm" @callFather="callFather"></Pagination>
|
||||||
|
<add-module ref="addModuleFlag" @refreshData="getData"></add-module>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import addModule from "./components/addModuleNoticeDetail";
|
||||||
|
import Pagination from "@/components/Pagination";
|
||||||
|
import {dateFormats,getTableHeight} from "@/utils/util";
|
||||||
|
import {getOwnMessage,delOwnMessage} from '@/api/api'
|
||||||
|
export default {
|
||||||
|
components:{
|
||||||
|
Pagination,
|
||||||
|
addModule
|
||||||
|
},
|
||||||
|
name:'messagNotice',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isFull:false,
|
||||||
|
tableHeight:500,
|
||||||
|
loadingPage:false,
|
||||||
|
formInline:{
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
pageparm: {
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
statusList:[
|
||||||
|
{id:1,name:'是'},
|
||||||
|
{id:0,name:'否'}
|
||||||
|
],
|
||||||
|
tableData:[]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
// 表格序列号
|
||||||
|
getIndex($index) {
|
||||||
|
// (当前页 - 1) * 当前显示数据条数 + 当前行数据的索引 + 1
|
||||||
|
return (this.pageparm.page-1)*this.pageparm.limit + $index +1
|
||||||
|
},
|
||||||
|
setFull(){
|
||||||
|
this.isFull=!this.isFull
|
||||||
|
window.dispatchEvent(new Event('resize'))
|
||||||
|
},
|
||||||
|
//当渲染的文字超出10字后显示省略号
|
||||||
|
customEllipsis(value) {
|
||||||
|
value = value.replace(/<.*?>/ig,"") //把v-html的格式标签替换掉
|
||||||
|
if(!value) return ""
|
||||||
|
if (value.length > 10) {
|
||||||
|
return value.slice(0, 10) + "..."
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
addModule() {
|
||||||
|
this.$refs.addModuleFlag.addModuleFn(null,'新增')
|
||||||
|
},
|
||||||
|
changeStatus(row) {
|
||||||
|
// console.log(row,'row----')
|
||||||
|
},
|
||||||
|
handleEdit(row,flag) {
|
||||||
|
let vm = this
|
||||||
|
if(flag=='detail') {
|
||||||
|
vm.$refs.addModuleFlag.addModuleFn(row,'详情')
|
||||||
|
}
|
||||||
|
else if(flag=='delete') {
|
||||||
|
vm.$confirm('您确定要删除选中的内容?',{
|
||||||
|
closeOnClickModal:false
|
||||||
|
}).then(res=>{
|
||||||
|
delOwnMessage({id:row.id}).then(res=>{
|
||||||
|
if(res.code == 2000) {
|
||||||
|
vm.$message.success(res.msg)
|
||||||
|
vm.search()
|
||||||
|
} else {
|
||||||
|
vm.$message.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(()=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if(flag=="reset"){
|
||||||
|
this.formInline = {
|
||||||
|
page:1,
|
||||||
|
limit: 10
|
||||||
|
}
|
||||||
|
this.pageparm={
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
this.getData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
callFather(parm) {
|
||||||
|
this.formInline.page = parm.page
|
||||||
|
this.formInline.limit = parm.limit
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
search() {
|
||||||
|
this.formInline.page = 1
|
||||||
|
this.formInline.limit = 10
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
//获取列表
|
||||||
|
async getData(){
|
||||||
|
this.loadingPage = true
|
||||||
|
getOwnMessage(this.formInline).then(res => {
|
||||||
|
this.loadingPage = false
|
||||||
|
if(res.code ==2000) {
|
||||||
|
this.tableData = res.data.data
|
||||||
|
this.pageparm.page = res.data.page;
|
||||||
|
this.pageparm.limit = res.data.limit;
|
||||||
|
this.pageparm.total = res.data.total;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 计算搜索栏的高度
|
||||||
|
listenResize() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.getTheTableHeight()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getTheTableHeight(){
|
||||||
|
let tabSelectHeight = this.$refs.tableSelect?this.$refs.tableSelect.offsetHeight:0
|
||||||
|
tabSelectHeight = this.isFull?tabSelectHeight - 110:tabSelectHeight
|
||||||
|
this.tableHeight = getTableHeight(tabSelectHeight)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// 监听页面宽度变化搜索框的高度
|
||||||
|
window.addEventListener('resize', this.listenResize);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.getTheTableHeight()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
// 页面销毁,去掉监听事件
|
||||||
|
window.removeEventListener("resize", this.listenResize);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="560px" :before-close="handleClose">
|
||||||
|
<el-form :inline="false" :model="formData" :rules="rules" ref="rulesForm" label-position="right" label-width="auto">
|
||||||
|
<el-form-item label="目录名:" prop="dirname">
|
||||||
|
<el-input v-model="formData.dirname" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import {sysFileGetToken,sysFileDownload,sysdownloadFile,sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed'])
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingTitle = ref('创建目录')
|
||||||
|
let formData = ref({
|
||||||
|
dirname:'',
|
||||||
|
})
|
||||||
|
|
||||||
|
let rules = ref({
|
||||||
|
dirname: [
|
||||||
|
{required: true, message: '请输入目录名',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
if(item){
|
||||||
|
let tempdata = deepClone(item)
|
||||||
|
formData.value.path = tempdata.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rulesForm = ref(null)
|
||||||
|
function submitData() {
|
||||||
|
rulesForm.value.validate(obj=>{
|
||||||
|
if(obj) {
|
||||||
|
loadingSave.value=true
|
||||||
|
let param = {
|
||||||
|
action:'create_dir',
|
||||||
|
dirname:formData.value.dirname,
|
||||||
|
path:formData.value.path
|
||||||
|
}
|
||||||
|
sysFileManage(param).then(res=>{
|
||||||
|
loadingSave.value=false
|
||||||
|
if(res.code ==2000) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
handleClose()
|
||||||
|
emits('refreshData')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="560px" :before-close="handleClose">
|
||||||
|
<el-form :inline="false" :model="formData" :rules="rules" ref="rulesForm" label-position="right" label-width="auto">
|
||||||
|
<el-form-item label="文件名:" prop="filename">
|
||||||
|
<el-input v-model="formData.filename" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import {sysFileGetToken,sysFileDownload,sysdownloadFile,sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed'])
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingTitle = ref('创建文件')
|
||||||
|
let formData = ref({
|
||||||
|
filename:'',
|
||||||
|
})
|
||||||
|
|
||||||
|
let rules = ref({
|
||||||
|
filename: [
|
||||||
|
{required: true, message: '请输入文件名',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
if(item){
|
||||||
|
let tempdata = deepClone(item)
|
||||||
|
formData.value.path = tempdata.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rulesForm = ref(null)
|
||||||
|
function submitData() {
|
||||||
|
rulesForm.value.validate(obj=>{
|
||||||
|
if(obj) {
|
||||||
|
loadingSave.value=true
|
||||||
|
let param = {
|
||||||
|
action:'create_file',
|
||||||
|
filename:formData.value.filename,
|
||||||
|
path:formData.value.path
|
||||||
|
}
|
||||||
|
sysFileManage(param).then(res=>{
|
||||||
|
loadingSave.value=false
|
||||||
|
if(res.code ==2000) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
handleClose()
|
||||||
|
emits('refreshData')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -0,0 +1,431 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="680px" :before-close="handleClose" class="lyinnertabs">
|
||||||
|
<el-tabs v-model="activeName" type="card" @tab-click="handleTabClick">
|
||||||
|
<el-tab-pane label="常规" name="attr">
|
||||||
|
<el-form :inline="false" :model="formData" ref="rulesForm" class="journal-detail" label-position="left" label-width="auto" v-loading="loadingPage">
|
||||||
|
<el-form-item label="名称:" prop="name" @click="copyText(formData.name)">
|
||||||
|
{{formData.name}}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="类型:" prop="typedesc">
|
||||||
|
{{formData.typedesc}}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="路径:" prop="path" @click="copyText(formData.path)">
|
||||||
|
{{formData.path}}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="大小:" prop="size">
|
||||||
|
<span v-if="userState.currentOs=='windows'">{{ calc_size(formData.size) + "("+formData.size+"字节)"}}</span>
|
||||||
|
<span v-else-if="userState.currentOs!='windows'&& formData.type=='dir'">{{formData.size}}</span>
|
||||||
|
<span v-else>{{ calc_size(formData.size)}}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="权限:" prop="permissions">
|
||||||
|
{{ formData.permissions }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="用户/UID:" prop="owner">
|
||||||
|
<span>{{ formData.owner }}</span><span> ({{ formData.owner_uid }})</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="组/GID:" prop="group" v-if="userState.currentOs!='windows'">
|
||||||
|
<span>{{ formData.group }}</span><span> ({{ formData.gid }})</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="修改时间:" prop="modified">
|
||||||
|
{{formData.modified}}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="访问时间:" prop="access_at">
|
||||||
|
{{formData.access_at}}
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="权限" name="qx" v-if="userState.currentOs!='windows'">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" header="所有者">
|
||||||
|
<div style="display: flex;flex-direction: column;margin-left: 20px;">
|
||||||
|
<el-checkbox v-for="dm1 in PermUL" v-model="dm1.val" :key="dm1.name" :label="dm1.name" @change="handleULChange($event,dm1.name)"></el-checkbox>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" header="所属组">
|
||||||
|
<div style="display: flex;flex-direction: column;margin-left: 20px;">
|
||||||
|
<el-checkbox v-for="dm2 in PermGL" v-model="dm2.val" :key="dm2.name" :label="dm2.name" :value="dm2.val" @change="handleGLChange($event,dm2.name)"></el-checkbox>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" header="公共">
|
||||||
|
<div style="display: flex;flex-direction: column;margin-left: 20px;">
|
||||||
|
<el-checkbox v-for="dm3 in PermOL" v-model="dm3.val" :key="dm3.name" :label="dm3.name" :value="dm3.val" @change="handleOLChange($event,dm3.name)"></el-checkbox>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div style="margin-top: 20px;display: flex;align-items: center;">
|
||||||
|
<div style="margin-left: 10px;">
|
||||||
|
权限:<el-input type="number" style="width: 90px;" placeholder="请输入权限值" v-model="newPerms" @input="handlePermInputChange"></el-input>
|
||||||
|
</div>
|
||||||
|
<div style="margin-left: 15px;">
|
||||||
|
所有者:<el-select v-model="belongUser" placeholder="请选择所有者" style="width: 150px;">
|
||||||
|
<el-option
|
||||||
|
v-for="item in userList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.name"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div style="margin-left: 15px;">
|
||||||
|
所属组:<el-select v-model="belongGroup" placeholder="请选择所属用户" style="width: 150px;">
|
||||||
|
<el-option
|
||||||
|
v-for="item in userList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.name"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 20px;display: flex;align-items: center;">
|
||||||
|
<el-checkbox v-model="isSubDir" label="应用到子目录" style="margin-left: 10px;"></el-checkbox>
|
||||||
|
<el-button type="primary" style="margin-left: 10px;" @click=submitClick>保存</el-button>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</LyDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import {sysFileGetToken,sysFileDownload,sysdownloadFile,sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone,getFileTypeDesc,formatUnitSize } from "@/utils/util"
|
||||||
|
import useClipboard from "vue-clipboard3";
|
||||||
|
import {useMutitabsStore} from "@/store/mutitabs";
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed'])
|
||||||
|
const userState = useMutitabsStore()
|
||||||
|
|
||||||
|
let activeName = ref("attr")
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingPage = ref(false)
|
||||||
|
let loadingTitle = ref('创建文件')
|
||||||
|
let formData = ref({
|
||||||
|
path:"",
|
||||||
|
permissions:777,
|
||||||
|
})
|
||||||
|
let PermUL = ref(
|
||||||
|
[{name:"读取",value:4,val:false},{name:"写入",value:2,val:false},{name:"执行",value:1,val:false}]
|
||||||
|
)
|
||||||
|
let PermGL = ref(
|
||||||
|
[{name:"读取",value:4,val:false},{name:"写入",value:2,val:false},{name:"执行",value:1,val:false}]
|
||||||
|
)
|
||||||
|
let PermOL = ref(
|
||||||
|
[{name:"读取",value:4,val:false},{name:"写入",value:2,val:false},{name:"执行",value:1,val:false}]
|
||||||
|
)
|
||||||
|
|
||||||
|
let newPerms = ref("777")
|
||||||
|
let isSubDir = ref(true)
|
||||||
|
let userList = ref([
|
||||||
|
{name:"root",value:0},
|
||||||
|
{name:"www",value:1},
|
||||||
|
{name:"redis",value:2},
|
||||||
|
{name:"mysql",value:3},
|
||||||
|
])
|
||||||
|
let belongUser = ref("")
|
||||||
|
let belongGroup = ref("")
|
||||||
|
let isCloseRefresh = ref(false)
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
if(isCloseRefresh.value){
|
||||||
|
emits('refreshData')
|
||||||
|
}
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
formData.path = item.path
|
||||||
|
if(item){
|
||||||
|
getData(item.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function calc_size(size){
|
||||||
|
return formatUnitSize(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { toClipboard } = useClipboard()
|
||||||
|
function copyText(content) {
|
||||||
|
toClipboard(content).then(()=>{
|
||||||
|
ElMessage.success("复制成功")
|
||||||
|
}).catch(()=>{
|
||||||
|
ElMessage.warning("复制失败")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleULChange(e,name){
|
||||||
|
let nums = 0
|
||||||
|
PermUL.value.forEach(item=>{
|
||||||
|
if(item.val){
|
||||||
|
nums = nums + item.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
newPerms.value = `${nums}${newPerms.value[1]}${newPerms.value[2]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleGLChange(e,name){
|
||||||
|
let nums = 0
|
||||||
|
PermGL.value.forEach(item=>{
|
||||||
|
if(item.val){
|
||||||
|
nums = nums + item.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
newPerms.value = `${newPerms.value[0]}${nums}${newPerms.value[2]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOLChange(e,name){
|
||||||
|
let nums = 0
|
||||||
|
PermOL.value.forEach(item=>{
|
||||||
|
if(item.val){
|
||||||
|
nums = nums + item.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
newPerms.value = `${newPerms.value[0]}${newPerms.value[1]}${nums}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePermInputChange(e){
|
||||||
|
calcPermCheckBox(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTabClick(e){
|
||||||
|
if(e.props.name=="attr"){
|
||||||
|
getData(formData.path)
|
||||||
|
}else{
|
||||||
|
newPerms.value = formData.value.permissions
|
||||||
|
belongUser.value = formData.value.owner
|
||||||
|
belongGroup.value = formData.value.group
|
||||||
|
calcPermCheckBox(formData.value.permissions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcPermCheckBox(permissions){
|
||||||
|
PermUL.value.forEach(item=>{
|
||||||
|
if(permissions[0]=="7"){
|
||||||
|
item.val = true
|
||||||
|
}else if(permissions[0]=="6"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 1){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[0]=="5"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[0]=="4"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 1 || item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[0]=="3"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[0]=="2"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4 || item.value == 1){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[0]=="1"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4 || item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
PermGL.value.forEach(item=>{
|
||||||
|
if(permissions[1]=="7"){
|
||||||
|
item.val = true
|
||||||
|
}else if(permissions[1]=="6"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 1){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[1]=="5"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[1]=="4"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 1 || item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[1]=="3"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[1]=="2"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4 || item.value == 1){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[1]=="1"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4 || item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
PermOL.value.forEach(item=>{
|
||||||
|
if(permissions[2]=="7"){
|
||||||
|
item.val = true
|
||||||
|
}else if(permissions[2]=="6"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 1){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[2]=="5"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[2]=="4"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 1 || item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[2]=="3"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[2]=="2"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4 || item.value == 1){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else if(permissions[2]=="1"){
|
||||||
|
item.val = true
|
||||||
|
if(item.value == 4 || item.value == 2){
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
item.val = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData(path){
|
||||||
|
let param = {
|
||||||
|
action:"get_filedir_attribute",
|
||||||
|
path:path
|
||||||
|
}
|
||||||
|
loadingPage.value = true
|
||||||
|
sysFileManage(param).then(res => {
|
||||||
|
loadingPage.value = false;
|
||||||
|
if (res.code == 2000) {
|
||||||
|
formData.value = res.data
|
||||||
|
if(formData.value.type == 'dir'){
|
||||||
|
formData.value.typedesc = "文件夹"
|
||||||
|
}else{
|
||||||
|
formData.value.typedesc = getFileTypeDesc(formData.value.name)[1]
|
||||||
|
}
|
||||||
|
if(formData.value.is_link){
|
||||||
|
formData.value.typedesc = "链接文件"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitClick(){
|
||||||
|
if(newPerms.value.length !=3){
|
||||||
|
ElMessage.warning("权限配置错误,正确格式:777")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(parseInt(newPerms.value)>777){
|
||||||
|
ElMessage.warning("权限配置错误,最大为:777")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(parseInt(newPerms.value)<0){
|
||||||
|
ElMessage.warning("权限配置错误,最小为:000")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let param = {
|
||||||
|
action:"set_file_access",
|
||||||
|
path:formData.value.path,
|
||||||
|
user:belongUser.value,
|
||||||
|
group:belongGroup.value,
|
||||||
|
access:newPerms.value,
|
||||||
|
issub:isSubDir.value,
|
||||||
|
}
|
||||||
|
userState.loadingInfo.isLoading = true
|
||||||
|
userState.loadingInfo.content = "正在设置..."
|
||||||
|
sysFileManage(param).then(res => {
|
||||||
|
userState.loadingInfo.isLoading = false
|
||||||
|
userState.loadingInfo.content = ""
|
||||||
|
if (res.code == 2000) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
isCloseRefresh.value = true
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.journal-detail:deep(.el-form-item) .el-form-item__content{
|
||||||
|
background: var(--el-color-info-light-9);
|
||||||
|
padding-left: 10px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.lyinnertabs:deep(.el-tabs__header){
|
||||||
|
border-bottom: 1px solid var(--el-border-color-light);
|
||||||
|
height: 32px;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
.lyinnertabs:deep(.el-tabs__item.is-active){
|
||||||
|
border-bottom-color: var(--el-bg-color);
|
||||||
|
border-left: 1px solid var(--el-color-primary) !important;
|
||||||
|
border-right: 1px solid var(--el-color-primary);
|
||||||
|
border-top: 1px solid var(--el-color-primary);
|
||||||
|
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
.lyinnertabs:deep(.el-tabs__item){
|
||||||
|
border-bottom: none;
|
||||||
|
border-left: 1px solid var(--el-border-color-light);
|
||||||
|
border-right: 1px solid var(--el-border-color-light);
|
||||||
|
border-top: 1px solid var(--el-border-color-light);
|
||||||
|
margin-right: 2px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
.lyinnertabs:deep(.el-tabs__item:first-child){
|
||||||
|
border-left: 1px solid var(--el-border-color-light);
|
||||||
|
}
|
||||||
|
.lyinnertabs:deep(.el-tabs__nav){
|
||||||
|
border: none;
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="650px" :before-close="handleClose">
|
||||||
|
<el-alert title="以下目标文件名称与源文件冲突,确定后勾选的文件将跳过,未勾选文件将会被覆盖!!!" type="warning" />
|
||||||
|
<el-table ref="lytable" :data="formData.conflict_list" table-layout="auto" max-height="500px" :scrollbar-always-on="true" @selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="30"></el-table-column>
|
||||||
|
<el-table-column prop="name" min-width="200" label="文件名" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="path" min-width="220" label="路径" show-overflow-tooltip></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref,onMounted,nextTick } from 'vue';
|
||||||
|
import {sysFileGetToken,sysFileDownload,sysdownloadFile,sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed'])
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingTitle = ref('')
|
||||||
|
let formData = ref({
|
||||||
|
action:"batch_operate",
|
||||||
|
path:"",
|
||||||
|
spath:[],
|
||||||
|
type:"",
|
||||||
|
confirm:false,
|
||||||
|
skip_list:[],
|
||||||
|
conflict_list:[],
|
||||||
|
})
|
||||||
|
let lytable = ref(null)
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
if(item){
|
||||||
|
let tempdata = deepClone(item)
|
||||||
|
formData.value.conflict_list = tempdata.conflict_list
|
||||||
|
formData.value.spath = tempdata.spath
|
||||||
|
formData.value.path = tempdata.path
|
||||||
|
formData.value.confirm = true
|
||||||
|
formData.value.type = tempdata.type
|
||||||
|
formData.value.action = tempdata.action
|
||||||
|
formData.value.skip_list = tempdata.conflict_list
|
||||||
|
nextTick(()=>{
|
||||||
|
lytable.value.toggleAllSelection()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelectionChange(e){
|
||||||
|
formData.value.skip_list = e
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitData() {
|
||||||
|
let param = {
|
||||||
|
action:"batch_operate",
|
||||||
|
path:formData.value.path,
|
||||||
|
spath:formData.value.spath,
|
||||||
|
type:formData.value.type,
|
||||||
|
confirm:true,
|
||||||
|
skip_list:formData.value.skip_list,
|
||||||
|
}
|
||||||
|
sysFileManage(param).then(res=>{
|
||||||
|
loadingSave.value=false
|
||||||
|
if(res.code ==2000) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
handleClose()
|
||||||
|
emits('refreshData')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="560px" :before-close="handleClose">
|
||||||
|
<el-form :inline="false" :model="formData" :rules="rules" ref="rulesForm" label-position="right" label-width="auto">
|
||||||
|
<el-form-item label="类型:" prop="cover">
|
||||||
|
<el-radio-group v-model="formData.cover" @change="handleCoverChange">
|
||||||
|
<el-radio :value="true" :label="true">覆盖</el-radio>
|
||||||
|
<el-radio :value="false" :label="false">重命名</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称:" prop="name" v-if="formData.cover">
|
||||||
|
<el-input v-model="formData.name" clearable :disabled="true" ></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称:" prop="newName" v-else>
|
||||||
|
<el-input v-model="formData.newName" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import {sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone,addCopySuffix } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed'])
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingTitle = ref('')
|
||||||
|
let formData = ref({
|
||||||
|
sFilePath:"",
|
||||||
|
dPath:"",
|
||||||
|
name:"",
|
||||||
|
newName:"",
|
||||||
|
type:"",
|
||||||
|
cover:false,
|
||||||
|
action:"copy",
|
||||||
|
})
|
||||||
|
|
||||||
|
let rules = ref({
|
||||||
|
name: [
|
||||||
|
{required: true, message: '请输入名称',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
newName: [
|
||||||
|
{required: true, message: '请输入名称',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
if(item){
|
||||||
|
let tempdata = deepClone(item)
|
||||||
|
formData.value.sFilePath = tempdata.sFilePath
|
||||||
|
formData.value.dPath = tempdata.dPath
|
||||||
|
formData.value.name = tempdata.name
|
||||||
|
formData.value.type = tempdata.type
|
||||||
|
formData.value.action = tempdata.action
|
||||||
|
handleCoverChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCoverChange(e){
|
||||||
|
if(formData.value.cover){
|
||||||
|
formData.value.newName = formData.value.name
|
||||||
|
}else{
|
||||||
|
formData.value.newName = addCopySuffix(formData.value.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rulesForm = ref(null)
|
||||||
|
function submitData() {
|
||||||
|
rulesForm.value.validate(obj=>{
|
||||||
|
if(obj) {
|
||||||
|
loadingSave.value=true
|
||||||
|
let action = 'copy_file'
|
||||||
|
if(formData.value.type == 'dir'){
|
||||||
|
action = 'copy_dir'
|
||||||
|
}
|
||||||
|
if(formData.value.action == 'move'){
|
||||||
|
action = 'move_file'
|
||||||
|
}
|
||||||
|
let cover = false
|
||||||
|
let name = formData.value.newName
|
||||||
|
if(formData.value.cover){
|
||||||
|
cover = true
|
||||||
|
name = formData.value.name
|
||||||
|
}
|
||||||
|
let param = {
|
||||||
|
action:action,
|
||||||
|
path:formData.value.dPath,
|
||||||
|
spath:formData.value.sFilePath,
|
||||||
|
name:name,
|
||||||
|
cover:cover
|
||||||
|
}
|
||||||
|
sysFileManage(param).then(res=>{
|
||||||
|
loadingSave.value=false
|
||||||
|
if(res.code ==2000) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
handleClose()
|
||||||
|
emits('refreshData')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="560px" :before-close="handleClose">
|
||||||
|
<el-form :inline="false" :model="formData" :rules="rules" ref="rulesForm" label-position="right" label-width="auto">
|
||||||
|
<el-form-item label="名称:" prop="dname">
|
||||||
|
<el-input v-model="formData.dname" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import {sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed'])
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingTitle = ref('创建文件')
|
||||||
|
let formData = ref({
|
||||||
|
sname:'',
|
||||||
|
dname:''
|
||||||
|
})
|
||||||
|
|
||||||
|
let rules = ref({
|
||||||
|
dname: [
|
||||||
|
{required: true, message: '请输入名称',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
if(item){
|
||||||
|
let tempdata = deepClone(item)
|
||||||
|
formData.value.dname = tempdata.name
|
||||||
|
formData.value.sname = tempdata.name
|
||||||
|
formData.value.path = tempdata.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rulesForm = ref(null)
|
||||||
|
function submitData() {
|
||||||
|
rulesForm.value.validate(obj=>{
|
||||||
|
if(obj) {
|
||||||
|
loadingSave.value=true
|
||||||
|
let param = {
|
||||||
|
action:'rename_file',
|
||||||
|
sname:formData.value.sname,
|
||||||
|
dname:formData.value.dname,
|
||||||
|
path:formData.value.path
|
||||||
|
}
|
||||||
|
sysFileManage(param).then(res=>{
|
||||||
|
loadingSave.value=false
|
||||||
|
if(res.code ==2000) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
handleClose()
|
||||||
|
emits('refreshData')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
111
frontend/src/views/systemManage/files/components/moduleUnzip.vue
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="680px" :before-close="handleClose">
|
||||||
|
<el-form :inline="false" :model="formData" :rules="rules" ref="rulesForm" label-position="top" label-width="auto">
|
||||||
|
<el-form-item label="解压文件:" prop="filename">
|
||||||
|
<el-input v-model="formData.filename" :disabled="true"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="解压到:" prop="path">
|
||||||
|
<el-input v-model="formData.path">
|
||||||
|
<template #prepend>
|
||||||
|
<el-button @click="chooseDir" icon="Folder"></el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave">解压</el-button>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
<lyFileList ref="chooseDirShowFlag" @change="handleChooseChange" v-if="isChooseDirShow" @closed="isChooseDirShow=false"></lyFileList>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref,nextTick } from 'vue';
|
||||||
|
import {sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import lyFileList from "@/components/file/fileList.vue"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed'])
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingTitle = ref('解压文件')
|
||||||
|
let formData = ref({
|
||||||
|
filename:'',
|
||||||
|
path:''
|
||||||
|
})
|
||||||
|
|
||||||
|
let isChooseDirShow = ref(false)
|
||||||
|
let chooseDirShowFlag = ref(null)
|
||||||
|
|
||||||
|
let rules = ref({
|
||||||
|
path: [
|
||||||
|
{required: true, message: '请选择解压到目录',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
filename: [
|
||||||
|
{required: true, message: '解压文件错误',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
if(item){
|
||||||
|
let tempdata = deepClone(item)
|
||||||
|
formData.value.filename = tempdata.filename
|
||||||
|
formData.value.path = tempdata.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseDir(){
|
||||||
|
isChooseDirShow.value = true
|
||||||
|
nextTick(()=>{
|
||||||
|
chooseDirShowFlag.value.handleOpen({path:formData.value.path,isDir:true},"目录选择")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChooseChange(path){
|
||||||
|
let newpath = deepClone(path)
|
||||||
|
formData.value.path = newpath
|
||||||
|
}
|
||||||
|
|
||||||
|
let rulesForm = ref(null)
|
||||||
|
function submitData() {
|
||||||
|
rulesForm.value.validate(obj=>{
|
||||||
|
if(obj) {
|
||||||
|
loadingSave.value=true
|
||||||
|
let param = {
|
||||||
|
action:'batch_operate',
|
||||||
|
type:"unzip",
|
||||||
|
spath:formData.value.filename,
|
||||||
|
path:formData.value.path
|
||||||
|
}
|
||||||
|
sysFileManage(param).then(res=>{
|
||||||
|
loadingSave.value=false
|
||||||
|
if(res.code ==2000) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
handleClose()
|
||||||
|
emits('refreshData')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -0,0 +1,434 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="690px" :before-close="handleClose">
|
||||||
|
<el-alert style="margin-bottom: 10px;" :title="'上传成功'+uploadedNums+'个,耗时'+timeDifference+'秒,平均速度'+avgSpeed" type="success" v-if="uploadOk" @click="handleReset"/>
|
||||||
|
<div class="handle-button" v-if="!uploadOk">
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="uploadClick('file')">上传文件</el-button>
|
||||||
|
<el-button type="primary" @click="uploadClick('dir')">上传目录</el-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-button @click="clearFiles">清空列表</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="el-upload-dragger" @dragover="handleDragover" @drop="handleDrop" @dragleave="handleDragleave">
|
||||||
|
<div class="handle-drag">
|
||||||
|
<div>
|
||||||
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||||
|
<div class="el-upload__text">
|
||||||
|
请拖拽需要上传的文件/目录到此处
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-upload ref="lyUploadRef" :show-file-list="false" multiple v-model:file-list="uploadFileList" action="#" :auto-upload="false" :on-change="handleUploadOnChange" :on-exceed="handleExceed" :on-success="handleSuccess">
|
||||||
|
<template #tip>
|
||||||
|
<el-text>{{ uploadHelperText }}</el-text>
|
||||||
|
<!-- <el-progress :duration="0" v-if="loadingSave" text-inside :stroke-width="20" :percentage="uploadPrecent" /> -->
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
<el-table ref="tableContainer" :data="uploadFileList" table-layout="auto" max-height="300px" :scrollbar-always-on="true">
|
||||||
|
<el-table-column prop="name" min-width="200" label="文件名" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.raw.webkitRelativePath != ''">{{ scope.row.raw.webkitRelativePath }}</span>
|
||||||
|
<span v-else>{{ scope.row.name }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="size" min-width="100" label="大小" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{formatUnitSize(scope.row.size)}}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" min-width="90" label="状态" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-progress :duration="0" v-if="scope.row.status== 'ready'" text-inside :stroke-width="15" :percentage="scope.row.percentage" />
|
||||||
|
<!-- <span v-if="scope.row.status == 'ready'">等待上传</span> -->
|
||||||
|
<span v-else-if="scope.row.status == 'success'" style="color: green;">上传成功</span>
|
||||||
|
<span v-else style="color: red;">{{ scope.row.status }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column width="45" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.status != 'success' && !loadingSave">
|
||||||
|
<el-button type="primary" link @click="removeFile(index)" icon="Close"></el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<template #footer>
|
||||||
|
<div class="uploadfooterbt">
|
||||||
|
<div>共{{uploadFileList.length}}个</div>
|
||||||
|
<div>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">关闭</el-button>
|
||||||
|
<el-button type="primary" @click="submitData" :loading="loadingSave" :disabled="uploadFileList.length < 1" v-if="!uploadOk">确认上传</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { nextTick, ref,onMounted } from 'vue';
|
||||||
|
import {uploadFile} from '@/api/request';
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import { ElMessage, formItemContextKey } from 'element-plus'
|
||||||
|
import { deepClone,formatUnitSize } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed'])
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingTitle = ref('')
|
||||||
|
|
||||||
|
let path = ref("")
|
||||||
|
let uploadFileList = ref([])
|
||||||
|
let uploadHelperText = ref("")
|
||||||
|
let uploadPrecent = ref(0)
|
||||||
|
let lyUploadRef = ref(null)
|
||||||
|
let uploadType = ref("file")
|
||||||
|
let uploadElement = ref(null)
|
||||||
|
let tableContainer = ref(null)
|
||||||
|
let uploadOk = ref(false)
|
||||||
|
let timeDifference = ref(0)
|
||||||
|
let avgSpeed = ref("")
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('refreshData')
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
let tempdata = deepClone(item)
|
||||||
|
path.value = tempdata.path
|
||||||
|
nextTick(()=>{
|
||||||
|
uploadElement.value = document.querySelector('.el-upload__input');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReset(){
|
||||||
|
uploadOk.value = false
|
||||||
|
uploadFileList.value = []
|
||||||
|
timeDifference.value = 0
|
||||||
|
avgSpeed.value = ""
|
||||||
|
clearFiles()
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadClick(type){
|
||||||
|
uploadType.value = type
|
||||||
|
if(type == "file"){
|
||||||
|
uploadElement.value.webkitdirectory = false
|
||||||
|
}else{
|
||||||
|
uploadElement.value.webkitdirectory = true
|
||||||
|
}
|
||||||
|
lyUploadRef.value.$el.querySelector('input').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearFiles(){
|
||||||
|
lyUploadRef.value.clearFiles();
|
||||||
|
};
|
||||||
|
function removeFile(index){
|
||||||
|
uploadFileList.value.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleDragover(event){
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDrop(event){
|
||||||
|
event.preventDefault();
|
||||||
|
const items = event.dataTransfer.items;
|
||||||
|
if (items) {
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const entry = items[i].webkitGetAsEntry();
|
||||||
|
if (entry) {
|
||||||
|
traverseFileTree(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragleave(event){
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverseFileTree(item, path = ''){
|
||||||
|
path = path || ''
|
||||||
|
if (item.isFile) {
|
||||||
|
item.file((file) => {
|
||||||
|
uploadFileList.value.push(convertFileToUploadFile(file, path));
|
||||||
|
});
|
||||||
|
} else if (item.isDirectory) {
|
||||||
|
const dirReader = item.createReader();
|
||||||
|
dirReader.readEntries((entries) => {
|
||||||
|
for (let i = 0; i < entries.length; i++) {
|
||||||
|
traverseFileTree(entries[i], path + item.name + '/');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function convertFileToUploadFile(file, path){
|
||||||
|
const uid = Date.now();
|
||||||
|
const uploadRawFile = new File([file], file.name, {
|
||||||
|
type: file.type,
|
||||||
|
lastModified: file.lastModified,
|
||||||
|
});
|
||||||
|
uploadRawFile.uid = uid;
|
||||||
|
|
||||||
|
let fileName = file.name;
|
||||||
|
if (path != '') {
|
||||||
|
fileName = path + file.name;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: fileName,
|
||||||
|
size: file.size,
|
||||||
|
status: 'ready',
|
||||||
|
uid: uid,
|
||||||
|
raw: uploadRawFile,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleUploadOnChange(_uploadFile,uploadFiles){
|
||||||
|
// 检查是否有重复文件,有的话删除新选择的文件
|
||||||
|
const indexCF = uploadFiles.findIndex(f=>f.name===_uploadFile.name)
|
||||||
|
const lastIndexCF = uploadFiles.findLastIndex(f=>f.name===_uploadFile.name)
|
||||||
|
if(indexCF!=lastIndexCF){
|
||||||
|
if(_uploadFile.raw.webkitRelativePath == uploadFiles[indexCF].raw.webkitRelativePath){
|
||||||
|
ElMessage.error(_uploadFile.name +" 文件已存在")
|
||||||
|
uploadFiles.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_uploadFile.size == 64 || _uploadFile.size == 0) {
|
||||||
|
uploadFileList.value = uploadFiles;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(_uploadFile.raw);
|
||||||
|
reader.onload = async () => {};
|
||||||
|
reader.onerror = () => {
|
||||||
|
uploadFileList.value = uploadFileList.value.filter((file) => file.uid !== _uploadFile.uid);
|
||||||
|
ElMessage.error({
|
||||||
|
message: `【${_uploadFile.name}】文件类型错误或为空文件夹`,
|
||||||
|
type: 'error',
|
||||||
|
showClose: true,
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
uploadFileList.value = uploadFiles;
|
||||||
|
}
|
||||||
|
scrollBottom()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleExceed(files){
|
||||||
|
lyUploadRef.value.clearFiles();
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
lyUploadRef.value.handleStart(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSuccess(res, file){
|
||||||
|
file.status = 'success';
|
||||||
|
}
|
||||||
|
|
||||||
|
let uploadedNums = ref(0)
|
||||||
|
async function scrollTop(topDistance){
|
||||||
|
nextTick(()=>{
|
||||||
|
topDistance = topDistance || 0
|
||||||
|
if(topDistance == 0){
|
||||||
|
tableContainer.value.setScrollTop(0)
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
tableContainer.value.setScrollTop(topDistance)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function scrollToRow(index){
|
||||||
|
// const row = uploadFileList.value[index]
|
||||||
|
// 设置高亮
|
||||||
|
// tableContainer.value.setCurrentRow(row)
|
||||||
|
// 滚动至当前行
|
||||||
|
nextTick(() => {
|
||||||
|
tableContainer.value.setScrollTop(38 * index)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function scrollBottom(){
|
||||||
|
nextTick(()=>{
|
||||||
|
// let tableBodyHeight = tableContainer.value.scrollBarRef.wrapRef.offsetHeight
|
||||||
|
// tableContainer.value.scrollBarRef.setScrollTop(tableBodyHeight)
|
||||||
|
nextTick(() => {
|
||||||
|
tableContainer.value.setScrollTop(38 * uploadFileList.value.length)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPathNoFilename(path){
|
||||||
|
return path ? path.split('/').slice(0, -1).join('/') : path;
|
||||||
|
}
|
||||||
|
|
||||||
|
function averageUploadSpeed(timediff){
|
||||||
|
let totalsize = 0
|
||||||
|
for (let i = 0; i < uploadFileList.value.length; i++) {
|
||||||
|
totalsize = totalsize + uploadFileList.value[i].size
|
||||||
|
}
|
||||||
|
const avgSpeed = totalsize / timediff
|
||||||
|
return formatUnitSize(avgSpeed)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitData() {
|
||||||
|
// await scrollTop(0)
|
||||||
|
await scrollToRow(1)
|
||||||
|
const startTime = new Date()
|
||||||
|
loadingSave.value=true
|
||||||
|
uploadedNums.value = 0
|
||||||
|
const files = uploadFileList.value.slice();
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
const fileSize = file.size;
|
||||||
|
uploadHelperText.value = `【${file.name}】开始上传`
|
||||||
|
if (fileSize <= 1024 * 1024 * 5) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('lyfile', file.raw);
|
||||||
|
if (file.raw.webkitRelativePath != '') {
|
||||||
|
formData.append('path', path.value + '/' + getPathNoFilename(file.raw.webkitRelativePath));
|
||||||
|
} else {
|
||||||
|
formData.append('path', path.value + '/' + getPathNoFilename(file.name));
|
||||||
|
}
|
||||||
|
// uploadPrecent.value = 0;
|
||||||
|
|
||||||
|
await uploadFile({url:"/api/sys/fileManage/upload/",formData:formData}, (progress) => {
|
||||||
|
// uploadPrecent.value = Math.round((progress.loaded / progress.total) * 100);
|
||||||
|
uploadFileList.value[i].percentage = Math.round((progress.loaded / progress.total) * 100);
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
// 文件上传成功处理
|
||||||
|
if(res.code === 2000){
|
||||||
|
uploadedNums.value ++
|
||||||
|
uploadFileList.value[i].status = 'success';
|
||||||
|
}else{
|
||||||
|
uploadedNums.value ++
|
||||||
|
uploadFileList.value[i].status = res.msg;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// 处理错误
|
||||||
|
ElMessage({
|
||||||
|
message: error,
|
||||||
|
grouping: true,
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
uploadedNums.value ++
|
||||||
|
uploadFileList.value[i].status = error;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
// const chunkSize = 1024 * 1024 * 5;
|
||||||
|
const chunkSize = 1024 * 1024 * 5;
|
||||||
|
const chunkCount = Math.ceil(fileSize / chunkSize);
|
||||||
|
let uploadedChunkCount = 0;
|
||||||
|
uploadPrecent.value = 0;
|
||||||
|
for (let c = 0; c < chunkCount; c++) {
|
||||||
|
const start = c * chunkSize;
|
||||||
|
const end = Math.min(start + chunkSize, fileSize);
|
||||||
|
const chunk = file.raw.slice(start, end);
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
formData.append('lyfilechunk', file.name);
|
||||||
|
if (file.raw.webkitRelativePath != '') {
|
||||||
|
formData.append('path', path.value + '/' + getPathNoFilename(file.raw.webkitRelativePath));
|
||||||
|
} else {
|
||||||
|
formData.append('path', path.value + '/' + getPathNoFilename(file.name));
|
||||||
|
}
|
||||||
|
formData.append('chunkSize', chunkSize);
|
||||||
|
formData.append('totalSize', fileSize);
|
||||||
|
formData.append('chunk', chunk);
|
||||||
|
formData.append('chunkIndex', c.toString());
|
||||||
|
formData.append('chunkCount', chunkCount.toString());
|
||||||
|
|
||||||
|
let chunkok = true
|
||||||
|
await uploadFile({url:"/api/sys/fileManage/upload/",formData:formData}, (progress) => {
|
||||||
|
// uploadPrecent.value = Math.round(
|
||||||
|
// ((uploadedChunkCount + progress.loaded / progress.total) * 100) / chunkCount,
|
||||||
|
// );
|
||||||
|
uploadFileList.value[i].percentage = Math.round(
|
||||||
|
((uploadedChunkCount + progress.loaded / progress.total) * 100) / chunkCount,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
// 文件上传成功处理
|
||||||
|
if(res.code === 2000){
|
||||||
|
chunkok = true
|
||||||
|
}else{
|
||||||
|
uploadFileList.value[i].status = res.msg;
|
||||||
|
chunkok = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// 处理错误
|
||||||
|
ElMessage({
|
||||||
|
message: error,
|
||||||
|
grouping: true,
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
uploadFileList.value[i].status = error;
|
||||||
|
chunkok = false
|
||||||
|
});
|
||||||
|
if(!chunkok){
|
||||||
|
break
|
||||||
|
}
|
||||||
|
uploadedChunkCount++;
|
||||||
|
if (uploadedChunkCount == chunkCount) {
|
||||||
|
uploadedNums.value++;
|
||||||
|
uploadFileList.value[i].status = 'success';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == files.length - 1) {
|
||||||
|
loadingSave.value = false;
|
||||||
|
uploadHelperText.value = '';
|
||||||
|
uploadOk.value = true
|
||||||
|
if (uploadedNums.value == files.length) {
|
||||||
|
const endTime = new Date();
|
||||||
|
const difftime = (endTime - startTime) / 1000 ; // 转换为秒
|
||||||
|
timeDifference.value = difftime.toFixed(2)
|
||||||
|
avgSpeed.value = averageUploadSpeed(timeDifference.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// starttop= starttop+40
|
||||||
|
// await scrollTop(starttop)
|
||||||
|
await scrollToRow(i+1)
|
||||||
|
}
|
||||||
|
loadingSave.value=false
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.handle-button{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.handle-drag{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 10em;
|
||||||
|
}
|
||||||
|
.uploadfooterbt{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
141
frontend/src/views/systemManage/files/components/moduleZip.vue
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LyDialog v-model="dialogVisible" :title="loadingTitle" width="560px" :before-close="handleClose">
|
||||||
|
<el-form :inline="false" :model="formData" :rules="rules" ref="rulesForm" label-position="top" label-width="auto">
|
||||||
|
<el-form-item label="类型:" prop="zip_type">
|
||||||
|
<el-radio-group v-model="formData.zip_type" @change="handleZipChange">
|
||||||
|
<el-radio value="zip">zip</el-radio>
|
||||||
|
<el-radio value="tar">tar.gz</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称:(注意同名文件会被覆盖)" prop="name">
|
||||||
|
<el-input v-model="formData.name">
|
||||||
|
<template #append>{{ formData.ext }}</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="压缩保存路径:" prop="path">
|
||||||
|
<el-input v-model="formData.path">
|
||||||
|
<template #prepend>
|
||||||
|
<el-button @click="chooseDir" icon="Folder"></el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose" :loading="loadingSave">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitData(false)" :loading="loadingSave">压缩</el-button>
|
||||||
|
<el-button type="primary" @click="submitData(true)" :loading="loadingSave">压缩并下载</el-button>
|
||||||
|
</template>
|
||||||
|
</LyDialog>
|
||||||
|
<lyFileList ref="chooseDirShowFlag" @change="handleChooseChange" v-if="isChooseDirShow" @closed="isChooseDirShow=false"></lyFileList>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref,nextTick } from 'vue';
|
||||||
|
import {sysFileManage} from "@/api/api"
|
||||||
|
import LyDialog from "@/components/dialog/dialog.vue"
|
||||||
|
import lyFileList from "@/components/file/fileList.vue"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { deepClone } from "@/utils/util"
|
||||||
|
|
||||||
|
const emits = defineEmits(['refreshData','closed','download'])
|
||||||
|
|
||||||
|
let dialogVisible = ref(false)
|
||||||
|
let loadingSave = ref(false)
|
||||||
|
let loadingTitle = ref('创建文件')
|
||||||
|
let formData = ref({
|
||||||
|
zip_type:"zip",
|
||||||
|
name:"",
|
||||||
|
ext:".zip",
|
||||||
|
path:"",
|
||||||
|
spath:[],
|
||||||
|
})
|
||||||
|
let isChooseDirShow = ref(false)
|
||||||
|
let chooseDirShowFlag = ref(null)
|
||||||
|
|
||||||
|
let rules = ref({
|
||||||
|
zip_type: [
|
||||||
|
{required: true, message: '请选择压缩格式',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
name: [
|
||||||
|
{required: true, message: '请输入压缩后名称',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
path: [
|
||||||
|
{required: true, message: '请输入压缩后保存路径',trigger: 'blur'}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emits('closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(item,flag) {
|
||||||
|
loadingTitle.value=flag
|
||||||
|
dialogVisible.value=true
|
||||||
|
if(item){
|
||||||
|
let tempdata = deepClone(item)
|
||||||
|
formData.value.path = tempdata.path
|
||||||
|
formData.value.spath = tempdata.selectPath
|
||||||
|
formData.value.name = tempdata.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseDir(){
|
||||||
|
isChooseDirShow.value = true
|
||||||
|
nextTick(()=>{
|
||||||
|
chooseDirShowFlag.value.handleOpen({path:formData.value.path,isDir:true},"目录选择")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleZipChange(e){
|
||||||
|
if(formData.value.zip_type == 'zip'){
|
||||||
|
formData.value.ext = '.zip'
|
||||||
|
}else if(formData.value.zip_type == 'tar'){
|
||||||
|
formData.value.ext = '.tar.gz'
|
||||||
|
}else{
|
||||||
|
formData.value.ext = '.zip'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChooseChange(path){
|
||||||
|
let newpath = deepClone(path)
|
||||||
|
formData.value.path = newpath
|
||||||
|
}
|
||||||
|
|
||||||
|
let rulesForm = ref(null)
|
||||||
|
function submitData(download=false) {
|
||||||
|
rulesForm.value.validate(obj=>{
|
||||||
|
if(obj) {
|
||||||
|
loadingSave.value=true
|
||||||
|
let filename = formData.value.path+"/"+formData.value.name+formData.value.ext
|
||||||
|
let param = {
|
||||||
|
path:filename,
|
||||||
|
spath:formData.value.spath,
|
||||||
|
action:'batch_operate',
|
||||||
|
type:"zip",
|
||||||
|
zip_type:formData.value.zip_type
|
||||||
|
}
|
||||||
|
sysFileManage(param).then(res=>{
|
||||||
|
loadingSave.value=false
|
||||||
|
if(res.code ==2000) {
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
if(download){
|
||||||
|
emits('download',{name:formData.value.name+formData.value.ext,path:filename})
|
||||||
|
}
|
||||||
|
emits('refreshData')
|
||||||
|
handleClose()
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||