2025-03-27 17:47:37 vscode task自动提交
@ -21,7 +21,7 @@
|
||||
4. 设置数据库不区分大小写:修改配置文件my.cnf 的[mysqld]中增加 lower_case_table_names = 1
|
||||
|
||||
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数据库导出的)
|
||||
sql脚本位置:backend/lyadmin_pro.sql
|
||||
|
||||
@ -325,7 +325,7 @@ X_FRAME_OPTIONS = 'SAMEORIGIN'#SAMEORIGIN允许同源iframe嵌套、 DENY不允
|
||||
# "accept-encoding",
|
||||
# "lypage"
|
||||
# )
|
||||
|
||||
CORS_EXPOSE_HEADERS = ['Content-Disposition'] # Content-Disposition 头部添加到 Access-Control-Expose-Headers 中,允许客户端 JavaScript 访问该头部
|
||||
# ================================================= #
|
||||
# ********************* 日志配置 ******************* #
|
||||
# ================================================= #
|
||||
@ -441,7 +441,7 @@ REST_FRAMEWORK = {
|
||||
# 'user': '60/minute' #已登录用户每分钟可以请求60次
|
||||
# },
|
||||
'EXCEPTION_HANDLER': 'utils.exception.CustomExceptionHandler', # 自定义的异常处理
|
||||
#线上部署正式环境,关闭web接口测试页面
|
||||
# #线上部署正式环境,关闭web接口测试页面
|
||||
# 'DEFAULT_RENDERER_CLASSES':(
|
||||
# 'rest_framework.renderers.JSONRenderer',
|
||||
# ),
|
||||
@ -557,6 +557,7 @@ API_MODEL_MAP = {
|
||||
"/api/token/": "登录模块",
|
||||
"/api/super/operate/":"前端API关闭开启",
|
||||
"/api/platformsettings/uploadplatformimg/":"图片上传",
|
||||
"/api/system/fileManage/":"文件管理"
|
||||
}
|
||||
# Default primary key field type
|
||||
# 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.dySystemAccountViews import DouyinSytemDarenCodeCallbackView
|
||||
|
||||
#文件管理
|
||||
|
||||
from mysystem.views.file_manage import RYFileMediaView,RYGetFileDownloadView
|
||||
|
||||
#字典信息数据获取
|
||||
from mysystem.views.dictionary import GetDictionaryInfoView,GetDictionaryAllView
|
||||
#app下载页
|
||||
@ -97,6 +101,10 @@ urlpatterns = [
|
||||
path('api/lyformbuilder/', include('apps.lyFormBuilder.urls')),
|
||||
path('api/lytiktokunion/', include('apps.lyTiktokUnion.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用户接口************************************ #
|
||||
|
||||
@ -13,9 +13,9 @@ system_url = routers.SimpleRouter()
|
||||
system_url.register(r'messagetemplate', MyMessageTemplateViewSet)
|
||||
system_url.register(r'messagenotice', MyMessageViewSet)
|
||||
|
||||
|
||||
|
||||
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
|
||||
@ -12,6 +12,8 @@ from rest_framework.permissions import IsAuthenticated
|
||||
from utils.pagination import CustomPagination
|
||||
from django.db import transaction
|
||||
import datetime
|
||||
from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
|
||||
# ================================================= #
|
||||
# ************** 后台消息中心 view ************** #
|
||||
@ -67,6 +69,20 @@ class MyMessageSerializer(CustomModelSerializer):
|
||||
fields = '__all__'
|
||||
# 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):
|
||||
"""
|
||||
消息公告 -序列化器
|
||||
@ -98,6 +114,9 @@ class MyMessageCreateUpdateSerializer(CustomModelSerializer):
|
||||
targetuser_instance = MyMessageUserSerializer(data=targetuser_data, many=True, request=self.request)
|
||||
targetuser_instance.is_valid(raise_exception=True)
|
||||
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
|
||||
|
||||
class Meta:
|
||||
@ -106,6 +125,28 @@ class MyMessageCreateUpdateSerializer(CustomModelSerializer):
|
||||
fields = '__all__'
|
||||
# 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):
|
||||
"""
|
||||
后台消息公告 接口:
|
||||
@ -116,6 +157,39 @@ class MyMessageViewSet(CustomModelViewSet):
|
||||
update_serializer_class = MyMessageCreateUpdateSerializer
|
||||
filterset_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):
|
||||
"""
|
||||
|
||||
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 . import consumers
|
||||
from apps.lymessages.wsmessage import NotificationConsumer
|
||||
|
||||
websocket_urlpatterns = [
|
||||
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 || [];
|
||||
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"){
|
||||
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>
|
||||
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 "============================================"
|
||||
|
||||
py_path="/usr/local/lyadmin"
|
||||
cpu_core=$(cat /proc/cpuinfo|grep processor|wc -l)
|
||||
|
||||
Return_Error(){
|
||||
echo '=================================================';
|
||||
printf '\033[1;31;40m%b\033[0m\n' "$@";
|
||||
@ -51,7 +53,7 @@ Install_Openssl111() {
|
||||
rm -f openssl-${opensslVersion}.tar.gz
|
||||
cd /tmp/openssl-${opensslVersion}
|
||||
./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}/
|
||||
echo "/usr/local/openssl111/lib" >>/etc/ld.so.conf.d/openssl111.conf
|
||||
ldconfig
|
||||
@ -81,7 +83,7 @@ Install_Python(){
|
||||
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
|
||||
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}"
|
||||
else
|
||||
Show_Text_With_Ellipsis "检测到OpenSSL版本为${openssl_version}正在安装支持Python${py_version}版本的openssl" 0.3
|
||||
@ -138,7 +140,7 @@ else
|
||||
source ${PROJECT_ROOT}/venv/bin/activate
|
||||
|
||||
# 安装 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依赖安装完成"
|
||||
fi
|
||||
@ -87,7 +87,11 @@ if object.{key}:
|
||||
{"id": 21, "name": "菜单配置", "value": "MenuConfig", },
|
||||
{"id": 22, "name": "审核", "value": "Audit", },
|
||||
{"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, "权限表标识")
|
||||
@ -97,55 +101,57 @@ if object.{key}:
|
||||
初始化菜单表
|
||||
"""
|
||||
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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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":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": 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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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, "菜单表")
|
||||
|
||||
@ -154,195 +160,202 @@ if object.{key}:
|
||||
初始化菜单权限表
|
||||
"""
|
||||
self.menu_button_data = [
|
||||
{"id":1,"name":"编辑","value":"Update","api":"/api/platformsettings/lunboimg/{id}/","method":2,"menu":7},
|
||||
{"id":2,"name":"编辑","value":"Update","api":"/api/platformsettings/other/{id}/","method":2,"menu":9},
|
||||
{"id":3,"name":"编辑","value":"Update","api":"/api/system/button/{id}/","method":2,"menu":19},
|
||||
{"id":4,"name":"编辑","value":"Update","api":"/api/system/menu/{id}/","method":2,"menu":15},
|
||||
{"id":5,"name":"编辑","value":"Update","api":"/api/system/dept/{id}/","method":2,"menu":12},
|
||||
{"id":6,"name":"修改密码","value":"Changepassword","api":"/api/system/user/change_password/{id}/","method":2,"menu":21},
|
||||
{"id":7,"name":"编辑","value":"Update","api":"/api/users/users/{id}/","method":2,"menu":2},
|
||||
{"id":8,"name":"编辑","value":"Update","api":"/api/system/user/{id}/","method":2,"menu":1},
|
||||
{"id":9,"name":"编辑","value":"Update","api":"/api/system/menu_button/{id}/","method":2,"menu":16},
|
||||
{"id":10,"name":"编辑","value":"Update","api":"/api/system/role/{id}/","method":2,"menu":17},
|
||||
{"id":11,"name":"编辑","value":"Update","api":"/api/system/user/user_info/","method":2,"menu":21},
|
||||
{"id":12,"name":"编辑","value":"Update","api":"/api/system/operation_log/{id}/","method":2,"menu":14},
|
||||
{"id":13,"name":"编辑","value":"Update","api":"/api/messages/messagenotice/{id}/","method":2,"menu":8},
|
||||
{"id":14,"name":"查询","value":"Search","api":"/api/platformsettings/lunboimg/","method":0,"menu":7},
|
||||
{"id":15,"name":"查询","value":"Search","api":"/api/platformsettings/other/","method":0,"menu":9},
|
||||
{"id":16,"name":"查询","value":"Search","api":"/api/system/role/","method":0,"menu":17},
|
||||
{"id":17,"name":"查询","value":"Search","api":"/api/system/user/","method":0,"menu":1},
|
||||
{"id":18,"name":"查询","value":"Search","api":"/api/system/user/user_info/","method":0,"menu":21},
|
||||
{"id":19,"name":"查询","value":"Search","api":"/api/system/operation_log/","method":0,"menu":14},
|
||||
{"id":20,"name":"查询","value":"Search","api":"/api/system/menu/","method":0,"menu":15},
|
||||
{"id":21,"name":"查询","value":"Search","api":"/api/users/users/","method":0,"menu":2},
|
||||
{"id":22,"name":"查询","value":"Search","api":"/api/system/menu_button/","method":0,"menu":16},
|
||||
{"id":23,"name":"查询","value":"Search","api":"/api/system/dept/","method":0,"menu":12},
|
||||
{"id":24,"name":"查询","value":"Search","api":"/api/system/button/","method":0,"menu":19},
|
||||
{"id":25,"name":"查询","value":"Search","api":"/api/messages/messagenotice/","method":0,"menu":8},
|
||||
{"id":26,"name":"新增","value":"Create","api":"/api/platformsettings/lunboimg/","method":1,"menu":7},
|
||||
{"id":27,"name":"新增","value":"Create","api":"/api/platformsettings/other/","method":1,"menu":9},
|
||||
{"id":28,"name":"新增","value":"Create","api":"/api/system/operation_log/","method":1,"menu":14},
|
||||
{"id":29,"name":"新增","value":"Create","api":"/api/system/dept/","method":1,"menu":12},
|
||||
{"id":30,"name":"新增","value":"Create","api":"/api/system/button/","method":1,"menu":19},
|
||||
{"id":31,"name":"新增","value":"Create","api":"/api/system/role/","method":1,"menu":17},
|
||||
{"id":32,"name":"新增","value":"Create","api":"/api/system/user/","method":1,"menu":1},
|
||||
{"id":33,"name":"新增","value":"Create","api":"/api/users/users/","method":1,"menu":2},
|
||||
{"id":34,"name":"新增","value":"Create","api":"/api/system/menu/","method":1,"menu":15},
|
||||
{"id":35,"name":"新增","value":"Create","api":"/api/system/menu_button/","method":1,"menu":16},
|
||||
{"id":36,"name":"新增","value":"Create","api":"/api/messages/messagenotice/","method":1,"menu":8},
|
||||
{"id":37,"name":"单例","value":"Retrieve","api":"/api/platformsettings/lunboimg/{id}/","method":0,"menu":7},
|
||||
{"id":38,"name":"单例","value":"Retrieve","api":"/api/platformsettings/other/{id}/","method":0,"menu":9},
|
||||
{"id":39,"name":"单例","value":"Retrieve","api":"/api/users/users/{id}/","method":0,"menu":2},
|
||||
{"id":40,"name":"单例","value":"Retrieve","api":"/api/system/button/{id}/","method":0,"menu":19},
|
||||
{"id":41,"name":"单例","value":"Retrieve","api":"/api/system/dept/{id}/","method":0,"menu":12},
|
||||
{"id":42,"name":"单例","value":"Retrieve","api":"/api/system/operation_log/{id}/","method":0,"menu":14},
|
||||
{"id":43,"name":"单例","value":"Retrieve","api":"/api/system/role/{id}/","method":0,"menu":17},
|
||||
{"id":44,"name":"单例","value":"Retrieve","api":"/api/system/user/{id}/","method":0,"menu":1},
|
||||
{"id":45,"name":"单例","value":"Retrieve","api":"/api/system/menu/{id}/","method":0,"menu":15},
|
||||
{"id":46,"name":"单例","value":"Retrieve","api":"/api/system/menu_button/{id}/","method":0,"menu":16},
|
||||
{"id":47,"name":"单例","value":"Retrieve","api":"/api/messages/messagenotice/{id}/","method":0,"menu":8},
|
||||
{"id":48,"name":"单例","value":"Retrieve","api":"/api/system/role_id_to_menu/{id}/","method":0,"menu":18},
|
||||
{"id":49,"name":"删除","value":"Delete","api":"/api/platformsettings/lunboimg/{id}/","method":3,"menu":7},
|
||||
{"id":50,"name":"删除","value":"Delete","api":"/api/platformsettings/other/{id}/","method":3,"menu":9},
|
||||
{"id":51,"name":"删除","value":"Delete","api":"/api/system/user/{id}/","method":3,"menu":1},
|
||||
{"id":52,"name":"删除","value":"Delete","api":"/api/system/role/{id}/","method":3,"menu":17},
|
||||
{"id":53,"name":"删除","value":"Delete","api":"/api/system/menu_button/{id}/","method":3,"menu":16},
|
||||
{"id":54,"name":"删除","value":"Delete","api":"/api/system/button/{id}/","method":3,"menu":19},
|
||||
{"id":55,"name":"删除","value":"Delete","api":"/api/system/menu/{id}/","method":3,"menu":15},
|
||||
{"id":56,"name":"删除","value":"Delete","api":"/api/system/operation_log/{id}/","method":3,"menu":14},
|
||||
{"id":57,"name":"删除","value":"Delete","api":"/api/system/dept/{id}/","method":3,"menu":12},
|
||||
{"id":58,"name":"删除","value":"Delete","api":"/api/users/users/{id}/","method":3,"menu":2},
|
||||
{"id":59,"name":"删除","value":"Delete","api":"/api/messages/messagenotice/{id}/","method":3,"menu":8},
|
||||
{"id":61,"name":"禁用","value":"Disable","api":"/api/users/users/disableuser/{id}/","method":2,"menu":2},
|
||||
{"id":62,"name":"编辑","value":"Update","api":"/api/system/user/{id}/","method":2,"menu":4},
|
||||
{"id":63,"name":"禁用","value":"Disable","api":"/api/users/users/disableuser/{id}/","method":2,"menu":4},
|
||||
{"id":64,"name":"查询","value":"Search","api":"/api/system/user/","method":0,"menu":4},
|
||||
{"id":65,"name":"新增","value":"Create","api":"/api/system/user/","method":1,"menu":4},
|
||||
{"id":68,"name":"查询","value":"Search","api":"","method":0,"menu":23},
|
||||
{"id":70,"name":"编辑","value":"Update","api":"/api/crontab/periodictask/{id}/","method":2,"menu":24},
|
||||
{"id":71,"name":"禁用","value":"Disable","api":"/api/crontab/periodictask/enabled/{id}/","method":2,"menu":24},
|
||||
{"id":72,"name":"查询","value":"Search","api":"/api/crontab/periodictask/","method":0,"menu":24},
|
||||
{"id":76,"name":"查询","value":"Search","api":"/api/monitor/getsysteminfo/","method":0,"menu":26},
|
||||
{"id":78,"name":"查询","value":"Search","api":"/api/terminal/terminal/","method":0,"menu":27},
|
||||
{"id":66,"name":"单例","value":"Retrieve","api":"/api/system/user/{id}/","method":0,"menu":4},
|
||||
{"id":69,"name":"单例","value":"Retrieve","api":"","method":0,"menu":23},
|
||||
{"id":74,"name":"单例","value":"Retrieve","api":"/api/crontab/periodictask/{id}/","method":0,"menu":24},
|
||||
{"id":82,"name":"编辑","value":"Update","api":"/api/terminal/terminal/{id}/","method":2,"menu":27},
|
||||
{"id":83,"name":"编辑","value":"Update","api":"/api/address/area/{id}/","method":2,"menu":28},
|
||||
{"id":89,"name":"编辑","value":"Update","api":"/api/mall/goodsspu/{id}/","method":2,"menu":30},
|
||||
{"id":96,"name":"编辑","value":"Update","api":"/api/mall/goodstype/{id}/","method":2,"menu":31},
|
||||
{"id":100,"name":"编辑","value":"Update","api":"/api/mall/goodsorder/{id}/","method":2,"menu":32},
|
||||
{"id":168,"name":"菜单配置","value":"MenuConfig","api":"/api/lyformbuilder/lyformbuilder/editMenu/","method":1,"menu":43},
|
||||
{"id":153,"name":"获取模型","value":"GetModels","api":"/api/platformsettings/sysconfig/get_models_info_list/","method":0,"menu":36},
|
||||
{"id":154,"name":"获取模型","value":"GetModels","api":"/api/platformsettings/sysconfig/get_models_info_list/","method":0,"menu":34},
|
||||
{"id":117,"name":"编辑","value":"Update","api":"/api/platformsettings/sysconfig/{id}/","method":2,"menu":6},
|
||||
{"id":121,"name":"编辑","value":"Update","api":"/api/autocode/autocode/{id}/","method":2,"menu":36},
|
||||
{"id":129,"name":"编辑","value":"Update","api":"/api/system/dictionary/{id}/","method":2,"menu":37},
|
||||
{"id":130,"name":"编辑","value":"Update","api":"/api/system/appversion/{id}/","method":2,"menu":11},
|
||||
{"id":137,"name":"编辑","value":"Update","api":"/api/autocode/StudentManage/{id}/","method":2,"menu":38},
|
||||
{"id":147,"name":"编辑","value":"Update","api":"","method":2,"menu":39},
|
||||
{"id":152,"name":"编辑","value":"Update","api":"","method":2,"menu":40},
|
||||
{"id":162,"name":"编辑","value":"Update","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":2,"menu":34},
|
||||
{"id":174,"name":"编辑","value":"Update","api":"/api/lyformbuilder/teacherManage/{id}/","method":2,"menu":44},
|
||||
{"id":177,"name":"编辑","value":"Update","api":"/api/system/user/user_info/","method":2,"menu":47},
|
||||
{"id":183,"name":"编辑","value":"Update","api":"/api/mall/freightcfg/{id}/","method":2,"menu":48},
|
||||
{"id":188,"name":"编辑","value":"Update","api":"","method":2,"menu":50},
|
||||
{"id":109,"name":"统计","value":"Statistics","api":"/api/mall/goodsorder/orderstatistics/","method":0,"menu":32},
|
||||
{"id":111,"name":"统计","value":"Statistics","api":"/api/mall/goodsforderinfo/orderstatistics/","method":0,"menu":33},
|
||||
{"id":77,"name":"终端","value":"Terminal","api":"/ws/webssh/","method":5,"menu":27},
|
||||
{"id":176,"name":"立即执行","value":"Execute","api":"/api/crontab/periodictask/exectask/","method":1,"menu":24},
|
||||
{"id":108,"name":"禁用","value":"Disable","api":"/api/mall/goodsspu/islaunched/{id}/","method":2,"menu":30},
|
||||
{"id":84,"name":"查询","value":"Search","api":"/api/address/area/area_root/","method":0,"menu":28},
|
||||
{"id":90,"name":"查询","value":"Search","api":"/api/mall/goodsspu/","method":0,"menu":30},
|
||||
{"id":94,"name":"查询","value":"Search","api":"/api/mall/goodstype/","method":0,"menu":31},
|
||||
{"id":101,"name":"查询","value":"Search","api":"/api/mall/goodsorder/","method":0,"menu":32},
|
||||
{"id":102,"name":"查询","value":"Search","api":"/api/mall/goodsforderinfo/","method":0,"menu":33},
|
||||
{"id":105,"name":"查询","value":"Search","api":"/api/platformsettings/userfeeckback/","method":0,"menu":10},
|
||||
{"id":118,"name":"查询","value":"Search","api":"/api/platformsettings/sysconfig/","method":0,"menu":6},
|
||||
{"id":124,"name":"查询","value":"Search","api":"/api/autocode/autocode/","method":0,"menu":36},
|
||||
{"id":128,"name":"查询","value":"Search","api":"/api/system/dictionary/","method":0,"menu":37},
|
||||
{"id":132,"name":"查询","value":"Search","api":"/api/system/appversion/","method":0,"menu":11},
|
||||
{"id":140,"name":"查询","value":"Search","api":"/api/autocode/StudentManage/","method":0,"menu":38},
|
||||
{"id":143,"name":"查询","value":"Search","api":"","method":0,"menu":39},
|
||||
{"id":151,"name":"查询","value":"Search","api":"","method":0,"menu":40},
|
||||
{"id":157,"name":"查询","value":"Search","api":"/api/system/operation_log/systemlog/","method":0,"menu":41},
|
||||
{"id":159,"name":"查询","value":"Search","api":"/api/platformsettings/other/functionSets/","method":0,"menu":42},
|
||||
{"id":161,"name":"查询","value":"Search","api":"/api/lyformbuilder/lyformbuilder/","method":0,"menu":43},
|
||||
{"id":173,"name":"查询","value":"Search","api":"/api/lyformbuilder/teacherManage/","method":0,"menu":44},
|
||||
{"id":175,"name":"查询","value":"Search","api":"/api/monitor/monitor/getredisinfo/","method":0,"menu":45},
|
||||
{"id":178,"name":"查询","value":"Search","api":"/api/system/user/user_info/","method":0,"menu":47},
|
||||
{"id":184,"name":"查询","value":"Search","api":"/api/mall/freightcfg/","method":0,"menu":48},
|
||||
{"id":189,"name":"查询","value":"Search","api":"","method":0,"menu":50},
|
||||
{"id":112,"name":"日志","value":"Logs","api":"/api/crontab/taskresult/","method":0,"menu":24},
|
||||
{"id":180,"name":"日志","value":"Logs","api":"/api/system/operation_log/getOwnerLogs/","method":0,"menu":47},
|
||||
{"id":73,"name":"新增","value":"Create","api":"/api/crontab/periodictask/","method":1,"menu":24},
|
||||
{"id":79,"name":"新增","value":"Create","api":"/api/terminal/terminal/","method":1,"menu":27},
|
||||
{"id":85,"name":"新增","value":"Create","api":"/api/address/area/","method":1,"menu":28},
|
||||
{"id":88,"name":"新增","value":"Create","api":"/api/mall/goodsspu/","method":1,"menu":30},
|
||||
{"id":95,"name":"新增","value":"Create","api":"/api/mall/goodstype/","method":1,"menu":31},
|
||||
{"id":114,"name":"新增","value":"Create","api":"/api/platformsettings/sysconfig/","method":1,"menu":6},
|
||||
{"id":120,"name":"新增","value":"Create","api":"/api/autocode/autocode/","method":1,"menu":36},
|
||||
{"id":125,"name":"新增","value":"Create","api":"/api/system/dictionary/","method":1,"menu":37},
|
||||
{"id":134,"name":"新增","value":"Create","api":"/api/system/appversion/","method":1,"menu":11},
|
||||
{"id":139,"name":"新增","value":"Create","api":"/api/autocode/StudentManage/","method":1,"menu":38},
|
||||
{"id":144,"name":"新增","value":"Create","api":"","method":1,"menu":39},
|
||||
{"id":149,"name":"新增","value":"Create","api":"","method":1,"menu":40},
|
||||
{"id":163,"name":"新增","value":"Create","api":"/api/lyformbuilder/lyformbuilder/","method":1,"menu":34},
|
||||
{"id":170,"name":"新增","value":"Create","api":"/api/lyformbuilder/teacherManage/","method":1,"menu":44},
|
||||
{"id":181,"name":"新增","value":"Create","api":"/api/mall/freightcfg/","method":1,"menu":48},
|
||||
{"id":186,"name":"新增","value":"Create","api":"","method":1,"menu":50},
|
||||
{"id":136,"name":"挂载同步","value":"MountSync","api":"/api/autocode/autocode/generatemount/","method":1,"menu":36},
|
||||
{"id":156,"name":"导出","value":"Export","api":"/api/mall/goodsspu/export/","method":1,"menu":30},
|
||||
{"id":158,"name":"导出","value":"Export","api":"/api/autocode/StudentManage/export/","method":1,"menu":38},
|
||||
{"id":172,"name":"导出","value":"Export","api":"/api/lyformbuilder/teacherManage/export/","method":1,"menu":44},
|
||||
{"id":142,"name":"同步数据库","value":"Syncdb","api":"/api/autocode/autocode/syncdb/","method":1,"menu":36},
|
||||
{"id":166,"name":"同步数据库","value":"Syncdb","api":"/api/lyformbuilder/lyformbuilder/syncdb/","method":1,"menu":43},
|
||||
{"id":165,"name":"同步挂载","value":"MountSync","api":"/api/lyformbuilder/lyformbuilder/generatemount/","method":1,"menu":43},
|
||||
{"id":110,"name":"发货","value":"Deliver","api":"/api/mall/goodsorder/sendoutgoods/","method":1,"menu":32},
|
||||
{"id":80,"name":"单例","value":"Retrieve","api":"/api/terminal/terminal/{id}/","method":0,"menu":27},
|
||||
{"id":86,"name":"单例","value":"Retrieve","api":"/api/address/area/{id}/","method":0,"menu":28},
|
||||
{"id":91,"name":"单例","value":"Retrieve","api":"/api/mall/goodsspu/{id}/","method":0,"menu":30},
|
||||
{"id":93,"name":"单例","value":"Retrieve","api":"/api/mall/goodstype/{id}/","method":0,"menu":31},
|
||||
{"id":98,"name":"单例","value":"Retrieve","api":"/api/mall/goodsorder/{id}/","method":0,"menu":32},
|
||||
{"id":103,"name":"单例","value":"Retrieve","api":"/api/mall/goodsforderinfo/{id}/","method":0,"menu":33},
|
||||
{"id":106,"name":"单例","value":"Retrieve","api":"/api/platformsettings/userfeeckback/{id}/","method":0,"menu":10},
|
||||
{"id":115,"name":"单例","value":"Retrieve","api":"/api/platformsettings/sysconfig/{id}/","method":0,"menu":6},
|
||||
{"id":122,"name":"单例","value":"Retrieve","api":"/api/autocode/autocode/{id}/","method":0,"menu":36},
|
||||
{"id":126,"name":"单例","value":"Retrieve","api":"/api/system/dictionary/{id}/","method":0,"menu":37},
|
||||
{"id":133,"name":"单例","value":"Retrieve","api":"/api/system/appversion/{id}/","method":0,"menu":11},
|
||||
{"id":141,"name":"单例","value":"Retrieve","api":"/api/autocode/StudentManage/{id}/","method":0,"menu":38},
|
||||
{"id":146,"name":"单例","value":"Retrieve","api":"","method":0,"menu":39},
|
||||
{"id":150,"name":"单例","value":"Retrieve","api":"","method":0,"menu":40},
|
||||
{"id":164,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":0,"menu":43},
|
||||
{"id":167,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":0,"menu":34},
|
||||
{"id":169,"name":"单例","value":"Retrieve","api":"/api/lyformbuilder/teacherManage/{id}/","method":0,"menu":44},
|
||||
{"id":185,"name":"区域查询","value":"AreaSearch","api":"/api/mall/freightc/getAllSelect/","method":0,"menu":48},
|
||||
{"id":67,"name":"删除","value":"Delete","api":"/api/system/user/{id}/","method":3,"menu":4},
|
||||
{"id":75,"name":"删除","value":"Delete","api":"/api/crontab/periodictask/{id}/","method":3,"menu":24},
|
||||
{"id":81,"name":"删除","value":"Delete","api":"/api/terminal/terminal/{id}/","method":3,"menu":27},
|
||||
{"id":87,"name":"删除","value":"Delete","api":"/api/address/area/{id}/","method":3,"menu":28},
|
||||
{"id":92,"name":"删除","value":"Delete","api":"/api/mall/goodsspu/{id}/","method":3,"menu":30},
|
||||
{"id":97,"name":"删除","value":"Delete","api":"/api/mall/goodstype/{id}/","method":3,"menu":31},
|
||||
{"id":99,"name":"删除","value":"Delete","api":"/api/mall/goodsorder/{id}/","method":3,"menu":32},
|
||||
{"id":104,"name":"删除","value":"Delete","api":"/api/mall/goodsforderinfo/{id}/","method":3,"menu":33},
|
||||
{"id":107,"name":"删除","value":"Delete","api":"/api/platformsettings/userfeeckback/{id}/","method":3,"menu":10},
|
||||
{"id":116,"name":"删除","value":"Delete","api":"/api/platformsettings/sysconfig/{id}/","method":3,"menu":6},
|
||||
{"id":123,"name":"删除","value":"Delete","api":"/api/autocode/autocode/{id}/","method":3,"menu":36},
|
||||
{"id":127,"name":"删除","value":"Delete","api":"/api/system/dictionary/{id}/","method":3,"menu":37},
|
||||
{"id":131,"name":"删除","value":"Delete","api":"/api/system/appversion/{id}/","method":3,"menu":11},
|
||||
{"id":138,"name":"删除","value":"Delete","api":"/api/autocode/StudentManage/{id}/","method":3,"menu":38},
|
||||
{"id":145,"name":"删除","value":"Delete","api":"","method":3,"menu":39},
|
||||
{"id":148,"name":"删除","value":"Delete","api":"","method":3,"menu":40},
|
||||
{"id":160,"name":"删除","value":"Delete","api":"/api/lyformbuilder/lyformbuilder/{id}/","method":3,"menu":43},
|
||||
{"id":171,"name":"删除","value":"Delete","api":"/api/lyformbuilder/teacherManage/{id}/","method":3,"menu":44},
|
||||
{"id":182,"name":"删除","value":"Delete","api":"/api/mall/freightcfg/{id}/","method":3,"menu":48},
|
||||
{"id":187,"name":"删除","value":"Delete","api":"","method":3,"menu":50},
|
||||
{"id":179,"name":"修改密码","value":"Changepassword","api":"/api/system/user/change_password/{id}/","method":2,"menu":47},
|
||||
{"id":60,"name":"保存","value":"Save","api":"/api/system/permission/{id}/","method":2,"menu":18},
|
||||
{"id":119,"name":"保存","value":"Save","api":"/api/platformsettings/sysconfig/save_content/{id}/","method":2,"menu":6},
|
||||
{"id":113,"name":"任务列表","value":"Tasklist","api":"/api/crontab/periodictask/tasklist/","method":0,"menu":24},
|
||||
{"id":135,"name":"代码预览","value":"PreCode","api":"/api/autocode/autocode/previewcode/","method":0,"menu":36},
|
||||
{"id":155,"name":"代码预览","value":"PreCode","api":"/api/lyformbuilder/lyformbuilder/previewcodejson/","method":1,"menu":34}
|
||||
{"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_id":9},
|
||||
{"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_id":15},
|
||||
{"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_id":21},
|
||||
{"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_id":1},
|
||||
{"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_id":17},
|
||||
{"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_id":14},
|
||||
{"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_id":7},
|
||||
{"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_id":17},
|
||||
{"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_id":21},
|
||||
{"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_id":15},
|
||||
{"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_id":16},
|
||||
{"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_id":19},
|
||||
{"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_id":7},
|
||||
{"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_id":14},
|
||||
{"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_id":19},
|
||||
{"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_id":1},
|
||||
{"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_id":15},
|
||||
{"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_id":8},
|
||||
{"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_id":9},
|
||||
{"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_id":19},
|
||||
{"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_id":14},
|
||||
{"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_id":1},
|
||||
{"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_id":16},
|
||||
{"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_id":18},
|
||||
{"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_id":9},
|
||||
{"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_id":17},
|
||||
{"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_id":19},
|
||||
{"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_id":14},
|
||||
{"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_id":2},
|
||||
{"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_id":2},
|
||||
{"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_id":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_id":4},
|
||||
{"id":68,"name":"查询","value":"Search","api":"","method":0,"menu_id":23},
|
||||
{"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_id":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_id":26},
|
||||
{"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_id":4},
|
||||
{"id":69,"name":"单例","value":"Retrieve","api":"","method":0,"menu_id":23},
|
||||
{"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_id":27},
|
||||
{"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_id":30},
|
||||
{"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_id":32},
|
||||
{"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_id":36},
|
||||
{"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_id":6},
|
||||
{"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_id":37},
|
||||
{"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_id":38},
|
||||
{"id":147,"name":"编辑","value":"Update","api":"","method":2,"menu_id":39},
|
||||
{"id":152,"name":"编辑","value":"Update","api":"","method":2,"menu_id":40},
|
||||
{"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_id":44},
|
||||
{"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_id":48},
|
||||
{"id":188,"name":"编辑","value":"Update","api":"","method":2,"menu_id":50},
|
||||
{"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_id":33},
|
||||
{"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_id":24},
|
||||
{"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_id":28},
|
||||
{"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_id":31},
|
||||
{"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_id":33},
|
||||
{"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_id":6},
|
||||
{"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_id":37},
|
||||
{"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_id":38},
|
||||
{"id":143,"name":"查询","value":"Search","api":"","method":0,"menu_id":39},
|
||||
{"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_id":41},
|
||||
{"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_id":43},
|
||||
{"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_id":45},
|
||||
{"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_id":48},
|
||||
{"id":189,"name":"查询","value":"Search","api":"","method":0,"menu_id":50},
|
||||
{"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_id":47},
|
||||
{"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_id":27},
|
||||
{"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_id":30},
|
||||
{"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_id":6},
|
||||
{"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_id":37},
|
||||
{"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_id":38},
|
||||
{"id":144,"name":"新增","value":"Create","api":"","method":1,"menu_id":39},
|
||||
{"id":149,"name":"新增","value":"Create","api":"","method":1,"menu_id":40},
|
||||
{"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_id":44},
|
||||
{"id":181,"name":"新增","value":"Create","api":"/api/mall/freightcfg/","method":1,"menu_id":48},
|
||||
{"id":186,"name":"新增","value":"Create","api":"","method":1,"menu_id":50},
|
||||
{"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_id":30},
|
||||
{"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_id":44},
|
||||
{"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_id":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_id":32},
|
||||
{"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_id":28},
|
||||
{"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_id":31},
|
||||
{"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_id":33},
|
||||
{"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_id":6},
|
||||
{"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_id":37},
|
||||
{"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_id":38},
|
||||
{"id":146,"name":"单例","value":"Retrieve","api":"","method":0,"menu_id":39},
|
||||
{"id":150,"name":"单例","value":"Retrieve","api":"","method":0,"menu_id":40},
|
||||
{"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_id":34},
|
||||
{"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_id":48},
|
||||
{"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_id":24},
|
||||
{"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_id":28},
|
||||
{"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_id":31},
|
||||
{"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_id":33},
|
||||
{"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_id":6},
|
||||
{"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_id":37},
|
||||
{"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_id":38},
|
||||
{"id":145,"name":"删除","value":"Delete","api":"","method":3,"menu_id":39},
|
||||
{"id":148,"name":"删除","value":"Delete","api":"","method":3,"menu_id":40},
|
||||
{"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_id":44},
|
||||
{"id":182,"name":"删除","value":"Delete","api":"/api/mall/freightcfg/{id}/","method":3,"menu_id":48},
|
||||
{"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_id":47},
|
||||
{"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_id":6},
|
||||
{"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_id":36},
|
||||
{"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, "菜单权限表")
|
||||
|
||||
@ -359,7 +372,7 @@ if object.{key}:
|
||||
{"id": 2, "name": "普通用户", "key": "public", "sort": 2, "status": 1,
|
||||
"admin": 0, "data_range": 4,
|
||||
"dept": [1],
|
||||
"menu": [21, 46, 47],
|
||||
"menu": [21, 46, 47,59],
|
||||
"permission": []
|
||||
},
|
||||
#自定义
|
||||
|
||||
@ -16,6 +16,7 @@ from mysystem.views.user import UserViewSet
|
||||
from mysystem.views.dictionary import DictionaryViewSet
|
||||
from mysystem.views.appversion import AppVersionViewSet
|
||||
from mysystem.views.sysfiles import FileManageViewSet,FileGroupViewSet
|
||||
from mysystem.views.file_manage import RYFileManageView,RYFileDownloadView,RYFileTokenView,RYFileUploadView
|
||||
|
||||
system_url = routers.SimpleRouter()
|
||||
system_url.register(r'menu', MenuViewSet)
|
||||
@ -43,5 +44,9 @@ urlpatterns = [
|
||||
re_path('operation_log/deletealllogs/',OperationLogViewSet.as_view({'delete':'deletealllogs'})),
|
||||
path('operation_log/systemlog/',OperationLogViewSet.as_view({'get':'system_logs'})),
|
||||
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
|
||||
|
||||
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 config import IS_SINGLE_TOKEN,LOGIN_ERROR_RETRY_TIMES,LOGIN_ERROR_RETRY_TIMEOUT
|
||||
from django.core.cache import cache
|
||||
from utils.common import current_os
|
||||
|
||||
class CaptchaView(APIView):
|
||||
"""
|
||||
@ -131,6 +132,7 @@ class LoginSerializer(TokenObtainPairSerializer):
|
||||
role_list = user.role.values_list('id',"name")
|
||||
role_names_list = [i[1] for i in role_list]
|
||||
role_names = ','.join(role_names_list)
|
||||
data['current_os'] = current_os
|
||||
data['avatar'] = self.user.avatar
|
||||
data['role_names'] = role_names
|
||||
data['name'] = self.user.name
|
||||
|
||||
BIN
backend/qqwry.dat
Normal file
@ -68,4 +68,7 @@ django-celery-results
|
||||
eventlet
|
||||
openpyxl
|
||||
python-barcode
|
||||
qqwry-py3
|
||||
qqwry-py3
|
||||
chardet
|
||||
natsort
|
||||
pywin32 ; sys_platform == 'win32'
|
||||
@ -71,4 +71,7 @@ uvicorn
|
||||
gunicorn
|
||||
gevent
|
||||
uwsgi
|
||||
qqwry-py3
|
||||
qqwry-py3
|
||||
chardet
|
||||
natsort
|
||||
pywin32 ; sys_platform == 'win32'
|
||||
@ -93,3 +93,6 @@ xlwt==1.3.0
|
||||
zope.interface==5.4.0
|
||||
openpyxl
|
||||
python-barcode
|
||||
chardet
|
||||
natsort
|
||||
pywin32 ; sys_platform == 'win32'
|
||||
|
||||
@ -11,7 +11,8 @@
|
||||
# ------------------------------
|
||||
# 公用方法
|
||||
# ------------------------------
|
||||
|
||||
import shutil
|
||||
import platform
|
||||
import os,re
|
||||
import random
|
||||
import time
|
||||
@ -23,14 +24,17 @@ import ast
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import subprocess
|
||||
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):
|
||||
"""
|
||||
@ -415,4 +419,128 @@ def verifyLyStateEncript(data,expire=300):
|
||||
return data,False
|
||||
return jsonData,True
|
||||
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.tokens import UntypedToken
|
||||
from utils.ip_util import IPQQwry
|
||||
from config import IS_DEMO
|
||||
|
||||
IS_ALLOW_FRONTEND = ALLOW_FRONTEND
|
||||
|
||||
@ -248,6 +249,8 @@ def has_permission(user, path):
|
||||
method = methodList.index(method)
|
||||
if not hasattr(user, "role"):
|
||||
return False
|
||||
if method == 5 and api == "/ws/msg/":
|
||||
return True
|
||||
userApiList = user.role.values('permission__api', 'permission__method') # 获取当前用户的角色拥有的所有接口
|
||||
for item in userApiList:
|
||||
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/']
|
||||
if IS_DEMO and not request.path in demo_api_white_list:
|
||||
if not request.method in ['GET', 'OPTIONS']:
|
||||
if IS_DEMO and not api in demo_api_white_list:
|
||||
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('演示模式,不允许操作!')
|
||||
# 对ViewSet下的def方法进行权限判断
|
||||
# 当权限为空时,则可以访问
|
||||
@ -53,8 +59,8 @@ class CustomPermission(BasePermission):
|
||||
if request.user.is_superuser:
|
||||
return True
|
||||
else:
|
||||
api = request.path # 当前请求接口
|
||||
method = request.method # 当前请求方法
|
||||
# api = request.path # 当前请求接口
|
||||
# method = request.method # 当前请求方法
|
||||
methodList = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH']
|
||||
method = methodList.index(method)
|
||||
if not hasattr(request.user, "role"):
|
||||
|
||||
@ -11,7 +11,6 @@ from user_agents import parse
|
||||
|
||||
from mysystem.models import LoginLog
|
||||
|
||||
|
||||
def get_request_user(request):
|
||||
"""
|
||||
获取请求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系统命令工具类封装
|
||||
# ------------------------------
|
||||
import pwd
|
||||
import grp
|
||||
import os, sys, re, time, json
|
||||
import psutil
|
||||
from django.core.cache import cache
|
||||
@ -390,4 +392,36 @@ def RestartServer():
|
||||
try:
|
||||
os.system("sync && init 6 &")
|
||||
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):
|
||||
data = myos.GetFileLastNumsLines(path=path,num=num)
|
||||
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 django.core.cache import cache
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
import win32security
|
||||
|
||||
|
||||
def ReadReg(path, key):
|
||||
@ -71,13 +70,13 @@ def to_size(size):
|
||||
if not size: return '0.00 b'
|
||||
size = float(size)
|
||||
|
||||
d = ('b', 'KB', 'MB', 'GB', 'TB');
|
||||
s = d[0];
|
||||
d = ('b', 'KB', 'MB', 'GB', 'TB')
|
||||
s = d[0]
|
||||
for b in d:
|
||||
if size < 1024: return ("%.2f" % size) + ' ' + b;
|
||||
size = size / 1024;
|
||||
s = b;
|
||||
return ("%.2f" % size) + ' ' + b;
|
||||
if size < 1024: return ("%.2f" % size) + ' ' + b
|
||||
size = size / 1024
|
||||
s = b
|
||||
return ("%.2f" % size) + ' ' + b
|
||||
|
||||
|
||||
def is_64bitos():
|
||||
@ -310,11 +309,11 @@ def GetBootTime():
|
||||
if sys_time: return sys_time
|
||||
import math
|
||||
tStr = time.time() - psutil.boot_time()
|
||||
min = tStr / 60;
|
||||
hours = min / 60;
|
||||
days = math.floor(hours / 24);
|
||||
hours = math.floor(hours - (days * 24));
|
||||
min = math.floor(min - (days * 60 * 24) - (hours * 60));
|
||||
min = tStr / 60
|
||||
hours = min / 60
|
||||
days = math.floor(hours / 24)
|
||||
hours = math.floor(hours - (days * 24))
|
||||
min = math.floor(min - (days * 60 * 24) - (hours * 60))
|
||||
sys_time = "{}天".format(int(days))
|
||||
cache.set(key, sys_time, 1800)
|
||||
return sys_time
|
||||
@ -352,10 +351,10 @@ def GetCpuInfo(interval=1):
|
||||
arrs = ret.strip().split('\n\n')
|
||||
for x in arrs:
|
||||
val = x.strip()
|
||||
if not val: continue;
|
||||
if not val: continue
|
||||
try:
|
||||
val = int(val)
|
||||
cpuW += 1;
|
||||
cpuW += 1
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -365,7 +364,7 @@ def GetCpuInfo(interval=1):
|
||||
if not cpu_name:
|
||||
try:
|
||||
cpu_name = '{} * {}'.format(
|
||||
ReadReg(r'HARDWARE\DESCRIPTION\System\CentralProcessor\0', 'ProcessorNameString').strip(), cpuW);
|
||||
ReadReg(r'HARDWARE\DESCRIPTION\System\CentralProcessor\0', 'ProcessorNameString').strip(), cpuW)
|
||||
except:
|
||||
cpu_name = ''
|
||||
cache.set('lybbn_cpu_cpu_name', cpu_name, 86400)
|
||||
@ -388,7 +387,7 @@ def GetDiskInfo():
|
||||
diskInfo = cache.get(key)
|
||||
if diskInfo: return diskInfo
|
||||
try:
|
||||
diskIo = psutil.disk_partitions();
|
||||
diskIo = psutil.disk_partitions()
|
||||
except:
|
||||
import string
|
||||
diskIo = []
|
||||
@ -453,4 +452,28 @@ def RestartServer():
|
||||
try:
|
||||
os.system("shutdown /r /f /t 0")
|
||||
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",
|
||||
"url": "https://gitee.com/lybbn"
|
||||
},
|
||||
"version": "1.3.6",
|
||||
"version": "1.3.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "npm run dev",
|
||||
@ -16,10 +16,19 @@
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.5.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-json": "^6.0.1",
|
||||
"@codemirror/lang-markdown": "^6.3.2",
|
||||
"@codemirror/lang-php": "^6.0.1",
|
||||
"@codemirror/lang-python": "^6.1.2",
|
||||
"@codemirror/lang-sql": "^6.8.0",
|
||||
"@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",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@tinymce/tinymce-vue": "^5.0.0",
|
||||
@ -32,8 +41,8 @@
|
||||
"core-js": "^3.32.2",
|
||||
"cropper": "^4.1.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"echarts": "^5.4.3",
|
||||
"element-plus": "^2.7.8",
|
||||
"echarts": "5.6.0",
|
||||
"element-plus": "2.9.6",
|
||||
"file-saver": "^2.0.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-base64": "^3.7.5",
|
||||
@ -45,25 +54,25 @@
|
||||
"screenfull": "^6.0.2",
|
||||
"sortablejs": "^1.15.0",
|
||||
"tinymce": "5.10.2",
|
||||
"vue": "^3.3.4",
|
||||
"vue": "3.5.13",
|
||||
"vue-axios": "^3.5.2",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.2.5",
|
||||
"xe-utils": "^3.5.13"
|
||||
"vue-i18n": "^10.0.4",
|
||||
"vue-router": "^4.4.5",
|
||||
"xe-utils": "^3.5.31"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.20",
|
||||
"@babel/eslint-parser": "^7.22.15",
|
||||
"@vue/babel-plugin-jsx": "^1.1.5",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/eslint-parser": "^7.25.9",
|
||||
"@vue/babel-plugin-jsx": "^1.2.5",
|
||||
"@vue/cli-plugin-babel": "~5.0.8",
|
||||
"@vue/cli-plugin-router": "~5.0.8",
|
||||
"@vue/cli-service": "~5.0.8",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"sass": "^1.68.0",
|
||||
"sass-loader": "^13.3.2",
|
||||
"@vue/compiler-sfc": "^3.5.12",
|
||||
"compression-webpack-plugin": "^11.1.0",
|
||||
"sass": "^1.80.6",
|
||||
"sass-loader": "^16.0.3",
|
||||
"svg-sprite-loader": "^6.0.11"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
||||
@ -4,18 +4,56 @@
|
||||
</el-config-provider>
|
||||
</template>
|
||||
<script setup>
|
||||
import {ref, onMounted,watch,computed } from 'vue'
|
||||
import {ref, onMounted,watch,computed,onBeforeUnmount } from 'vue'
|
||||
import {useSiteThemeStore} from "@/store/siteTheme";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const i18n = useI18n();
|
||||
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 colorPrimary = siteThemeStore.colorPrimary
|
||||
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)
|
||||
if (siteThemeStore.siteTheme === 'dark') {
|
||||
document.documentElement.classList.add('dark')
|
||||
@ -31,11 +69,14 @@
|
||||
|
||||
//此内容不能删除
|
||||
console.info(`%cDjango-Vue-Lyadmin 专业版 %cVer${config.APP_VER} %chttps://doc.lybbn.cn/`,
|
||||
"color:#409EFF;font-size: 22px;font-weight:bolder",
|
||||
"color:#999;font-size: 12px",
|
||||
"color:#333"
|
||||
)
|
||||
|
||||
"color:#409EFF;font-size: 22px;font-weight:bolder",
|
||||
"color:#999;font-size: 12px",
|
||||
"color:#333"
|
||||
)
|
||||
onBeforeUnmount(() => {
|
||||
// 关闭连接
|
||||
WebSocket.closeWebSocket();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
#app {
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import axios from 'axios';
|
||||
import {reqExpost,ajaxGet,ajaxPost,ajaxDelete,ajaxPut,ajaxPatch,uploadImg,ajaxGetDetailByID,ajaxDownloadExcel,uploadFilev2} from './request';
|
||||
import {url} from './url';
|
||||
import {reqExpost,ajaxGet,ajaxPost,ajaxDelete,ajaxPut,ajaxPatch,uploadImg,ajaxGetDetailByID,ajaxDownloadExcel,uploadFilev2,getDownloadFile,downloadFile} from './request';
|
||||
|
||||
// 获取登录页的信息
|
||||
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 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 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 sysConfig from "@/config"
|
||||
import {useMutitabsStore} from "@/store/mutitabs";
|
||||
import { cancelRequestState } from "@/store/cancelRequest";
|
||||
|
||||
var request = axios.create({
|
||||
timeout: sysConfig.TIMEOUT,
|
||||
});
|
||||
|
||||
request.interceptors.request.use(config => {
|
||||
const cancelRequest = cancelRequestState()
|
||||
config.cancelToken = new axios.CancelToken(cancel => {
|
||||
cancelRequest.addCancelToken(cancel)
|
||||
})
|
||||
return config
|
||||
})
|
||||
|
||||
// http response 拦截器
|
||||
request.interceptors.response.use(
|
||||
response => {
|
||||
@ -189,6 +198,26 @@ export function ajaxDownloadExcel (opt) {
|
||||
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
|
||||
export function getJWTAuthorization() {
|
||||
var token= getToken()
|
||||
@ -377,4 +406,30 @@ export async function uploadFilev2(param){
|
||||
let data = param.params//formData
|
||||
let config = param.params.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>
|
||||
<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>
|
||||
</template>
|
||||
<script setup>
|
||||
@ -18,15 +18,21 @@
|
||||
return siteThemeStore.pagingLayout
|
||||
})
|
||||
|
||||
const isBackgroud = computed(()=>{
|
||||
return pagingLayout.value == 'backgroud'?true:false
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
childMsg: { type: Object, default: () => {} },
|
||||
pageSizes: { type: Array, default: [10,20,30,40,50,100] },
|
||||
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({
|
||||
@ -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>
|
||||
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>
|
||||
<el-icon v-if="isEleIcon" :style="style">
|
||||
<el-icon v-if="isEleIcon" :style="style" class="lyadminfixlag">
|
||||
<component
|
||||
v-if="iconName"
|
||||
:is="iconName"
|
||||
@ -73,6 +73,11 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.lyadminfixlag {
|
||||
/*transform: translateZ(0);
|
||||
will-change: transform;*/
|
||||
font-size: 1.3em !important;
|
||||
}
|
||||
.svg-icon-lyicon{
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
|
||||
@ -9,12 +9,9 @@
|
||||
@tab-remove="removeTab"
|
||||
@tab-click="tabClick($event)"
|
||||
@contextmenu.prevent.native="openContextMenu($event)">
|
||||
<el-tab-pane
|
||||
:key="item.name"
|
||||
v-for="item in editableTabs"
|
||||
:label="item.title"
|
||||
:name="item.name">
|
||||
</el-tab-pane>
|
||||
<template v-for="(item,index) in editableTabs" :key="index">
|
||||
<el-tab-pane :label="item.title" :name="item.name" v-if="index<100"></el-tab-pane>
|
||||
</template>
|
||||
</el-tabs>
|
||||
<transition name="el-zoom-in-top">
|
||||
<ul v-show="contextMenuVisible" :style="{left:left+'px',top:top+'px'}" class="contextmenu" id="lycontextmenu">
|
||||
@ -246,6 +243,7 @@
|
||||
relogin()//重新登录
|
||||
}
|
||||
mutitabsstore.tabsPage = JSON.parse(lytabsPage);
|
||||
// mutitabsstore.tabsPage = Object.freeze(JSON.parse(lytabsPage))
|
||||
const currentRouteName = route.name
|
||||
const currentRouteQuery = route.query
|
||||
if(currentRouteName == 'login' || currentRouteName == 'root'){
|
||||
|
||||
@ -213,7 +213,6 @@
|
||||
window.removeEventListener("resize", handleResize);
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.divleft{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//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
|
||||
//是否开启代理
|
||||
@ -40,6 +40,9 @@ module.exports = {
|
||||
//是否开启多标签
|
||||
ISMULTITABS: true,
|
||||
|
||||
//Token前缀,注意最后有个空格,如不需要需设置空字符串
|
||||
TOKEN_PREFIX: "JWT ",
|
||||
|
||||
//语言 简体中文 zh-cn、 英文 en(此功能只是示例)
|
||||
LANG: 'zh-cn',
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import '../assets/css/nprogress.scss'//自定义样式
|
||||
NProgress.inc(0.4)
|
||||
NProgress.configure({ easing: 'ease', speed: 500, showSpinner: true })
|
||||
import {setStorage,getStorage} from '@/utils/util'
|
||||
import { cancelRequestState } from "@/store/cancelRequest";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@ -242,7 +243,7 @@ const autoRouters = getAutoRouterList(names)
|
||||
function getAutoRouterList(names) {
|
||||
const routerList = [];
|
||||
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
|
||||
const componentConfig = requireComponent(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']
|
||||
// 进度条
|
||||
NProgress.start()
|
||||
const cancelReques = cancelRequestState();
|
||||
cancelReques.clearAllCancelToken()
|
||||
let userId = store.userId ? store.userId : ''
|
||||
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
|
||||
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,
|
||||
isFullscreen:false,//是否全屏
|
||||
isWebSocketOpen: false,
|
||||
// 未读消息
|
||||
unread: 0,
|
||||
currentOs:getStorage('currentOs')||"windows",
|
||||
fileInfo:{
|
||||
currentDir:"",//文件管理当前所在文件夹path路径
|
||||
},
|
||||
}
|
||||
},
|
||||
getters:{
|
||||
@ -69,6 +76,10 @@ export const useMutitabsStore = defineStore('mutitabs', {
|
||||
this.refresh = val;
|
||||
setStorage('refresh',val)
|
||||
},
|
||||
setCurrentOS(val) {
|
||||
this.currentOs = val;
|
||||
setStorage('currentOs',val)
|
||||
},
|
||||
refreshUserinfo(val){
|
||||
this.roleNames = val.role_names
|
||||
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>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>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;
|
||||
|
||||
@ -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{
|
||||
timestampToTime,
|
||||
dateFormats,
|
||||
@ -506,5 +626,11 @@ export{
|
||||
removeStorage,
|
||||
getToken,
|
||||
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.setUserId(res.data.userId)
|
||||
mutitabsstore.setRefresh(res.data.refresh)
|
||||
mutitabsstore.setCurrentOS(res.data.current_os)
|
||||
getMenu()
|
||||
} else {
|
||||
getCaptchas()
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="login_bg">
|
||||
<div class="login_adv" style="background-image: url(static/img/auth_banner.jpg);">
|
||||
<div class="login_adv__title">
|
||||
<h2>django-vue-lyadmin pro-V2test版</h2>
|
||||
<h2>django-vue-lyadmin pro版</h2>
|
||||
<p>{{ $t('login.describe') }}</p>
|
||||
</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>
|
||||