Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
967e4e76c2 | ||
|
|
f7b93c349e | ||
|
|
72f1d2eae7 | ||
|
|
592bc947a0 | ||
|
|
953fcc3437 | ||
|
|
13aa6dbb99 | ||
|
|
460ae171dd | ||
|
|
0d556bfb2b | ||
|
|
61ae6e8eb3 | ||
|
|
175b151f7f | ||
|
|
a50f73d466 | ||
|
|
eea9d320af | ||
|
|
15c4808bbb | ||
|
|
d2d6ba3460 | ||
|
|
4f0295acb1 | ||
|
|
255c405e59 | ||
|
|
96ad956efd | ||
|
|
b586b46016 | ||
|
|
875146e588 | ||
|
|
1ee709b9eb | ||
|
|
f89a5228cd | ||
|
|
795f621637 | ||
|
|
c5d7a70f46 | ||
|
|
8710b047b1 | ||
|
|
300b6c6bb8 | ||
|
|
296640cb2a | ||
|
|
24861fda42 | ||
|
|
0e3fac37e9 | ||
|
|
93d8d94049 | ||
|
|
9d4f007d48 | ||
|
|
4d17a7d9df | ||
|
|
c126c704a4 | ||
|
|
662c314518 | ||
|
|
1715fcb4d8 | ||
|
|
a304a49107 | ||
|
|
fc1ab98b2b | ||
|
|
92f57f8608 | ||
|
|
5ab2aaa066 | ||
|
|
b5f50bdf30 | ||
|
|
256c6e4ab9 |
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [](https://python.org/) [](https://docs.djangoproject.com/zh-hans/3.2/) [](https://nodejs.org/zh-cn/) [](https://gitee.com/liqianglog/django-vue-admin)
|
||||
|
||||
[preview](https://demo.django-vue-admin.com) | [Official website document](https://www.django-vue-admin.com) | [qq group](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [community](https://bbs.django-vue-admin.com) | [plugins market](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin)
|
||||
[preview](https://demo.dvadmin.com) | [Official website document](https://www.django-vue-admin.com) | [qq group](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [community](https://bbs.django-vue-admin.com) | [plugins market](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin)
|
||||
|
||||
💡 **「About」**
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [](https://python.org/) [](https://docs.djangoproject.com/zh-hans/3.2/) [](https://nodejs.org/zh-cn/) [](https://gitee.com/liqianglog/django-vue-admin)
|
||||
|
||||
[预 览](https://demo.django-vue-admin.com) | [官网文档](https://www.django-vue-admin.com) | [群聊](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [社区](https://bbs.django-vue-admin.com) | [插件市场](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin)
|
||||
[预 览](https://demo.dvadmin.com) | [官网文档](https://www.django-vue-admin.com) | [群聊](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [社区](https://bbs.django-vue-admin.com) | [插件市场](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin)
|
||||
|
||||
|
||||
|
||||
|
||||
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
@@ -97,3 +97,4 @@ db.sqlite3
|
||||
media/
|
||||
__pypackages__/
|
||||
package-lock.json
|
||||
gunicorn.pid
|
||||
|
||||
@@ -261,6 +261,7 @@ LOGGING = {
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_PARSER_CLASSES': (
|
||||
'rest_framework.parsers.JSONParser',
|
||||
'rest_framework.parsers.MultiPartParser',
|
||||
),
|
||||
"DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S", # 日期时间格式配置
|
||||
"DATE_FORMAT": "%Y-%m-%d",
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
"is_link": false,
|
||||
"is_catalog": true,
|
||||
"web_path": "/system",
|
||||
"component": "layout/routerView/parent",
|
||||
"component_name": "menu",
|
||||
"component": "",
|
||||
"component_name": "",
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
@@ -25,7 +25,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 19,
|
||||
"parent": 41,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -72,7 +72,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": false,
|
||||
"parent": 19,
|
||||
"parent": 41,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -113,7 +113,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 19,
|
||||
"parent": 41,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -160,7 +160,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 19,
|
||||
"parent": 41,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -213,7 +213,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 19,
|
||||
"parent": 41,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -284,7 +284,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 19,
|
||||
"parent": 41,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -331,7 +331,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 19,
|
||||
"parent": 41,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -376,8 +376,8 @@
|
||||
"is_link": false,
|
||||
"is_catalog": true,
|
||||
"web_path": "/generalConfig",
|
||||
"component": "layout/routerView/parent",
|
||||
"component_name": "config",
|
||||
"component": "",
|
||||
"component_name": "",
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
@@ -395,7 +395,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 27,
|
||||
"parent": 49,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -442,7 +442,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 27,
|
||||
"parent": 49,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -489,7 +489,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 27,
|
||||
"parent": 49,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -536,7 +536,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 27,
|
||||
"parent": 49,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -575,8 +575,8 @@
|
||||
"is_link": false,
|
||||
"is_catalog": true,
|
||||
"web_path": "/log",
|
||||
"component": "layout/routerView/parent",
|
||||
"component_name": "log",
|
||||
"component": "",
|
||||
"component_name": "",
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
@@ -594,7 +594,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 32,
|
||||
"parent": 54,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
@@ -623,7 +623,7 @@
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 32,
|
||||
"parent": 54,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.contrib.auth.models import AbstractUser, UserManager
|
||||
from django.db import models
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from application import dispatch
|
||||
from dvadmin.utils.models import CoreModel, table_prefix
|
||||
|
||||
@@ -13,6 +13,34 @@ STATUS_CHOICES = (
|
||||
)
|
||||
|
||||
|
||||
class Role(CoreModel):
|
||||
name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
|
||||
key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
|
||||
sort = models.IntegerField(default=1, verbose_name="角色顺序", help_text="角色顺序")
|
||||
status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")
|
||||
admin = models.BooleanField(default=False, verbose_name="是否为admin", help_text="是否为admin")
|
||||
|
||||
class Meta:
|
||||
db_table = table_prefix + "system_role"
|
||||
verbose_name = "角色表"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ("sort",)
|
||||
|
||||
|
||||
class CustomUserManager(UserManager):
|
||||
|
||||
def create_superuser(self, username, email=None, password=None, **extra_fields):
|
||||
user = super(CustomUserManager, self).create_superuser(username, email, password, **extra_fields)
|
||||
user.set_password(password)
|
||||
try:
|
||||
user.role.add(Role.objects.get(name="管理员"))
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
except ObjectDoesNotExist:
|
||||
user.delete()
|
||||
raise ValidationError("角色`管理员`不存在, 创建失败, 请先执行python manage.py init")
|
||||
|
||||
|
||||
class Users(CoreModel, AbstractUser):
|
||||
username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号",
|
||||
help_text="用户账号")
|
||||
@@ -48,6 +76,7 @@ class Users(CoreModel, AbstractUser):
|
||||
blank=True,
|
||||
help_text="关联部门",
|
||||
)
|
||||
objects = CustomUserManager()
|
||||
|
||||
def set_password(self, raw_password):
|
||||
super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest())
|
||||
@@ -76,20 +105,6 @@ class Post(CoreModel):
|
||||
ordering = ("sort",)
|
||||
|
||||
|
||||
class Role(CoreModel):
|
||||
name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
|
||||
key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
|
||||
sort = models.IntegerField(default=1, verbose_name="角色顺序", help_text="角色顺序")
|
||||
status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")
|
||||
admin = models.BooleanField(default=False, verbose_name="是否为admin", help_text="是否为admin")
|
||||
|
||||
class Meta:
|
||||
db_table = table_prefix + "system_role"
|
||||
verbose_name = "角色表"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ("sort",)
|
||||
|
||||
|
||||
class Dept(CoreModel):
|
||||
name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
|
||||
key = models.CharField(max_length=64, unique=True, null=True, blank=True, verbose_name="关联字符",
|
||||
@@ -339,7 +354,7 @@ class OperationLog(CoreModel):
|
||||
def media_file_name(instance, filename):
|
||||
h = instance.md5sum
|
||||
basename, ext = os.path.splitext(filename)
|
||||
return os.path.join("files", h[0:1], h[1:2], h + ext.lower())
|
||||
return os.path.join("files", h[:1], h[1:2], h + ext.lower())
|
||||
|
||||
|
||||
class FileList(CoreModel):
|
||||
|
||||
@@ -123,6 +123,7 @@ class DeptViewSet(CustomModelViewSet):
|
||||
data = serializer.data
|
||||
return SuccessResponse(data=data)
|
||||
|
||||
@action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated], extra_filter_class=[])
|
||||
def dept_lazy_tree(self, request, *args, **kwargs):
|
||||
parent = self.request.query_params.get('parent')
|
||||
is_superuser = request.user.is_superuser
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from dvadmin.system.models import FileList
|
||||
from dvadmin.utils.serializers import CustomModelSerializer
|
||||
from dvadmin.utils.viewset import CustomModelViewSet
|
||||
@@ -9,7 +8,7 @@ class FileSerializer(CustomModelSerializer):
|
||||
url = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
def get_url(self, instance):
|
||||
return 'media/' + str(instance.url)
|
||||
return f'media/{str(instance.url)}'
|
||||
|
||||
class Meta:
|
||||
model = FileList
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import base64
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from captcha.views import CaptchaStore, captcha_image
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth.hashers import make_password, check_password
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_yasg import openapi
|
||||
@@ -14,9 +12,7 @@ from rest_framework import serializers
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
||||
from rest_framework_simplejwt.views import TokenObtainPairView
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from application import dispatch
|
||||
from dvadmin.system.models import Users
|
||||
from dvadmin.utils.json_response import ErrorResponse, DetailResponse
|
||||
@@ -58,6 +54,7 @@ class LoginSerializer(TokenObtainPairSerializer):
|
||||
captcha = serializers.CharField(
|
||||
max_length=6, required=False, allow_null=True, allow_blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Users
|
||||
fields = "__all__"
|
||||
@@ -79,13 +76,18 @@ class LoginSerializer(TokenObtainPairSerializer):
|
||||
raise CustomValidationError("验证码过期")
|
||||
else:
|
||||
if self.image_code and (
|
||||
self.image_code.response == captcha
|
||||
or self.image_code.challenge == captcha
|
||||
self.image_code.response == captcha
|
||||
or self.image_code.challenge == captcha
|
||||
):
|
||||
self.image_code and self.image_code.delete()
|
||||
else:
|
||||
self.image_code and self.image_code.delete()
|
||||
raise CustomValidationError("图片验证码错误")
|
||||
|
||||
user = Users.objects.get(username=attrs['username'])
|
||||
if not user.is_active:
|
||||
raise CustomValidationError("账号被锁定")
|
||||
|
||||
data = super().validate(attrs)
|
||||
data["name"] = self.user.name
|
||||
data["userId"] = self.user.id
|
||||
@@ -107,6 +109,7 @@ class LoginSerializer(TokenObtainPairSerializer):
|
||||
save_login_log(request=request)
|
||||
return {"code": 2000, "msg": "请求成功", "data": data}
|
||||
|
||||
|
||||
class LoginView(TokenObtainPairView):
|
||||
"""
|
||||
登录接口
|
||||
|
||||
@@ -53,9 +53,6 @@ class MenuCreateSerializer(CustomModelSerializer):
|
||||
read_only_fields = ["id"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class WebRouterSerializer(CustomModelSerializer):
|
||||
"""
|
||||
前端菜单路由的简单序列化器
|
||||
@@ -63,11 +60,11 @@ class WebRouterSerializer(CustomModelSerializer):
|
||||
path = serializers.CharField(source="web_path")
|
||||
title = serializers.CharField(source="name")
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Menu
|
||||
fields = ('id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component',
|
||||
'component_name', 'cache', 'visible')
|
||||
fields = (
|
||||
'id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component',
|
||||
'component_name', 'cache', 'visible', 'status')
|
||||
read_only_fields = ["id"]
|
||||
|
||||
|
||||
@@ -86,6 +83,7 @@ class MenuViewSet(CustomModelViewSet):
|
||||
update_serializer_class = MenuCreateSerializer
|
||||
search_fields = ['name', 'status']
|
||||
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']
|
||||
|
||||
# extra_filter_class = []
|
||||
|
||||
@action(methods=['GET'], detail=False, permission_classes=[])
|
||||
@@ -101,12 +99,36 @@ class MenuViewSet(CustomModelViewSet):
|
||||
data = serializer.data
|
||||
return SuccessResponse(data=data, total=len(data), msg="获取成功")
|
||||
|
||||
def list(self,request):
|
||||
@action(methods=['GET'], detail=False, permission_classes=[])
|
||||
def get_all_menu(self, request):
|
||||
"""用于菜单管理获取所有的菜单"""
|
||||
user = request.user
|
||||
queryset = self.queryset.all()
|
||||
if not user.is_superuser:
|
||||
role_list = user.role.values_list('id', flat=True)
|
||||
menu_list = RoleMenuPermission.objects.filter(role__in=role_list).values_list('menu_id')
|
||||
queryset = Menu.objects.filter(id__in=menu_list)
|
||||
serializer = WebRouterSerializer(queryset, many=True, request=request)
|
||||
data = serializer.data
|
||||
return SuccessResponse(data=data, total=len(data), msg="获取成功")
|
||||
|
||||
@action(methods=['POST'], detail=False, permission_classes=[])
|
||||
def drag_menu(self, request):
|
||||
"""前端拖拽菜单之后重写parent"""
|
||||
menu_id = request.data['menu_id']
|
||||
parent_id = request.data['parent_id']
|
||||
menu = Menu.objects.get(id=menu_id)
|
||||
menu.parent_id = parent_id
|
||||
menu.save()
|
||||
|
||||
return SuccessResponse(data=[], msg="拖拽菜单成功")
|
||||
|
||||
def list(self, request):
|
||||
"""懒加载"""
|
||||
request.query_params._mutable = True
|
||||
params = request.query_params
|
||||
parent = params.get('parent', None)
|
||||
page = params.get('page',None)
|
||||
page = params.get('page', None)
|
||||
limit = params.get('limit', None)
|
||||
if page:
|
||||
del params['page']
|
||||
@@ -114,11 +136,11 @@ class MenuViewSet(CustomModelViewSet):
|
||||
del params['limit']
|
||||
if params:
|
||||
if parent:
|
||||
queryset = self.queryset.filter(status=1, parent=parent)
|
||||
queryset = self.queryset.filter(parent=parent)
|
||||
else:
|
||||
queryset = self.queryset.filter(status=1)
|
||||
queryset = self.queryset.filter()
|
||||
else:
|
||||
queryset = self.queryset.filter(status=1, parent__isnull=True)
|
||||
queryset = self.queryset.filter(parent__isnull=True)
|
||||
queryset = self.filter_queryset(queryset)
|
||||
serializer = MenuSerializer(queryset, many=True, request=request)
|
||||
data = serializer.data
|
||||
|
||||
@@ -85,7 +85,7 @@ class MessageCenterTargetUserListSerializer(CustomModelSerializer):
|
||||
def get_is_read(self, instance):
|
||||
user_id = self.request.user.id
|
||||
message_center_id = instance.id
|
||||
queryset = MessageCenterTargetUser.objects.filter(messagecenter__id=message_center_id,users_id=user_id).first()
|
||||
queryset = MessageCenterTargetUser.objects.filter(messagecenter__id=message_center_id, users_id=user_id).first()
|
||||
if queryset:
|
||||
return queryset.is_read
|
||||
return False
|
||||
@@ -95,21 +95,22 @@ class MessageCenterTargetUserListSerializer(CustomModelSerializer):
|
||||
fields = "__all__"
|
||||
read_only_fields = ["id"]
|
||||
|
||||
|
||||
def websocket_push(user_id, message):
|
||||
"""
|
||||
主动推送消息
|
||||
"""
|
||||
username = "user_"+str(user_id)
|
||||
print(103,message)
|
||||
username = "user_" + str(user_id)
|
||||
channel_layer = get_channel_layer()
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
username,
|
||||
{
|
||||
"type": "push.message",
|
||||
"json": message
|
||||
}
|
||||
username,
|
||||
{
|
||||
"type": "push.message",
|
||||
"json": message
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class MessageCenterCreateSerializer(CustomModelSerializer):
|
||||
"""
|
||||
消息中心-新增-序列化器
|
||||
@@ -122,10 +123,10 @@ class MessageCenterCreateSerializer(CustomModelSerializer):
|
||||
# 在保存之前,根据目标类型,把目标用户查询出来并保存
|
||||
users = initial_data.get('target_user', [])
|
||||
if target_type in [1]: # 按角色
|
||||
target_role = initial_data.get('target_role',[])
|
||||
target_role = initial_data.get('target_role', [])
|
||||
users = Users.objects.filter(role__id__in=target_role).values_list('id', flat=True)
|
||||
if target_type in [2]: # 按部门
|
||||
target_dept = initial_data.get('target_dept',[])
|
||||
target_dept = initial_data.get('target_dept', [])
|
||||
users = Users.objects.filter(dept__id__in=target_dept).values_list('id', flat=True)
|
||||
if target_type in [3]: # 系统通知
|
||||
users = Users.objects.values_list('id', flat=True)
|
||||
@@ -141,7 +142,7 @@ class MessageCenterCreateSerializer(CustomModelSerializer):
|
||||
for user in users:
|
||||
unread_count = MessageCenterTargetUser.objects.filter(users__id=user, is_read=False).count()
|
||||
websocket_push(user, message={"sender": 'system', "contentType": 'SYSTEM',
|
||||
"content": '您有一条新消息~', "unread": unread_count})
|
||||
"content": '您有一条新消息~', "unread": unread_count})
|
||||
return data
|
||||
|
||||
class Meta:
|
||||
@@ -184,7 +185,7 @@ class MessageCenterViewSet(CustomModelViewSet):
|
||||
# 主动推送消息
|
||||
unread_count = MessageCenterTargetUser.objects.filter(users__id=user_id, is_read=False).count()
|
||||
websocket_push(user_id, message={"sender": 'system', "contentType": 'TEXT',
|
||||
"content": '您查看了一条消息~', "unread": unread_count})
|
||||
"content": '您查看了一条消息~', "unread": unread_count})
|
||||
return DetailResponse(data=serializer.data, msg="获取成功")
|
||||
|
||||
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
||||
@@ -195,7 +196,6 @@ class MessageCenterViewSet(CustomModelViewSet):
|
||||
self_user_id = self.request.user.id
|
||||
# queryset = MessageCenterTargetUser.objects.filter(users__id=self_user_id).order_by('-create_datetime')
|
||||
queryset = MessageCenter.objects.filter(target_user__id=self_user_id)
|
||||
print(queryset)
|
||||
# queryset = self.filter_queryset(queryset)
|
||||
page = self.paginate_queryset(queryset)
|
||||
if page is not None:
|
||||
|
||||
@@ -61,10 +61,10 @@ class SystemConfigChinldernSerializer(CustomModelSerializer):
|
||||
"""
|
||||
系统配置子级-序列化器
|
||||
"""
|
||||
chinldern = serializers.SerializerMethodField()
|
||||
children = serializers.SerializerMethodField()
|
||||
form_item_type_label = serializers.CharField(source='get_form_item_type_display', read_only=True)
|
||||
|
||||
def get_chinldern(self, instance):
|
||||
def get_children(self, instance):
|
||||
queryset = SystemConfig.objects.filter(parent=instance)
|
||||
serializer = SystemConfigSerializer(queryset, many=True)
|
||||
return serializer.data
|
||||
|
||||
@@ -41,6 +41,7 @@ class UserSerializer(CustomModelSerializer):
|
||||
exclude = ["password"]
|
||||
extra_kwargs = {
|
||||
"post": {"required": False},
|
||||
"mobile": {"required": False},
|
||||
}
|
||||
|
||||
def get_dept_name_all(self, instance):
|
||||
@@ -60,9 +61,6 @@ class UserSerializer(CustomModelSerializer):
|
||||
return serializer.data
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class UserCreateSerializer(CustomModelSerializer):
|
||||
"""
|
||||
用户新增-序列化器
|
||||
@@ -82,10 +80,10 @@ class UserCreateSerializer(CustomModelSerializer):
|
||||
"""
|
||||
对密码进行验证
|
||||
"""
|
||||
password = self.initial_data.get("password")
|
||||
if password:
|
||||
return make_password(value)
|
||||
return value
|
||||
md5 = hashlib.md5()
|
||||
md5.update(value.encode('utf-8'))
|
||||
md5_password = md5.hexdigest()
|
||||
return make_password(md5_password)
|
||||
|
||||
def save(self, **kwargs):
|
||||
data = super().save(**kwargs)
|
||||
@@ -100,6 +98,7 @@ class UserCreateSerializer(CustomModelSerializer):
|
||||
read_only_fields = ["id"]
|
||||
extra_kwargs = {
|
||||
"post": {"required": False},
|
||||
"mobile": {"required": False},
|
||||
}
|
||||
|
||||
|
||||
@@ -114,14 +113,15 @@ class UserUpdateSerializer(CustomModelSerializer):
|
||||
CustomUniqueValidator(queryset=Users.objects.all(), message="账号必须唯一")
|
||||
],
|
||||
)
|
||||
|
||||
# password = serializers.CharField(required=False, allow_blank=True)
|
||||
mobile = serializers.CharField(
|
||||
max_length=50,
|
||||
validators=[
|
||||
CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一")
|
||||
],
|
||||
allow_blank=True
|
||||
)
|
||||
# mobile = serializers.CharField(
|
||||
# max_length=50,
|
||||
# validators=[
|
||||
# CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一")
|
||||
# ],
|
||||
# allow_blank=True
|
||||
# )
|
||||
|
||||
def save(self, **kwargs):
|
||||
data = super().save(**kwargs)
|
||||
@@ -136,6 +136,7 @@ class UserUpdateSerializer(CustomModelSerializer):
|
||||
fields = "__all__"
|
||||
extra_kwargs = {
|
||||
"post": {"required": False, "read_only": True},
|
||||
"mobile": {"required": False},
|
||||
}
|
||||
|
||||
|
||||
@@ -159,6 +160,7 @@ class UserInfoUpdateSerializer(CustomModelSerializer):
|
||||
fields = ['email', 'mobile', 'avatar', 'name', 'gender']
|
||||
extra_kwargs = {
|
||||
"post": {"required": False, "read_only": True},
|
||||
"mobile": {"required": False},
|
||||
}
|
||||
|
||||
|
||||
@@ -293,6 +295,11 @@ class UserViewSet(CustomModelViewSet):
|
||||
'dept_id': dept.id,
|
||||
'dept_name': dept.name
|
||||
}
|
||||
else:
|
||||
result['dept_info'] = {
|
||||
'dept_id': None,
|
||||
'dept_name': "暂无部门"
|
||||
}
|
||||
role = getattr(user, 'role', None)
|
||||
if role:
|
||||
result['role_info'] = role.values('id', 'name', 'key')
|
||||
|
||||
@@ -38,12 +38,16 @@ def CustomExceptionHandler(ex, context):
|
||||
# 调用默认的异常处理函数
|
||||
response = exception_handler(ex, context)
|
||||
if isinstance(ex, AuthenticationFailed):
|
||||
code = 401
|
||||
code_type = response.data.get('detail').code
|
||||
if code_type == 'no_active_account':
|
||||
code=400
|
||||
# 如果是身份验证错误
|
||||
if response and response.data.get('detail') == "Given token not valid for any token type":
|
||||
code = 401
|
||||
msg = ex.detail
|
||||
elif response and response.data.get('detail') == "Token is blacklisted":
|
||||
# token在黑名单
|
||||
return ErrorResponse(status=HTTP_401_UNAUTHORIZED)
|
||||
msg = ex.detail
|
||||
else:
|
||||
code = 401
|
||||
msg = ex.detail
|
||||
elif isinstance(ex,Http404):
|
||||
code = 400
|
||||
msg = "接口地址不正确"
|
||||
|
||||
@@ -1,28 +1,37 @@
|
||||
import logging
|
||||
import os.path
|
||||
from logging import LogRecord
|
||||
import sys
|
||||
|
||||
from django.core.servers.basehttp import WSGIRequestHandler
|
||||
from django.db import connection
|
||||
from loguru import logger
|
||||
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
# 1.🎖️先声明一个类继承logging.Handler(制作一件品如的衣服)
|
||||
from loguru._defaults import LOGURU_FORMAT
|
||||
|
||||
from application.dispatch import is_tenants_mode
|
||||
|
||||
|
||||
class InterceptTimedRotatingFileHandler(RotatingFileHandler):
|
||||
class InterceptTimedRotatingFileHandler(RotatingFileHandler, logging.Filter):
|
||||
"""
|
||||
自定义反射时间回滚日志记录器
|
||||
缺少命名空间
|
||||
"""
|
||||
|
||||
def __init__(self, filename, when='d', interval=1, backupCount=5, encoding="utf-8", delay=False, utc=False,
|
||||
maxBytes=1024 * 1024 * 100, atTime=None, logging_levels="all"):
|
||||
maxBytes=1024 * 1024 * 100, atTime=None, logging_levels="all", format=None):
|
||||
super(InterceptTimedRotatingFileHandler, self).__init__(filename)
|
||||
filename = os.path.abspath(filename)
|
||||
# 定义默认格式
|
||||
if not format:
|
||||
format = "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <green>{extra[ip]}:{extra[port]}</green> | <level>{level: <8}</level>| <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>"
|
||||
when = when.lower()
|
||||
# 2.🎖️需要本地用不同的文件名做为不同日志的筛选器
|
||||
logger.configure(
|
||||
handlers=[
|
||||
dict(sink=sys.stderr, format=format),
|
||||
],
|
||||
)
|
||||
self.logger_ = logger.bind(sime=filename, ip="-", port="-", username="张三")
|
||||
self.filename = filename
|
||||
key_map = {
|
||||
@@ -74,7 +83,7 @@ class InterceptTimedRotatingFileHandler(RotatingFileHandler):
|
||||
# self.logger_.remove(file_key[filename_fmt_key])
|
||||
self.logger_.add(
|
||||
filename_fmt,
|
||||
# format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <green>{extra[ip]}:{extra[port]}</green> | <level>{level: <8}</level>| <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
|
||||
format=format,
|
||||
retention=retention,
|
||||
encoding=encoding,
|
||||
level=self.level,
|
||||
@@ -100,13 +109,17 @@ class InterceptTimedRotatingFileHandler(RotatingFileHandler):
|
||||
# 设置自定义属性
|
||||
port = "-"
|
||||
ip = "-"
|
||||
locals_self = frame.f_locals.get('self', None)
|
||||
details = frame.f_locals.get('details', None)
|
||||
msg = self.format(record)
|
||||
if locals_self and hasattr(locals_self, 'client_address'):
|
||||
ip, port = locals_self.client_address
|
||||
# - 127.0.0.1:56525 -
|
||||
msg = f"{ip}:{port} - {msg}"
|
||||
bind = {}
|
||||
if details and details.get('client'):
|
||||
ip, port = details.get('client').split(':')
|
||||
if is_tenants_mode():
|
||||
bind["schema_name"] = connection.tenant.schema_name
|
||||
bind["domain_url"] = getattr(connection.tenant, 'domain_url', None)
|
||||
bind["ip"] = ip
|
||||
bind["port"] = port
|
||||
self.logger_ \
|
||||
.opt(depth=depth, exception=record.exc_info, colors=True) \
|
||||
.bind(ip=ip, port=port) \
|
||||
.bind(**bind) \
|
||||
.log(level, msg)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
7
|
||||
@@ -28,6 +28,6 @@ server {
|
||||
proxy_send_timeout 600s;
|
||||
real_ip_header X-Forwarded-For;
|
||||
rewrite ^/api/(.*)$ /$1 break; #重写
|
||||
proxy_pass http://177.8.0.12:8000/; # 设置代理服务器的协议和地址
|
||||
proxy_pass http://178.10.0.12:8000/; # 设置代理服务器的协议和地址
|
||||
}
|
||||
}
|
||||
|
||||
6818
web/package-lock.json
generated
6818
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,12 +10,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.0.10",
|
||||
"@fast-crud/fast-crud": "^1.9.0",
|
||||
"@fast-crud/fast-extends": "^1.9.0",
|
||||
"@fast-crud/ui-element": "^1.9.0",
|
||||
"@fast-crud/fast-crud": "^1.11.10",
|
||||
"@fast-crud/fast-extends": "^1.11.10",
|
||||
"@fast-crud/ui-element": "^1.11.10",
|
||||
"@fast-crud/ui-interface": "^1.11.9",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"axios": "^1.2.1",
|
||||
"countup.js": "^2.3.2",
|
||||
"cropperjs": "^1.5.13",
|
||||
@@ -32,15 +34,18 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.28",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"postcss": "^8.4.21",
|
||||
"print-js": "^1.6.0",
|
||||
"qrcodejs2-fixes": "^0.0.2",
|
||||
"qs": "^6.11.0",
|
||||
"screenfull": "^6.0.2",
|
||||
"sortablejs": "^1.15.0",
|
||||
"splitpanes": "^3.1.5",
|
||||
"tailwindcss": "^3.2.7",
|
||||
"ts-md5": "^1.3.1",
|
||||
"vue": "^3.2.45",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-cropper": "^1.0.8",
|
||||
"vue-grid-layout": "^3.0.0-beta1",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.1.6",
|
||||
|
||||
6
web/postcss.config.js
Normal file
6
web/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
3
web/src/assets/style/tailwind.css
Normal file
3
web/src/assets/style/tailwind.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
196
web/src/components/avatarSelector/index.vue
Normal file
196
web/src/components/avatarSelector/index.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div class="user-info-head" @click="editCropper()">
|
||||
<el-avatar :size="100" :src="options.img" />
|
||||
<el-dialog :title="title" v-model="dialogVisiable" width="600px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-row>
|
||||
<el-col class="flex justify-center">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="options.img"
|
||||
:info="true"
|
||||
:autoCrop="options.autoCrop"
|
||||
:autoCropWidth="options.autoCropWidth"
|
||||
:autoCropHeight="options.autoCropHeight"
|
||||
:fixedBox="options.fixedBox"
|
||||
:outputType="options.outputType"
|
||||
@realTime="realTime"
|
||||
:centerBox="true"
|
||||
v-if="visible"
|
||||
class="cropper"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<br />
|
||||
<el-row class="flex justify-center">
|
||||
<el-col :lg="2" :md="2">
|
||||
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
|
||||
<el-button type="success">
|
||||
选择
|
||||
<el-icon class="el-icon--right"><Plus /></el-icon>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 2, offset: 2 }" :md="2">
|
||||
<el-button type="primary" @click="uploadImg()">更新头像</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import 'vue-cropper/dist/index.css';
|
||||
import { VueCropper } from 'vue-cropper';
|
||||
import { useUserInfo } from '/@/stores/userInfo';
|
||||
import { getCurrentInstance, nextTick, reactive, ref, computed, onMounted, defineExpose } from 'vue';
|
||||
import { base64ToFile } from '/@/utils/tools';
|
||||
const userStore = useUserInfo();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const open = ref(false);
|
||||
const visible = ref(false);
|
||||
const title = ref('修改头像');
|
||||
const emit = defineEmits(['uploadImg']);
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const dialogVisiable = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(newVal) {
|
||||
emit('update:modelValue', newVal);
|
||||
},
|
||||
});
|
||||
|
||||
//图片裁剪数据
|
||||
const options = reactive({
|
||||
img: userStore.userInfos.avatar, // 裁剪图片的地址
|
||||
fileName: '',
|
||||
autoCrop: true, // 是否默认生成截图框
|
||||
autoCropWidth: 200, // 默认生成截图框宽度
|
||||
autoCropHeight: 200, // 默认生成截图框高度
|
||||
fixedBox: true, // 固定截图框大小 不允许改变
|
||||
outputType: 'png', // 默认生成截图为PNG格式
|
||||
});
|
||||
|
||||
/** 编辑头像 */
|
||||
function editCropper() {
|
||||
dialogVisiable.value = true;
|
||||
}
|
||||
/** 打开弹出层结束时的回调 */
|
||||
function modalOpened() {
|
||||
nextTick(() => {
|
||||
visible.value = true;
|
||||
});
|
||||
}
|
||||
/** 覆盖默认上传行为 */
|
||||
function requestUpload() {}
|
||||
/** 向左旋转 */
|
||||
function rotateLeft() {
|
||||
proxy.$refs.cropper.rotateLeft();
|
||||
}
|
||||
/** 向右旋转 */
|
||||
function rotateRight() {
|
||||
proxy.$refs.cropper.rotateRight();
|
||||
}
|
||||
/** 图片缩放 */
|
||||
function changeScale(num) {
|
||||
num = num || 1;
|
||||
proxy.$refs.cropper.changeScale(num);
|
||||
}
|
||||
/** 上传预处理 */
|
||||
function beforeUpload(file) {
|
||||
if (file.type.indexOf('image/') == -1) {
|
||||
proxy.$modal.msgError('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。');
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
options.img = reader.result;
|
||||
options.fileName = file.name;
|
||||
};
|
||||
}
|
||||
}
|
||||
/** 上传图片 */
|
||||
function uploadImg() {
|
||||
// 获取截图的 base64 数据
|
||||
proxy.$refs.cropper.getCropData((data) => {
|
||||
let img = new Image();
|
||||
img.src = data;
|
||||
img.onload = async () => {
|
||||
let _data = compress(img);
|
||||
const imgFile = base64ToFile(_data, options.fileName);
|
||||
emit('uploadImg', imgFile);
|
||||
};
|
||||
});
|
||||
}
|
||||
// 压缩图片
|
||||
function compress(img) {
|
||||
let canvas = document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d');
|
||||
// let initSize = img.src.length;
|
||||
let width = img.width;
|
||||
let height = img.height;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
// 铺底色
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
// 进行压缩
|
||||
let ndata = canvas.toDataURL('image/jpeg', 0.8);
|
||||
return ndata;
|
||||
}
|
||||
|
||||
/** 关闭窗口 */
|
||||
function closeDialog() {
|
||||
options.visible = false;
|
||||
options.img = userStore.userInfos.avatar;
|
||||
}
|
||||
|
||||
const updateAvatar = (img) => {
|
||||
options.img = img;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
updateAvatar,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-info-head {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.user-info-head:hover:after {
|
||||
content: '修改头像';
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
color: #000000;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
cursor: pointer;
|
||||
line-height: 110px;
|
||||
}
|
||||
.cropper {
|
||||
height: 400px;
|
||||
width: 400px;
|
||||
}
|
||||
</style>
|
||||
@@ -9,8 +9,8 @@ export default {
|
||||
two4: 'Links',
|
||||
},
|
||||
account: {
|
||||
accountPlaceholder1: 'The user name admin or not is common',
|
||||
accountPlaceholder2: 'Password: 123456',
|
||||
accountPlaceholder1: 'Please enter your login account',
|
||||
accountPlaceholder2: 'Please enter your login password',
|
||||
accountPlaceholder3: 'Please enter the verification code',
|
||||
accountBtnText: 'Sign in',
|
||||
},
|
||||
|
||||
@@ -9,8 +9,8 @@ export default {
|
||||
two4: '友情链接',
|
||||
},
|
||||
account: {
|
||||
accountPlaceholder1: '用户名 admin 或不输均为 common',
|
||||
accountPlaceholder2: '密码:123456',
|
||||
accountPlaceholder1: '请输入登录账号',
|
||||
accountPlaceholder2: '请输入登录密码',
|
||||
accountPlaceholder3: '请输入验证码',
|
||||
accountBtnText: '登 录',
|
||||
},
|
||||
|
||||
@@ -9,8 +9,8 @@ export default {
|
||||
two4: '友情連結',
|
||||
},
|
||||
account: {
|
||||
accountPlaceholder1: '用戶名admin或不輸均為common',
|
||||
accountPlaceholder2: '密碼:123456',
|
||||
accountPlaceholder1: '請輸入登入賬號',
|
||||
accountPlaceholder2: '請輸入登入密碼',
|
||||
accountPlaceholder3: '請輸入驗證碼',
|
||||
accountBtnText: '登入',
|
||||
},
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="layout-navbars-breadcrumb-user-icon">
|
||||
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
|
||||
<template #reference>
|
||||
<el-badge :value="messageCenter.unread" :hidden="messageCenter.unread===0">
|
||||
<el-badge :value="messageCenter.unread" :hidden="messageCenter.unread === 0">
|
||||
<el-icon :title="$t('message.user.title4')">
|
||||
<ele-Bell />
|
||||
</el-icon>
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
||||
<span class="layout-navbars-breadcrumb-user-link">
|
||||
<img :src="userInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" />
|
||||
<img :src="userInfos.avatar" class="layout-navbars-breadcrumb-user-link-photo mr5" />
|
||||
{{ userInfos.userName === '' ? 'common' : userInfos.userName }}
|
||||
<el-icon class="el-icon--right">
|
||||
<ele-ArrowDown />
|
||||
@@ -68,7 +68,7 @@
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -208,8 +208,8 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
//消息中心的未读数量
|
||||
import {messageCenterStore} from "/@/stores/messageCenter";
|
||||
const messageCenter = messageCenterStore()
|
||||
import { messageCenterStore } from '/@/stores/messageCenter';
|
||||
const messageCenter = messageCenterStore();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import {createApp} from 'vue';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import {directive} from '/@/utils/directive';
|
||||
import {i18n} from '/@/i18n/index';
|
||||
import { directive } from '/@/utils/directive';
|
||||
import { i18n } from '/@/i18n/index';
|
||||
import other from '/@/utils/other';
|
||||
import '/@/assets/style/tailwind.css'; // 先引入tailwind css, 以免element-plus冲突
|
||||
import ElementPlus from 'element-plus';
|
||||
import 'element-plus/dist/index.css';
|
||||
import '/@/theme/index.scss';
|
||||
@@ -15,7 +16,7 @@ import fastCrud from './settings.ts';
|
||||
import pinia from './stores';
|
||||
import permission from '/@/plugin/permission/index';
|
||||
// @ts-ignore
|
||||
import eIconPicker, { iconList,analyzingIconForIconfont } from 'e-icon-picker';
|
||||
import eIconPicker, { iconList, analyzingIconForIconfont } from 'e-icon-picker';
|
||||
import 'e-icon-picker/icon/default-icon/symbol.js'; //基本彩色图标库
|
||||
import 'e-icon-picker/index.css'; // 基本样式,包含基本图标
|
||||
import 'font-awesome/css/font-awesome.min.css';
|
||||
@@ -24,6 +25,9 @@ import fontAwesome470 from 'e-icon-picker/icon/fontawesome/font-awesome.v4.7.0.j
|
||||
import eIconList from 'e-icon-picker/icon/default-icon/eIconList.js';
|
||||
import iconfont from '/@/assets/iconfont/iconfont.json'; //引入json文件
|
||||
import '/@/assets/iconfont/iconfont.css'; //引入css
|
||||
// 自动注册插件
|
||||
import { scanAndInstallPlugins } from '/@/views/plugins/index';
|
||||
|
||||
let forIconfont = analyzingIconForIconfont(iconfont); //解析class
|
||||
iconList.addIcon(forIconfont.list); // 添加iconfont dvadmin3的icon
|
||||
iconList.addIcon(elementPlus); // 添加element plus的图标
|
||||
@@ -31,10 +35,12 @@ iconList.addIcon(fontAwesome470); // 添加fontAwesome 470版本的图标
|
||||
|
||||
let app = createApp(App);
|
||||
|
||||
scanAndInstallPlugins(app);
|
||||
|
||||
app.use(eIconPicker, {
|
||||
addIconList: eIconList, //全局添加图标
|
||||
removeIconList: [], //全局删除图标
|
||||
zIndex: 3100, //选择器弹层的最低层,全局配置
|
||||
addIconList: eIconList, //全局添加图标
|
||||
removeIconList: [], //全局删除图标
|
||||
zIndex: 3100, //选择器弹层的最低层,全局配置
|
||||
});
|
||||
|
||||
pinia.use(piniaPersist);
|
||||
@@ -42,6 +48,6 @@ directive(app);
|
||||
other.elSvg(app);
|
||||
|
||||
app.use(permission);
|
||||
app.use(pinia).use(router).use(ElementPlus, {i18n: i18n.global.t}).use(i18n).use(VueGridLayout).use(fastCrud).mount('#app');
|
||||
app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(i18n).use(VueGridLayout).use(fastCrud).mount('#app');
|
||||
|
||||
app.config.globalProperties.mittBus = mitt();
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useRoutesList } from '/@/stores/routesList';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
import { useMenuApi } from '/@/api/menu/index';
|
||||
import { handleMenu } from '../utils/menu';
|
||||
import {BtnPermissionStore} from "/@/plugin/permission/store.permission";
|
||||
import { BtnPermissionStore } from '/@/plugin/permission/store.permission';
|
||||
|
||||
const menuApi = useMenuApi();
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import { setLogger } from '@fast-crud/fast-crud';
|
||||
import ui from '@fast-crud/ui-element';
|
||||
import { request } from '/@/utils/service';
|
||||
//扩展包
|
||||
import {FsExtendsEditor} from "@fast-crud/fast-extends";
|
||||
import "@fast-crud/fast-extends/dist/style.css";
|
||||
import { FsExtendsEditor } from '@fast-crud/fast-extends';
|
||||
import '@fast-crud/fast-extends/dist/style.css';
|
||||
export default {
|
||||
async install(app: any, options: any) {
|
||||
// 先安装ui
|
||||
@@ -18,7 +18,7 @@ export default {
|
||||
//i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件
|
||||
// 此处配置公共的dictRequest(字典请求)
|
||||
async dictRequest({ dict }: any) {
|
||||
return await request({ url: dict.url,params:dict.params || {} }); //根据dict的url,异步返回一个字典数组
|
||||
return await request({ url: dict.url, params: dict.params || {} }); //根据dict的url,异步返回一个字典数组
|
||||
},
|
||||
//公共crud配置
|
||||
commonOptions() {
|
||||
@@ -28,6 +28,9 @@ export default {
|
||||
//你项目后台接口大概率与fast-crud所需要的返回结构不一致,所以需要配置此项
|
||||
//请参考文档http://fast-crud.docmirror.cn/api/crud-options/request.html
|
||||
transformQuery: ({ page, form, sort }: any) => {
|
||||
if (sort.asc !== undefined) {
|
||||
form['ordering'] = `${sort.asc ? '' : '-'}${sort.prop}`;
|
||||
}
|
||||
//转换为你pageRequest所需要的请求参数结构
|
||||
return { page: page.currentPage, limit: page.pageSize, ...form };
|
||||
},
|
||||
@@ -55,11 +58,11 @@ export default {
|
||||
},
|
||||
});
|
||||
//富文本
|
||||
app.use(FsExtendsEditor,{
|
||||
wangEditor:{
|
||||
width:300,
|
||||
}
|
||||
})
|
||||
app.use(FsExtendsEditor, {
|
||||
wangEditor: {
|
||||
width: 300,
|
||||
},
|
||||
});
|
||||
setLogger({ level: 'error' });
|
||||
// 设置自动染色
|
||||
const dictComponentList = ['dict-cascader', 'dict-checkbox', 'dict-radio', 'dict-select', 'dict-switch', 'dict-tree'];
|
||||
|
||||
@@ -5,11 +5,17 @@
|
||||
|
||||
// 用户信息
|
||||
export interface UserInfosState {
|
||||
authBtnList: string[];
|
||||
photo: string;
|
||||
roles: string[];
|
||||
time: number;
|
||||
avatar: string;
|
||||
userName: string;
|
||||
name: string;
|
||||
email: string;
|
||||
mobile: string;
|
||||
gender: string;
|
||||
dept_info: {
|
||||
dept_id: number;
|
||||
dept_name: string;
|
||||
};
|
||||
role_info: any[];
|
||||
}
|
||||
export interface UserInfosStates {
|
||||
userInfos: UserInfosState;
|
||||
@@ -91,4 +97,7 @@ export interface ThemeConfigStates {
|
||||
|
||||
export interface DictionaryStates {
|
||||
data: any;
|
||||
}
|
||||
}
|
||||
export interface ConfigStates {
|
||||
systemConfig: any;
|
||||
}
|
||||
|
||||
28
web/src/stores/systemConfig.ts
Normal file
28
web/src/stores/systemConfig.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ConfigStates } from './interface';
|
||||
import { request } from '../utils/service';
|
||||
export const urlPrefix = '/api/init/settings/';
|
||||
|
||||
/**
|
||||
* 系统配置数据
|
||||
* @methods getSystemConfig 获取系统配置数据
|
||||
*/
|
||||
export const SystemConfigStore = defineStore('SystemConfig', {
|
||||
state: (): ConfigStates => ({
|
||||
systemConfig: {},
|
||||
}),
|
||||
actions: {
|
||||
async getSystemConfigs() {
|
||||
request({
|
||||
url: urlPrefix,
|
||||
method: 'get',
|
||||
}).then((ret: { data: [] }) => {
|
||||
// 转换数据格式并保存到pinia
|
||||
this.systemConfig = JSON.parse(JSON.stringify(ret.data));
|
||||
});
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
@@ -1,9 +1,7 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import Cookies from 'js-cookie';
|
||||
import { UserInfosStates } from './interface';
|
||||
import { Session } from '/@/utils/storage';
|
||||
import { request } from '../utils/service';
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
* @methods setUserInfos 设置用户信息
|
||||
@@ -11,14 +9,37 @@ import { request } from '../utils/service';
|
||||
export const useUserInfo = defineStore('userInfo', {
|
||||
state: (): UserInfosStates => ({
|
||||
userInfos: {
|
||||
avatar: '',
|
||||
userName: '',
|
||||
photo: '',
|
||||
time: 0,
|
||||
roles: [],
|
||||
authBtnList: [],
|
||||
name: '',
|
||||
email: '',
|
||||
mobile: '',
|
||||
gender: '',
|
||||
dept_info: {
|
||||
dept_id: 0,
|
||||
dept_name: '',
|
||||
},
|
||||
role_info: [
|
||||
{
|
||||
id: 0,
|
||||
name: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
actions: {
|
||||
async updateUserInfos() {
|
||||
let userInfos: any = await this.getApiUserInfo();
|
||||
this.userInfos.userName = userInfos.data.name;
|
||||
this.userInfos.avatar = userInfos.data.avatar;
|
||||
this.userInfos.name = userInfos.data.name;
|
||||
this.userInfos.email = userInfos.data.email;
|
||||
this.userInfos.mobile = userInfos.data.mobile;
|
||||
this.userInfos.gender = userInfos.data.gender;
|
||||
this.userInfos.dept_info = userInfos.data.dept_info;
|
||||
this.userInfos.role_info = userInfos.data.role_info;
|
||||
Session.set('userInfo', this.userInfos);
|
||||
},
|
||||
async setUserInfos() {
|
||||
// 存储用户信息到浏览器缓存
|
||||
if (Session.get('userInfo')) {
|
||||
@@ -26,17 +47,21 @@ export const useUserInfo = defineStore('userInfo', {
|
||||
} else {
|
||||
let userInfos: any = await this.getApiUserInfo();
|
||||
this.userInfos.userName = userInfos.data.name;
|
||||
this.userInfos.photo = userInfos.data.avatar || 'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500'
|
||||
this.userInfos.time = new Date().getTime()
|
||||
this.userInfos.roles = ['admin']
|
||||
Session.set('userInfo', this.userInfos)
|
||||
this.userInfos.avatar = userInfos.data.avatar;
|
||||
this.userInfos.name = userInfos.data.name;
|
||||
this.userInfos.email = userInfos.data.email;
|
||||
this.userInfos.mobile = userInfos.data.mobile;
|
||||
this.userInfos.gender = userInfos.data.gender;
|
||||
this.userInfos.dept_info = userInfos.data.dept_info;
|
||||
this.userInfos.role_info = userInfos.data.role_info;
|
||||
Session.set('userInfo', this.userInfos);
|
||||
}
|
||||
},
|
||||
async getApiUserInfo() {
|
||||
return request({
|
||||
url: '/api/system/user/user_info/',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -62,15 +62,15 @@ function createService() {
|
||||
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
||||
switch (code) {
|
||||
case 400:
|
||||
Local.clear();
|
||||
Session.clear();
|
||||
// Local.clear();
|
||||
// Session.clear();
|
||||
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
||||
window.location.reload();
|
||||
// window.location.reload();
|
||||
break;
|
||||
case 401:
|
||||
Local.clear();
|
||||
Session.clear();
|
||||
dataAxios.msg = '登录授权过期,请重新登录';
|
||||
dataAxios.msg = '登录认证失败,请重新登录';
|
||||
ElMessageBox.alert(dataAxios.msg, '提示', {
|
||||
confirmButtonText: 'OK',
|
||||
callback: (action: Action) => {
|
||||
@@ -100,7 +100,15 @@ function createService() {
|
||||
error.message = '请求错误';
|
||||
break;
|
||||
case 401:
|
||||
error.message = '未授权,请登录';
|
||||
Local.clear();
|
||||
Session.clear();
|
||||
error.message = '登录授权过期,请重新登录';
|
||||
ElMessageBox.alert(error.message, '提示', {
|
||||
confirmButtonText: 'OK',
|
||||
callback: (action: Action) => {
|
||||
window.location.reload();
|
||||
},
|
||||
})
|
||||
break;
|
||||
case 403:
|
||||
error.message = '拒绝访问';
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
* @param {String} jsonString 需要解析的 json 字符串
|
||||
* @param {String} defaultValue 默认值
|
||||
*/
|
||||
import { uiContext } from "@fast-crud/fast-crud";
|
||||
import { uiContext } from '@fast-crud/fast-crud';
|
||||
|
||||
export function parse(jsonString = "{}", defaultValue = {}) {
|
||||
let result = defaultValue;
|
||||
try {
|
||||
result = JSON.parse(jsonString);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
return result;
|
||||
export function parse(jsonString = '{}', defaultValue = {}) {
|
||||
let result = defaultValue;
|
||||
try {
|
||||
result = JSON.parse(jsonString);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,8 +21,8 @@ export function parse(jsonString = "{}", defaultValue = {}) {
|
||||
* @param {String} msg 状态信息
|
||||
* @param {Number} code 状态码
|
||||
*/
|
||||
export function response(data = {}, msg = "", code = 0) {
|
||||
return [200, { code, msg, data }];
|
||||
export function response(data = {}, msg = '', code = 0) {
|
||||
return [200, { code, msg, data }];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,8 +30,8 @@ export function response(data = {}, msg = "", code = 0) {
|
||||
* @param {Any} data 返回值
|
||||
* @param {String} msg 状态信息
|
||||
*/
|
||||
export function responseSuccess(data = {}, msg = "成功") {
|
||||
return response(data, msg);
|
||||
export function responseSuccess(data = {}, msg = '成功') {
|
||||
return response(data, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,30 +40,62 @@ export function responseSuccess(data = {}, msg = "成功") {
|
||||
* @param {String} msg 状态信息
|
||||
* @param {Number} code 状态码
|
||||
*/
|
||||
export function responseError(data = {}, msg = "请求失败", code = 500) {
|
||||
return response(data, msg, code);
|
||||
export function responseError(data = {}, msg = '请求失败', code = 500) {
|
||||
return response(data, msg, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 记录和显示错误
|
||||
* @param {Error} error 错误对象
|
||||
*/
|
||||
export function errorLog(error:any,notification=true) {
|
||||
// 打印到控制台
|
||||
console.error(error);
|
||||
// 显示提示
|
||||
if(notification){
|
||||
uiContext.get().notification.error({ message: error.message });
|
||||
}
|
||||
|
||||
export function errorLog(error: any, notification = true) {
|
||||
// 打印到控制台
|
||||
console.error(error);
|
||||
// 显示提示
|
||||
if (notification) {
|
||||
uiContext.get().notification.error({ message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 创建一个错误
|
||||
* @param {String} msg 错误信息
|
||||
*/
|
||||
export function errorCreate(msg:any,notification=true) {
|
||||
const error = new Error(msg);
|
||||
errorLog(error,notification);
|
||||
// throw error;
|
||||
export function errorCreate(msg: any, notification = true) {
|
||||
const error = new Error(msg);
|
||||
errorLog(error, notification);
|
||||
// throw error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description base64转file
|
||||
* @param {String} base64 base64字符串
|
||||
* @param {String} fileName 文件名
|
||||
*/
|
||||
export function base64ToFile(base64: any, fileName: string) {
|
||||
// 将base64按照 , 进行分割 将前缀 与后续内容分隔开
|
||||
let data = base64.split(',');
|
||||
// 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
|
||||
let type = data[0].match(/:(.*?);/)[1];
|
||||
// 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
|
||||
let suffix = type.split('/')[1];
|
||||
// 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
|
||||
const bstr = window.atob(data[1]);
|
||||
// 获取解码结果字符串的长度
|
||||
let n = bstr.length;
|
||||
// 根据解码结果字符串的长度创建一个等长的整形数字数组
|
||||
// 但在创建时 所有元素初始值都为 0
|
||||
const u8arr = new Uint8Array(n);
|
||||
// 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
|
||||
while (n--) {
|
||||
// charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
// 利用构造函数创建File文件对象
|
||||
// new File(bits, name, options)
|
||||
const file = new File([u8arr], `${fileName}.${suffix}`, {
|
||||
type: type,
|
||||
});
|
||||
// 将File文件对象返回给方法的调用者
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ const websocket: socket = {
|
||||
}
|
||||
const token = Session.get('token')
|
||||
if(!token){
|
||||
message.warning('websocket认证失败')
|
||||
// message.warning('websocket认证失败')
|
||||
return null
|
||||
}
|
||||
const wsUrl = `${getWsBaseURL()}ws/${token}/`
|
||||
|
||||
9
web/src/views/interface/index.ts
Normal file
9
web/src/views/interface/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
*
|
||||
* 后端API接口响应数据
|
||||
*/
|
||||
interface APIResponseData {
|
||||
code?: number;
|
||||
data: [];
|
||||
msg?: string;
|
||||
}
|
||||
15
web/src/views/plugins/index.ts
Normal file
15
web/src/views/plugins/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { defineAsyncComponent, AsyncComponentLoader } from 'vue';
|
||||
|
||||
// 扫描插件目录并注册插件
|
||||
export const scanAndInstallPlugins = (app: any) => {
|
||||
const components = import.meta.glob('./**/*.vue');
|
||||
const pluginNames = new Set();
|
||||
// 遍历对象并注册异步组件
|
||||
for (const [key, value] of Object.entries(components)) {
|
||||
const name = key.slice(key.lastIndexOf('/') + 1, key.lastIndexOf('.'));
|
||||
app.component(name, defineAsyncComponent(value as AsyncComponentLoader));
|
||||
const pluginsName = key.match(/\/([^\/]*)\//)?.[1];
|
||||
pluginNames.add(pluginsName);
|
||||
}
|
||||
console.log('已发现插件:', Array.from(pluginNames));
|
||||
};
|
||||
@@ -1,41 +1,41 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/area/';
|
||||
export function GetList(query: PageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
export function GetObj(id: InfoReq) {
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: AddReq) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: EditReq) {
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: DelReq) {
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { request } from '/@/utils/service';
|
||||
import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
import { successMessage } from '/@/utils/message';
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -26,8 +23,8 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
* @param row
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
const loadContentMethod = (tree: any, treeNode: any, resolve: any) => {
|
||||
api.GetList({ pcode: tree.code }).then((res: any) => {
|
||||
const loadContentMethod = (tree: any, treeNode: any, resolve: Function) => {
|
||||
pageRequest({ pcode: tree.code }).then((res: APIResponseData) => {
|
||||
resolve(res.data);
|
||||
});
|
||||
};
|
||||
@@ -40,6 +37,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
show: false,
|
||||
},
|
||||
@@ -90,8 +105,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
show: true,
|
||||
},
|
||||
treeNode: true,
|
||||
width: 160,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -108,6 +125,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
show: true,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 90,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -124,6 +144,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: true,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -140,6 +163,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: true,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
disabled: false,
|
||||
rules: [
|
||||
@@ -153,6 +179,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
initials: {
|
||||
title: '首字母',
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -169,14 +198,26 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
width: 90,
|
||||
type: 'dict-radio',
|
||||
column: {
|
||||
minWidth:90,
|
||||
component: {
|
||||
name: 'fs-dict-switch',
|
||||
activeText: '',
|
||||
inactiveText: '',
|
||||
style: '--el-switch-on-color: #409eff; --el-switch-off-color: #dcdfe6',
|
||||
onChange: compute((context) => {
|
||||
return () => {
|
||||
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool'),
|
||||
}),
|
||||
form: {
|
||||
value: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -6,18 +6,10 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import XEUtils from 'xe-utils';
|
||||
|
||||
export const apiPrefix = '/api/system/system_config/';
|
||||
export function GetList(query: PageQuery) {
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
@@ -52,10 +52,10 @@ export function GetAssociationTable() {
|
||||
});
|
||||
}
|
||||
|
||||
export function saveContent (data:any) {
|
||||
export function saveContent(data: any) {
|
||||
return request({
|
||||
url: apiPrefix + 'save_content/',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,41 +1,49 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/dept/';
|
||||
export function GetList(query: PageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
export function GetObj(id: InfoReq) {
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: AddReq) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: EditReq) {
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: DelReq) {
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
export function DelObj(obj: DelReq) {
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'delete',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function lazyLoadDept(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||
import { verifyPhone } from '/@/utils/toolsValidate';
|
||||
import { request } from '/@/utils/service';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
import { successMessage } from '/@/utils/message';
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
||||
const { getFormRef, getFormData } = crudExpose;
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -64,14 +60,26 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
rowHandle: {
|
||||
fiexd: 'right',
|
||||
width: 310,
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
},
|
||||
addChildren: {
|
||||
text: '添加子级',
|
||||
type: 'warning',
|
||||
type: 'text',
|
||||
click(context) {
|
||||
const rowId = context.row.id;
|
||||
crudExpose.openAdd({ row: { parent: rowId } });
|
||||
crudExpose!.openAdd({ row: { parent: rowId } });
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -192,6 +200,7 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
{
|
||||
type: 'email',
|
||||
message: '请输入正确的邮箱地址',
|
||||
// @ts-ignore
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
@@ -217,16 +226,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: false,
|
||||
},
|
||||
type: 'dict-radio',
|
||||
column: {
|
||||
component: {
|
||||
name: 'fs-dict-switch',
|
||||
activeText: '',
|
||||
inactiveText: '',
|
||||
style: '--el-switch-on-color: #409eff; --el-switch-off-color: #dcdfe6',
|
||||
onChange: compute((context) => {
|
||||
return () => {
|
||||
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool'),
|
||||
}),
|
||||
form: {
|
||||
value: true,
|
||||
component: {
|
||||
span: 12,
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,26 +1,272 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
<el-row class="mx-2">
|
||||
<el-col :span="4" class="p-1">
|
||||
<el-card :body-style="{ height: '100%' }">
|
||||
<p class="font-mono font-black text-center text-xl pb-5">部门列表</p>
|
||||
<el-input v-model="filterText" :placeholder="placeholder" />
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
class="font-mono font-bold leading-6 text-7xl"
|
||||
:data="data"
|
||||
:props="treeProps"
|
||||
:filter-node-method="filterNode"
|
||||
:load="loadNode"
|
||||
@node-drop="nodeDrop"
|
||||
lazy
|
||||
icon="ArrowRightBold"
|
||||
:indent="12"
|
||||
draggable
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span v-if="data.status" class="text-center font-black text-xl"><SvgIcon :name="node.data.icon" /> {{ node.label }}</span>
|
||||
<span v-else class="text-center font-black text-xl text-red-700"><SvgIcon :name="node.data.icon" /> {{ node.label }}</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="20" class="p-1">
|
||||
<el-card :body-style="{ height: '100%' }">
|
||||
<el-form ref="formRef" :rules="rules" :model="form" label-width="120px" label-position="right">
|
||||
<el-divider>
|
||||
<strong>部门配置</strong>
|
||||
</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="部门ID" prop="id"> <el-input v-model="form.id" disabled /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="父级部门ID" prop="parent"> <el-input v-model="form.parent" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item required label="部门名称" prop="name"> <el-input v-model="form.name" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item required label="部门标识" prop="key"> <el-input v-model="form.key" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="负责人" prop="owner"> <el-input v-model="form.owner" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="联系电话" prop="phone"> <el-input v-model="form.phone" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="邮箱" prop="email"> <el-input v-model="form.email" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="form.sort" controls-position="right" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col class="center">
|
||||
<el-divider>
|
||||
<el-button @click="saveMenu()" type="primary" round>保存</el-button>
|
||||
<el-button @click="newMenu()" type="success" round>新建</el-button>
|
||||
<el-button @click="addChildMenu()" type="info" round>添加子级</el-button>
|
||||
<el-button @click="addSameLevelMenu()" type="warning" round>添加同级</el-button>
|
||||
<el-button @click="deleteMenu()" type="danger" round>删除部门</el-button>
|
||||
</el-divider>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
import * as api from './api';
|
||||
import { ElForm, ElTree, FormRules } from 'element-plus';
|
||||
import { ref, onMounted, reactive, toRaw, watch } from 'vue';
|
||||
import XEUtils from 'xe-utils';
|
||||
import { errorMessage, successMessage } from '../../../utils/message';
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
name: string;
|
||||
status: boolean;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
interface APIResponseData {
|
||||
code?: number;
|
||||
data: [];
|
||||
msg?: string;
|
||||
}
|
||||
|
||||
interface Form<T> {
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
const placeholder = ref('请输入部门名称');
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
// isLeaf: (data: Tree[], node: Node) => {
|
||||
// // @ts-ignore
|
||||
// if (node.data.is_catalog) {
|
||||
// return false;
|
||||
// } else {
|
||||
// return true;
|
||||
// }
|
||||
// },
|
||||
};
|
||||
|
||||
const validateWebPath = (rule: string, value: string, callback: Function) => {
|
||||
let pattern = /^\/.*?/;
|
||||
if (!pattern.test(value)) {
|
||||
callback(new Error('请输入正确的地址'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return toRaw(data).name.indexOf(value) !== -1;
|
||||
};
|
||||
|
||||
// 懒加载
|
||||
const loadNode = (node: Node, resolve: (data: Tree[]) => void) => {
|
||||
// @ts-ignore
|
||||
if (node.level !== 0) {
|
||||
// @ts-ignore
|
||||
api.lazyLoadDept({ parent: node.data.id }).then((res: APIResponseData) => {
|
||||
resolve(res.data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const nodeDrop = (draggingNode: Node, dropNode: Node, dropType: string, event: any) => {
|
||||
// @ts-ignore
|
||||
if (!dropNode.isLeaf) {
|
||||
// @ts-ignore
|
||||
api.dragMenu({ menu_id: draggingNode.data.id, parent_id: dropNode.data.id }).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let data = ref([]);
|
||||
|
||||
let isAddNewMenu = ref(false); // 判断当前是新增部门,还是更新保存当前部门
|
||||
|
||||
let form: Form<any> = reactive({
|
||||
id: '',
|
||||
parent: '',
|
||||
name: '',
|
||||
owner: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
sort: '',
|
||||
});
|
||||
|
||||
const formRef = ref<InstanceType<typeof ElForm>>();
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
// @ts-ignore
|
||||
parent: [{ required: true, message: '父级部门名称必填', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '部门名称必填', trigger: 'blur' }],
|
||||
key: [{ required: true, message: '部门标识必填', trigger: 'blur' }],
|
||||
});
|
||||
|
||||
const getData = () => {
|
||||
api.GetList({}).then((ret: APIResponseData) => {
|
||||
const responseData = ret.data;
|
||||
const result = XEUtils.toArrayTree(responseData, {
|
||||
parentKey: 'parent',
|
||||
children: 'children',
|
||||
strict: true,
|
||||
});
|
||||
data.value = result;
|
||||
});
|
||||
};
|
||||
|
||||
const saveMenu = () => {
|
||||
formRef.value?.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
if (!isAddNewMenu.value) {
|
||||
// 保存部门
|
||||
form.component == '' ? (form.is_catalog = true) : (form.is_catalog = false);
|
||||
api.UpdateObj(form).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
getData();
|
||||
});
|
||||
} else {
|
||||
// 新增部门
|
||||
form.component == '' ? (form.is_catalog = true) : (form.is_catalog = false);
|
||||
api.AddObj(form).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
getData();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
errorMessage('请填写检查表单');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const newMenu = () => {
|
||||
formRef.value?.resetFields();
|
||||
isAddNewMenu.value = true;
|
||||
};
|
||||
|
||||
const addChildMenu = () => {
|
||||
let parentId = form.id;
|
||||
formRef.value?.resetFields();
|
||||
form.parent = parentId;
|
||||
isAddNewMenu.value = true;
|
||||
};
|
||||
|
||||
const addSameLevelMenu = () => {
|
||||
let parentId = form.parent;
|
||||
formRef.value?.resetFields();
|
||||
form.parent = parentId;
|
||||
isAddNewMenu.value = true;
|
||||
};
|
||||
|
||||
const deleteMenu = () => {
|
||||
api.DelObj(form).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
getData();
|
||||
});
|
||||
};
|
||||
|
||||
const handleNodeClick = (data: any, node: any, prop: any) => {
|
||||
Object.keys(toRaw(data)).forEach((key: string) => {
|
||||
form[key] = data[key];
|
||||
});
|
||||
delete form.component_name;
|
||||
form.id = data.id;
|
||||
isAddNewMenu.value = false;
|
||||
};
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-row {
|
||||
height: 100%;
|
||||
|
||||
.el-col {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.el-card {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
import {nextTick, ref} from 'vue';
|
||||
import { inject, nextTick, ref } from 'vue';
|
||||
import { successMessage } from '/@/utils/message';
|
||||
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExpose: CrudExpose; subDictRef: any }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -21,6 +18,10 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
//权限判定
|
||||
const hasPermissions = inject('$hasPermissions');
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
@@ -30,25 +31,38 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
width: 360,
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
show: hasPermissions('dictionary:Update'),
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
show: hasPermissions('dictionary:Delete'),
|
||||
},
|
||||
custom: {
|
||||
text: '字典配置',
|
||||
type: 'success',
|
||||
type: 'text',
|
||||
show: hasPermissions('dictionary:Update'),
|
||||
tooltip: {
|
||||
placement: 'top',
|
||||
content: '字典配置',
|
||||
},
|
||||
//@ts-ignore
|
||||
click: (context: any) => {
|
||||
const {row} = context
|
||||
subDictRef.value.drawer = true;
|
||||
nextTick(()=>{
|
||||
subDictRef.value.setSearchFormData({ form: { parent: row.id } });
|
||||
subDictRef.value.doRefresh();
|
||||
})
|
||||
|
||||
|
||||
click: (ctx: any) => {
|
||||
const { row } = ctx;
|
||||
context!.subDictRef.value.drawer = true;
|
||||
nextTick(() => {
|
||||
context!.subDictRef.value.setSearchFormData({ form: { parent: row.id } });
|
||||
context!.subDictRef.value.doRefresh();
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -65,7 +79,7 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
||||
formatter: (context) => {
|
||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||
let index = context.index ?? 1;
|
||||
let pagination = crudExpose.crudBinding.value.pagination;
|
||||
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||
// @ts-ignore
|
||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||
},
|
||||
@@ -105,6 +119,9 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
||||
},
|
||||
},
|
||||
type: 'input',
|
||||
column: {
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -129,6 +146,9 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
||||
},
|
||||
},
|
||||
type: 'input',
|
||||
column: {
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -149,34 +169,36 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
||||
},
|
||||
status: {
|
||||
title: '状态',
|
||||
width: 90,
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'dict-radio',
|
||||
column: {
|
||||
minWidth: 90,
|
||||
component: {
|
||||
name: 'fs-dict-switch',
|
||||
activeText: '',
|
||||
inactiveText: '',
|
||||
style: '--el-switch-on-color: #409eff; --el-switch-off-color: #dcdfe6',
|
||||
onChange: compute((context) => {
|
||||
return () => {
|
||||
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool'),
|
||||
}),
|
||||
component: {
|
||||
props: {
|
||||
options: [],
|
||||
},
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{ required: true, message: '状态必填项' },
|
||||
],
|
||||
value: true,
|
||||
component: {
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
},
|
||||
sort: {
|
||||
title: '排序',
|
||||
width: 90,
|
||||
type: 'number',
|
||||
column: {
|
||||
minWidth: 80,
|
||||
},
|
||||
form: {
|
||||
value: 1,
|
||||
},
|
||||
|
||||
@@ -6,24 +6,13 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { ref, onMounted, defineAsyncComponent } from 'vue';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
import subDict from './subDict/index.vue';
|
||||
|
||||
//字典配置ref
|
||||
const subDict = defineAsyncComponent(() => import('./subDict/index.vue'));
|
||||
const subDictRef = ref();
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose, subDictRef });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { subDictRef } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/dictionary/';
|
||||
export function GetList(query: PageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
export function GetObj(id: InfoReq) {
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: AddReq) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: EditReq) {
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: DelReq) {
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { request } from '/@/utils/service';
|
||||
import { dict, UserPageQuery, AddReq, DelReq, EditReq, CrudOptions, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
import {watch} from "vue";
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -30,6 +25,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
@@ -42,7 +55,7 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
formatter: (context) => {
|
||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||
let index = context.index ?? 1;
|
||||
let pagination = crudExpose.crudBinding.value.pagination;
|
||||
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||
// @ts-ignore
|
||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-drawer size="70%" v-model="drawer" direction="rtl" destroy-on-close :before-close="handleClose">
|
||||
<el-drawer size="70%" v-model="drawer" direction="rtl" destroy-on-close :before-close="handleClose">
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
@@ -7,27 +7,17 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, onMounted, defineProps, computed, watch} from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { ref, onMounted, defineAsyncComponent } from 'vue';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
|
||||
//抽屉是否显示
|
||||
const drawer = ref(false);
|
||||
|
||||
//抽屉关闭确认
|
||||
const handleClose = (done: () => void) => {
|
||||
|
||||
ElMessageBox.confirm('您确定要关闭?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
@@ -40,11 +30,13 @@ const handleClose = (done: () => void) => {
|
||||
// catch error
|
||||
});
|
||||
};
|
||||
const {setSearchFormData,doRefresh} = crudExpose
|
||||
defineExpose({ drawer,setSearchFormData,doRefresh });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
|
||||
const { setSearchFormData, doRefresh } = crudExpose;
|
||||
|
||||
defineExpose({ drawer, setSearchFormData, doRefresh });
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
// console.log(48,currentRow)
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/file/';
|
||||
export function GetList(query: PageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
data: query,
|
||||
});
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function GetObj(id: InfoReq) {
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: AddReq) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: EditReq) {
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: DelReq) {
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { request } from '/@/utils/service';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -35,6 +30,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
@@ -47,8 +60,8 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
formatter: (context) => {
|
||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||
let index = context.index ?? 1;
|
||||
let pagination = crudExpose.crudBinding.value.pagination;
|
||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||
return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -81,6 +94,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
show: true,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入文件名称',
|
||||
@@ -93,13 +109,18 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
search: {
|
||||
disabled: true,
|
||||
},
|
||||
column:{
|
||||
minWidth: 200,
|
||||
},
|
||||
},
|
||||
md5sum: {
|
||||
title: '文件MD5',
|
||||
width: 200,
|
||||
search: {
|
||||
disabled: true,
|
||||
},
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
disabled: false,
|
||||
},
|
||||
|
||||
@@ -6,18 +6,10 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/login_log/';
|
||||
export function GetList(query: PageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
data: query,
|
||||
});
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function GetObj(id: InfoReq) {
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: AddReq) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: EditReq) {
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: DelReq) {
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { request } from '/@/utils/service';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict } from '@fast-crud/fast-crud';
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -36,7 +31,12 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
fixed:'right',
|
||||
width: 100,
|
||||
buttons: {
|
||||
view: {
|
||||
type: 'text',
|
||||
},
|
||||
edit: {
|
||||
show: false,
|
||||
},
|
||||
@@ -54,6 +54,12 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
formatter: (context) => {
|
||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||
let index = context.index ?? 1;
|
||||
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||
return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
|
||||
},
|
||||
},
|
||||
},
|
||||
search: {
|
||||
@@ -84,8 +90,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
search: {
|
||||
disabled: false,
|
||||
},
|
||||
width: 140,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
component: {
|
||||
@@ -98,8 +106,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
search: {
|
||||
disabled: false,
|
||||
},
|
||||
width: 130,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
component: {
|
||||
@@ -113,8 +123,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: true,
|
||||
},
|
||||
disabled: true,
|
||||
width: 180,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入运营商',
|
||||
@@ -123,8 +135,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
continent: {
|
||||
title: '大州',
|
||||
width: 80,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 90,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
component: {
|
||||
@@ -135,8 +149,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
country: {
|
||||
title: '国家',
|
||||
width: 80,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 90,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入国家',
|
||||
@@ -146,8 +162,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
province: {
|
||||
title: '省份',
|
||||
width: 80,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 80,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入省份',
|
||||
@@ -157,8 +175,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
city: {
|
||||
title: '城市',
|
||||
width: 80,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 80,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入城市',
|
||||
@@ -169,8 +189,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
district: {
|
||||
title: '县区',
|
||||
key: '',
|
||||
width: 80,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 80,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入县区',
|
||||
@@ -181,6 +203,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
area_code: {
|
||||
title: '区域代码',
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 90,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入区域代码',
|
||||
@@ -190,8 +215,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
country_english: {
|
||||
title: '英文全称',
|
||||
width: 120,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入英文全称',
|
||||
@@ -202,6 +229,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
country_code: {
|
||||
title: '简称',
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入简称',
|
||||
@@ -213,6 +243,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
title: '经度',
|
||||
type: 'input',
|
||||
disabled: true,
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入经度',
|
||||
@@ -224,6 +257,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
title: '纬度',
|
||||
type: 'input',
|
||||
disabled: true,
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入纬度',
|
||||
@@ -233,26 +269,31 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
login_type: {
|
||||
title: '登录类型',
|
||||
type: 'select',
|
||||
type: 'dict-select',
|
||||
search: {
|
||||
disabled: false,
|
||||
},
|
||||
dict: {
|
||||
dict: dict({
|
||||
data: [
|
||||
{ label: '普通登录', value: 1 },
|
||||
{ label: '微信扫码登录', value: 2 },
|
||||
],
|
||||
}),
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请选择登录类型',
|
||||
},
|
||||
},
|
||||
component: { props: { color: 'auto' } }, // 自动染色
|
||||
},
|
||||
os: {
|
||||
title: '操作系统',
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入操作系统',
|
||||
@@ -262,6 +303,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
browser: {
|
||||
title: '浏览器名',
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入浏览器名',
|
||||
@@ -272,6 +316,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
title: 'agent信息',
|
||||
disabled: true,
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请输入agent信息',
|
||||
|
||||
@@ -6,18 +6,10 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/operation_log/';
|
||||
export function GetList(query: PageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
export function GetObj(id: InfoReq) {
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: AddReq) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: EditReq) {
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: DelReq) {
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { request } from '/@/utils/service';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -36,7 +31,12 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
fixed:'right',
|
||||
width: 100,
|
||||
buttons: {
|
||||
view: {
|
||||
type: 'text',
|
||||
},
|
||||
edit: {
|
||||
show: false,
|
||||
},
|
||||
@@ -57,8 +57,8 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
formatter: (context) => {
|
||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||
let index = context.index ?? 1;
|
||||
let pagination = crudExpose.crudBinding.value.pagination;
|
||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||
return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -91,6 +91,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: false,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
component: {
|
||||
@@ -104,6 +107,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: false,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 200,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
component: {
|
||||
@@ -142,6 +148,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
search: {
|
||||
disabled: false,
|
||||
},
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
component: {
|
||||
@@ -165,6 +174,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: false,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
component: {
|
||||
@@ -176,6 +188,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
request_browser: {
|
||||
title: '请求浏览器',
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
},
|
||||
@@ -187,6 +202,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: true,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
},
|
||||
@@ -199,6 +217,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: true,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
},
|
||||
@@ -210,6 +231,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
disabled: true,
|
||||
},
|
||||
type: 'input',
|
||||
column:{
|
||||
minWidth: 150,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
},
|
||||
@@ -217,6 +241,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
creator_name: {
|
||||
title: '操作人',
|
||||
column:{
|
||||
minWidth: 100,
|
||||
},
|
||||
form: {
|
||||
disabled: true,
|
||||
},
|
||||
|
||||
@@ -6,18 +6,10 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="login-animation3">
|
||||
<el-form-item class="login-animation3" v-if="isShowCaptcha">
|
||||
<el-col :span="15">
|
||||
<el-input
|
||||
type="text"
|
||||
@@ -46,7 +46,6 @@
|
||||
<el-col :span="8">
|
||||
<el-button class="login-content-captcha">
|
||||
<el-image :src="ruleForm.captchaImgBase" @click="refreshCaptcha" />
|
||||
<!-- TODO 完成点击刷新验证码 -->
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
@@ -59,7 +58,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent, computed, onMounted } from 'vue';
|
||||
import { toRefs, reactive, defineComponent, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@@ -74,7 +73,8 @@ import { NextLoading } from '/@/utils/loading';
|
||||
import * as loginApi from '/@/views/system/login/api';
|
||||
import { useUserInfo } from '/@/stores/userInfo';
|
||||
import { DictionaryStore } from '/@/stores/dictionary';
|
||||
import {BtnPermissionStore} from "/@/plugin/permission/store.permission";
|
||||
import { SystemConfigStore } from '/@/stores/systemConfig';
|
||||
import { BtnPermissionStore } from '/@/plugin/permission/store.permission';
|
||||
import { Md5 } from 'ts-md5';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -89,8 +89,8 @@ export default defineComponent({
|
||||
const state = reactive({
|
||||
isShowPassword: false,
|
||||
ruleForm: {
|
||||
username: 'superadmin',
|
||||
password: 'admin123456',
|
||||
username: '',
|
||||
password: '',
|
||||
captcha: '',
|
||||
captchaKey: '',
|
||||
captchaImgBase: '',
|
||||
@@ -103,6 +103,10 @@ export default defineComponent({
|
||||
const currentTime = computed(() => {
|
||||
return formatAxis(new Date());
|
||||
});
|
||||
// 是否关闭验证码
|
||||
const isShowCaptcha = computed(() => {
|
||||
return SystemConfigStore().systemConfig['base.captcha_state'];
|
||||
});
|
||||
|
||||
const getCaptcha = async () => {
|
||||
loginApi.getCaptcha().then((ret: any) => {
|
||||
@@ -117,31 +121,43 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
const loginClick = async () => {
|
||||
loginApi.login({ ...state.ruleForm, password: Md5.hashStr(state.ruleForm.password) }).then((ret: any) => {
|
||||
Session.set('token', ret.data.access);
|
||||
Cookies.set('username', ret.data.name);
|
||||
if (!themeConfig.value.isRequestRoutes) {
|
||||
// 前端控制路由,2、请注意执行顺序
|
||||
initFrontEndControlRoutes();
|
||||
loginSuccess();
|
||||
} else {
|
||||
// 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||
// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
|
||||
initBackEndControlRoutes();
|
||||
// 执行完 initBackEndControlRoutes,再执行 signInSuccess
|
||||
loginSuccess();
|
||||
loginApi.login({ ...state.ruleForm, password: Md5.hashStr(state.ruleForm.password) }).then((res: any) => {
|
||||
if (res.code === 2000) {
|
||||
Session.set('token', res.data.access);
|
||||
Cookies.set('username', res.data.name);
|
||||
if (!themeConfig.value.isRequestRoutes) {
|
||||
// 前端控制路由,2、请注意执行顺序
|
||||
initFrontEndControlRoutes();
|
||||
loginSuccess();
|
||||
} else {
|
||||
// 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||
// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
|
||||
initBackEndControlRoutes();
|
||||
// 执行完 initBackEndControlRoutes,再执行 signInSuccess
|
||||
loginSuccess();
|
||||
}
|
||||
} else if (res.code === 4000) {
|
||||
// 登录错误之后,刷新验证码
|
||||
refreshCaptcha();
|
||||
}
|
||||
});
|
||||
};
|
||||
const getUserInfo = () => {
|
||||
useUserInfo().setUserInfos();
|
||||
};
|
||||
|
||||
const enterClickLogin = (e: any) => {
|
||||
if (e.keyCode == 13 || e.keyCode == 100) {
|
||||
loginClick();
|
||||
}
|
||||
};
|
||||
// 登录成功后的跳转
|
||||
const loginSuccess = () => {
|
||||
//登录成功获取用户信息,获取系统字典数据
|
||||
getUserInfo();
|
||||
//获取所有字典
|
||||
DictionaryStore().getSystemDictionarys();
|
||||
|
||||
// 初始化登录成功时间问候语
|
||||
let currentTimeInfo = currentTime.value;
|
||||
// 登录成功,跳到转首页
|
||||
@@ -164,12 +180,19 @@ export default defineComponent({
|
||||
};
|
||||
onMounted(() => {
|
||||
getCaptcha();
|
||||
//获取系统配置
|
||||
SystemConfigStore().getSystemConfigs();
|
||||
window.addEventListener('keyup', enterClickLogin, false);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keyup', enterClickLogin, false);
|
||||
});
|
||||
|
||||
return {
|
||||
refreshCaptcha,
|
||||
loginClick,
|
||||
loginSuccess,
|
||||
isShowCaptcha,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/menu/';
|
||||
export function GetList(query: PageQuery) {
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: InfoReq) {
|
||||
return request({
|
||||
url: apiPrefix + id+'/',
|
||||
url: apiPrefix + id + '/',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
@@ -32,10 +33,33 @@ export function UpdateObj(obj: EditReq) {
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: DelReq) {
|
||||
export function DelObj(obj: DelReq) {
|
||||
return request({
|
||||
url: apiPrefix + id + '/',
|
||||
url: apiPrefix + obj.id + '/',
|
||||
method: 'delete',
|
||||
data: { id },
|
||||
});
|
||||
}
|
||||
|
||||
export function GetAllMenu(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix + 'get_all_menu/',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function lazyLoadMenu(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function dragMenu(obj: AddReq) {
|
||||
return request({
|
||||
url: apiPrefix + 'drag_menu/',
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
201
web/src/views/system/menu/components/menuButton/crud.tsx
Normal file
201
web/src/views/system/menu/components/menuButton/crud.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import {
|
||||
CrudOptions,
|
||||
AddReq,
|
||||
DelReq,
|
||||
EditReq,
|
||||
dict,
|
||||
CrudExpose,
|
||||
CreateCrudOptionsRet,
|
||||
CreateCrudOptionsProps,
|
||||
UserPageQuery,
|
||||
} from '@fast-crud/fast-crud';
|
||||
import _ from 'lodash-es';
|
||||
import * as api from './api';
|
||||
|
||||
import { request } from '/@/utils/service';
|
||||
//此处为crudOptions配置
|
||||
export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
console.log(context!.selectOptions);
|
||||
return await api.GetList({ menu: context!.selectOptions.value.id } as any);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
labelWidth: '100px',
|
||||
wrapper: {
|
||||
is: 'el-dialog',
|
||||
width: '600px',
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: { show: false },
|
||||
column: {
|
||||
type: 'index',
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
},
|
||||
},
|
||||
search: {
|
||||
title: '关键词',
|
||||
column: { show: false },
|
||||
type: 'text',
|
||||
search: { show: true },
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
placeholder: '输入关键词搜索',
|
||||
},
|
||||
},
|
||||
},
|
||||
id: {
|
||||
title: 'ID',
|
||||
type: 'text',
|
||||
column: { show: false },
|
||||
search: { show: false },
|
||||
form: { show: false },
|
||||
},
|
||||
name: {
|
||||
title: '权限名称',
|
||||
type: 'text',
|
||||
search: { show: true },
|
||||
column: {
|
||||
minWidth: 120,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '权限名称必填' }],
|
||||
component: {
|
||||
placeholder: '输入权限名称搜索',
|
||||
props: {
|
||||
clearable: true,
|
||||
allowCreate: true,
|
||||
filterable: true,
|
||||
},
|
||||
},
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="手动输入" type="warning" description="页面中按钮的名称或者自定义一个名称" />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
value: {
|
||||
title: '权限值',
|
||||
type: 'text',
|
||||
search: { show: false },
|
||||
column: {
|
||||
width: 120,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '权限标识必填' }],
|
||||
placeholder: '输入权限标识',
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="唯一值" type="warning" description="用于判断前端按钮权限或接口权限" />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
method: {
|
||||
title: '请求方式',
|
||||
search: { show: false },
|
||||
type: 'dict-select',
|
||||
column: {
|
||||
width: 120,
|
||||
sortable: true,
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{ label: 'GET', value: 0 },
|
||||
{ label: 'POST', value: 1, color: 'success' },
|
||||
{ label: 'PUT', value: 2, color: 'warning' },
|
||||
{ label: 'DELETE', value: 3, color: 'danger' },
|
||||
],
|
||||
}),
|
||||
form: {
|
||||
rules: [{ required: true, message: '必填项' }],
|
||||
},
|
||||
},
|
||||
api: {
|
||||
title: '接口地址',
|
||||
search: { show: false },
|
||||
type: 'dict-select',
|
||||
dict: dict({
|
||||
getData() {
|
||||
return request({ url: '/swagger.json' }).then((res: any) => {
|
||||
const ret = Object.keys(res.paths);
|
||||
const data = [];
|
||||
for (const item of ret) {
|
||||
const obj: any = {};
|
||||
obj.label = item;
|
||||
obj.value = item;
|
||||
data.push(obj);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
},
|
||||
}),
|
||||
column: {
|
||||
minWidth: 200,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '必填项' }],
|
||||
component: {
|
||||
props: {
|
||||
allowCreate: true,
|
||||
filterable: true,
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="请正确填写,以免请求时被拦截。匹配单例使用正则,例如:/api/xx/.*?/" type="warning" />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,186 +0,0 @@
|
||||
import {CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose} from '@fast-crud/fast-crud';
|
||||
import _ from 'lodash-es';
|
||||
import * as api from "./api";
|
||||
import {dictionary} from '/@/utils/dictionary';
|
||||
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
|
||||
import {request} from '/@/utils/service';
|
||||
//此处为crudOptions配置
|
||||
export const createCrudOptions = function ({
|
||||
crudExpose,
|
||||
selectOptions
|
||||
}: { crudExpose: CrudExpose, selectOptions: any }): CreateCrudOptionsTypes {
|
||||
|
||||
const pageRequest = async (query: any) => {
|
||||
return await api.GetList({ menu: selectOptions.value.id});
|
||||
};
|
||||
const editRequest = async ({form, row}: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({row}: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({form}: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
form: {
|
||||
col: {span: 24},
|
||||
labelWidth: '100px',
|
||||
wrapper: {
|
||||
is: 'el-dialog',
|
||||
width: '600px',
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: {show: false},
|
||||
column: {
|
||||
//type: 'index',
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
},
|
||||
},
|
||||
search: {
|
||||
title: '关键词',
|
||||
column: {show: false},
|
||||
type: 'text',
|
||||
search: {show: true},
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
placeholder: '输入关键词搜索',
|
||||
},
|
||||
},
|
||||
},
|
||||
id: {
|
||||
title: 'ID',
|
||||
type: 'text',
|
||||
column: {show: false},
|
||||
search: {show: false},
|
||||
form: {show: false},
|
||||
},
|
||||
name: {
|
||||
title: '权限名称',
|
||||
type: 'text',
|
||||
search: {show: true},
|
||||
column: {
|
||||
minWidth: 120,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{required: true, message: '权限名称必填'}],
|
||||
component: {
|
||||
placeholder: '输入权限名称搜索',
|
||||
props: {
|
||||
clearable: true,
|
||||
allowCreate: true,
|
||||
filterable: true,
|
||||
|
||||
}
|
||||
},
|
||||
helper: {
|
||||
render (h) {
|
||||
return (< el-alert title="手动输入" type="warning" description="页面中按钮的名称或者自定义一个名称"/>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
value: {
|
||||
title: '权限值',
|
||||
type: 'text',
|
||||
search: {show: false},
|
||||
column: {
|
||||
width: 120,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{required: true, message: '权限标识必填'}],
|
||||
placeholder: '输入权限标识',
|
||||
helper: {
|
||||
render (h) {
|
||||
return (< el-alert title="唯一值" type="warning" description="用于判断前端按钮权限或接口权限"/>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
method: {
|
||||
title: '请求方式',
|
||||
search: {show: false},
|
||||
type: 'dict-select',
|
||||
column: {
|
||||
width: 120,
|
||||
sortable: true,
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{label: 'GET', value: 0},
|
||||
{label: 'POST', value: 1, color: 'success'},
|
||||
{label: 'PUT', value: 2, color: 'warning'},
|
||||
{label: 'DELETE', value: 3, color: 'danger'}
|
||||
]
|
||||
}),
|
||||
form:{
|
||||
rules: [{required: true, message: '必填项'}],
|
||||
}
|
||||
},
|
||||
api: {
|
||||
title: '接口地址',
|
||||
search: {show: false},
|
||||
type: 'dict-select',
|
||||
dict: dict({
|
||||
getData() {
|
||||
return request({url: '/swagger.json'}).then((res: any) => {
|
||||
const ret = Object.keys(res.paths)
|
||||
const data = []
|
||||
for (const item of ret) {
|
||||
const obj: any = {}
|
||||
obj.label = item
|
||||
obj.value = item
|
||||
data.push(obj)
|
||||
}
|
||||
return data
|
||||
})
|
||||
}
|
||||
}),
|
||||
column: {
|
||||
minWidth: 200,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{required: true, message: '必填项'}],
|
||||
component:{
|
||||
props:{
|
||||
allowCreate: true,
|
||||
filterable: true,
|
||||
clearable: true
|
||||
}
|
||||
},
|
||||
helper: {
|
||||
render (h) {
|
||||
return (< el-alert title="请正确填写,以免请求时被拦截。匹配单例使用正则,例如:/api/xx/.*?/" type="warning" />
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,69 +1,26 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
size="70%"
|
||||
v-model="drawer"
|
||||
direction="rtl"
|
||||
destroy-on-close
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header>
|
||||
<div>当前菜单:
|
||||
<el-tag>{{ selectOptions.name }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<fs-page style="margin-top: 60px;">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
|
||||
</div>
|
||||
</el-drawer>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, onMounted} from 'vue';
|
||||
import {useExpose, useCrud} from '@fast-crud/fast-crud';
|
||||
import {createCrudOptions} from './curd';
|
||||
import {ElMessageBox} from "element-plus";
|
||||
import * as api from './api'
|
||||
// 弹窗是否显示
|
||||
const drawer = ref(false)
|
||||
import { ref, defineProps, watch } from 'vue';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
// 当前选择的菜单信息
|
||||
const selectOptions:any = ref({name:null})
|
||||
let selectOptions: any = ref({ name: null });
|
||||
const props = defineProps<{
|
||||
selectMenu: object;
|
||||
}>();
|
||||
|
||||
//抽屉关闭确认
|
||||
const handleClose = (done: () => void) => {
|
||||
ElMessageBox.confirm('您确定要关闭?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
done()
|
||||
})
|
||||
.catch(() => {
|
||||
// catch error
|
||||
})
|
||||
}
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const {crudExpose} = useExpose({crudRef, crudBinding});
|
||||
// 你的crud配置
|
||||
const {crudOptions} = createCrudOptions({crudExpose, selectOptions});
|
||||
// 初始化crud配置
|
||||
const {resetCrudOptions} = useCrud({crudExpose, crudOptions});
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
const initGet = ()=>{
|
||||
api.GetList({menu:selectOptions.value.id}).then((res:any)=>{
|
||||
const {data} = res
|
||||
crudExpose.crudBinding.value.data=data
|
||||
})
|
||||
}
|
||||
watch(props.selectMenu, (val: any) => {
|
||||
if (!val.is_catalog) {
|
||||
selectOptions.value = val;
|
||||
doRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({drawer, selectOptions,initGet})
|
||||
const { crudRef, crudBinding, crudExpose, context } = useFs({ createCrudOptions, context: { selectOptions } });
|
||||
const { doRefresh } = crudExpose;
|
||||
|
||||
defineExpose({ selectOptions });
|
||||
</script>
|
||||
|
||||
@@ -1,37 +1,8 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
import { eIconPicker, eIcon } from 'e-icon-picker';
|
||||
import { useCompute } from '@fast-crud/fast-crud';
|
||||
import { inject,computed } from 'vue';
|
||||
import { apiPrefix as menuPrefix } from './api';
|
||||
import XEUtils from 'xe-utils';
|
||||
import { request } from '/@/utils/service';
|
||||
const { compute } = useCompute();
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from '@fast-crud/fast-crud';
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose, menuButtonRef }: { crudExpose: CrudExpose; menuButtonRef: any }): CreateCrudOptionsTypes {
|
||||
const hasPermissions: any = inject('$hasPermissions');
|
||||
//验证路由地址
|
||||
const validateWebPath = (rule: string, value: string, callback: Function) => {
|
||||
const isLink = JSON.parse(crudExpose.getFormData().is_link);
|
||||
let pattern = /^\/.*?/;
|
||||
if (isLink) {
|
||||
pattern = /^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+/g;
|
||||
} else {
|
||||
pattern = /^\/.*?/;
|
||||
}
|
||||
// console.log('isLink', isLink, 'pattern', pattern, pattern.test(value));
|
||||
if (!pattern.test(value)) {
|
||||
callback(new Error('请正确的地址'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -41,21 +12,11 @@ export const createCrudOptions = function ({ crudExpose, menuButtonRef }: { crud
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
/**
|
||||
* 懒加载
|
||||
* @param row
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
const loadContentMethod = (tree: any, treeNode: any, resolve: any) => {
|
||||
api.GetList({ parent: tree.id }).then((res: any) => {
|
||||
resolve(res.data);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
@@ -64,404 +25,7 @@ export const createCrudOptions = function ({ crudExpose, menuButtonRef }: { crud
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
pagination: {
|
||||
show: false,
|
||||
},
|
||||
table: {
|
||||
rowKey: 'id',
|
||||
lazy: true,
|
||||
load: loadContentMethod,
|
||||
treeProps: { children: 'children', hasChildren: 'hasChild' },
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
add: {
|
||||
show: hasPermissions('menu:Create'),
|
||||
},
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right",
|
||||
width: 310,
|
||||
buttons: {
|
||||
custom: {
|
||||
text: '按钮配置',
|
||||
type: 'warning',
|
||||
tooltip: {
|
||||
placement: 'top',
|
||||
content: '按钮配置',
|
||||
},
|
||||
show: compute(({ row }: any) => {
|
||||
if (row.is_catalog) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
click: (context: any): void => {
|
||||
const { row } = context;
|
||||
menuButtonRef.value.drawer = true;
|
||||
menuButtonRef.value.selectOptions = row;
|
||||
menuButtonRef.value.initGet();
|
||||
},
|
||||
},
|
||||
addChildren:{
|
||||
text: "添加子级",
|
||||
type:"warning",
|
||||
click(context){
|
||||
const rowId =context.row.id
|
||||
crudExpose.openAdd({ row: { parent: rowId } })
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: { show: false },
|
||||
column: {
|
||||
type: 'index',
|
||||
align: 'center',
|
||||
width: '80px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
formatter: (context) => {
|
||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||
let index = context.index ?? 1;
|
||||
let pagination: any = crudExpose.crudBinding.value.pagination;
|
||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||
},
|
||||
},
|
||||
},
|
||||
search: {
|
||||
title: '关键词',
|
||||
column: {
|
||||
show: false,
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: {
|
||||
props: {
|
||||
clearable: true,
|
||||
},
|
||||
placeholder: '请输入关键词',
|
||||
},
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
props: {
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: {
|
||||
title: '菜单名称',
|
||||
search: {
|
||||
show: true,
|
||||
component: {
|
||||
props: {
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'input',
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{ required: true, message: '菜单名称必填项' },
|
||||
],
|
||||
component: {
|
||||
props: {
|
||||
clearable: true,
|
||||
},
|
||||
placeholder: '请输入菜单名称',
|
||||
},
|
||||
},
|
||||
column: {
|
||||
width: 180,
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
title: '图标',
|
||||
form: {
|
||||
component: {
|
||||
name: eIconPicker,
|
||||
vModel: 'modelValue',
|
||||
},
|
||||
},
|
||||
column: {
|
||||
component: {
|
||||
name: eIcon,
|
||||
vModel: 'modelValue',
|
||||
style: 'font-size:18px',
|
||||
},
|
||||
},
|
||||
},
|
||||
sort: {
|
||||
title: '排序',
|
||||
column: {
|
||||
width: 60,
|
||||
},
|
||||
type: 'number',
|
||||
form: {
|
||||
value: 1,
|
||||
component: {
|
||||
placeholder: '请输入排序',
|
||||
},
|
||||
},
|
||||
},
|
||||
is_catalog: {
|
||||
title: '是否目录',
|
||||
column: {
|
||||
width: 100,
|
||||
},
|
||||
type: 'dict-switch',
|
||||
dict: dict({
|
||||
data: dictionary('button_whether_bool'),
|
||||
}),
|
||||
form: {
|
||||
component: {
|
||||
placeholder: '请选择是否目录',
|
||||
},
|
||||
// valueChange(key, value, form, { getColumn, mode, component, immediate, getComponent }) {
|
||||
// if (!value) {
|
||||
// form.web_path = undefined
|
||||
// form.component = undefined
|
||||
// form.component_name = undefined
|
||||
// form.cache = false
|
||||
// form.is_link = false
|
||||
// }
|
||||
// }
|
||||
},
|
||||
},
|
||||
is_link: {
|
||||
title: '外链接',
|
||||
type: 'dict-switch',
|
||||
dict: dict({
|
||||
data: dictionary('button_whether_bool'),
|
||||
}),
|
||||
form: {
|
||||
value: false,
|
||||
component: {
|
||||
/* show(context) {
|
||||
const { form } = context
|
||||
return !form.is_catalog
|
||||
}, */
|
||||
placeholder: '请选择是否外链接',
|
||||
},
|
||||
/* valueChange(key, value, form, { getColumn, mode, component, immediate, getComponent }) {
|
||||
form.web_path = undefined
|
||||
form.component = undefined
|
||||
form.component_name = undefined
|
||||
if (value) {
|
||||
getColumn('web_path').title = '外链接地址'
|
||||
getColumn('web_path').component.placeholder = '请输入外链接地址'
|
||||
getColumn('web_path').helper = {
|
||||
render(h) {
|
||||
return (< el-alert title="外链接地址,请以https|http|ftp|rtsp|mms开头" type="warning" />
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getColumn('web_path').title = '路由地址'
|
||||
getColumn('web_path').component.placeholder = '请输入路由地址'
|
||||
getColumn('web_path').helper = {
|
||||
render(h) {
|
||||
return (< el-alert title="浏览器中url的地址,请以/开头" type="warning" />
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
},
|
||||
},
|
||||
web_path: {
|
||||
title: '路由地址',
|
||||
width: 150,
|
||||
column: {
|
||||
show: false,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
{ required: true, message: '请输入正确的路由地址' },
|
||||
{ validator: validateWebPath, trigger: 'change' },
|
||||
],
|
||||
component: {
|
||||
show(context: any) {
|
||||
const { form } = context;
|
||||
return !form.is_catalog;
|
||||
},
|
||||
props: {
|
||||
clearable: true,
|
||||
},
|
||||
placeholder: '请输入路由地址',
|
||||
},
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="浏览器中url的地址,请以/开头" type="warning" />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: {
|
||||
title: '组件地址',
|
||||
type: 'dict-select',
|
||||
show: false,
|
||||
dict: dict({
|
||||
getData: () => {
|
||||
const files: any = import.meta.globEager('@views/**/*.vue');
|
||||
let result: Array<any> = [];
|
||||
Object.keys(files).forEach((key: string) => {
|
||||
result.push({
|
||||
label: key.replace(/(\.\/|\.vue)/g, ''),
|
||||
value: key.replace(/(\.\/|\.vue)/g, ''),
|
||||
});
|
||||
});
|
||||
return result;
|
||||
},
|
||||
}),
|
||||
column: {
|
||||
show: false,
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '请选择组件地址' }],
|
||||
component: {
|
||||
props: {
|
||||
clearable: true,
|
||||
filterable: true, // 可过滤选择项
|
||||
},
|
||||
placeholder: '请输入组件地址',
|
||||
},
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="src/views下的文件夹地址" type="warning" />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
component_name: {
|
||||
title: '组件名称',
|
||||
column: {
|
||||
width: 140,
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '请输入组件名称' }],
|
||||
component: {
|
||||
show(context: any) {
|
||||
const { form } = context;
|
||||
return !form.is_catalog && !form.is_link;
|
||||
},
|
||||
props: {
|
||||
clearable: true,
|
||||
},
|
||||
placeholder: '请输入组件名称',
|
||||
},
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="xx.vue文件中的name" type="warning" />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
menuPermission: {
|
||||
title: '拥有权限',
|
||||
type: 'dict-select',
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
elProps: {
|
||||
// el-select的配置项,https://element.eleme.cn/#/zh-CN/component/select
|
||||
filterable: true,
|
||||
multiple: true,
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
column: {
|
||||
width: 260,
|
||||
},
|
||||
},
|
||||
cache: {
|
||||
title: '缓存',
|
||||
column: {
|
||||
width: 60,
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'dict-switch',
|
||||
dict: dict({
|
||||
data: dictionary('button_whether_bool'),
|
||||
}),
|
||||
form: {
|
||||
value: false,
|
||||
component: {
|
||||
// show(context) {
|
||||
// const { form } = context
|
||||
// return !form.is_catalog
|
||||
// },
|
||||
placeholder: '请选择是否缓存',
|
||||
},
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="是否开启页面缓存,需要组件名称和xx.vue页面的name一致" type="warning" />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
visible: {
|
||||
title: '侧边可见',
|
||||
column: {
|
||||
width: 100,
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'dict-switch',
|
||||
dict: dict({
|
||||
data: dictionary('button_whether_bool'),
|
||||
}),
|
||||
form: {
|
||||
value: true,
|
||||
component: {
|
||||
placeholder: '请选择侧边可见',
|
||||
},
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{ required: true, message: '侧边可见必填项' },
|
||||
],
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="是否显示在侧边菜单中" type="warning" />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
status: {
|
||||
title: '状态',
|
||||
sortable: true,
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
column: {
|
||||
width: 80,
|
||||
},
|
||||
type: 'dict-switch',
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool'),
|
||||
}),
|
||||
form: {
|
||||
value: true,
|
||||
component: {
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{ required: true, message: '状态必填项' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {},
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,30 +1,384 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
<MenuButton ref="menuButtonRef"></MenuButton>
|
||||
</fs-page>
|
||||
<el-row class="mx-2">
|
||||
<el-col :span="4" class="p-1">
|
||||
<el-card :body-style="{ height: '100%' }">
|
||||
<p class="font-mono font-black text-center text-xl pb-5">
|
||||
菜单列表
|
||||
<el-tooltip effect="dark" :content="content" placement="right">
|
||||
<el-icon> <QuestionFilled /> </el-icon>
|
||||
</el-tooltip>
|
||||
</p>
|
||||
<el-input v-model="filterText" :placeholder="placeholder" />
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
class="font-mono font-bold leading-6 text-7xl"
|
||||
:data="data"
|
||||
:props="treeProps"
|
||||
:filter-node-method="filterNode"
|
||||
:load="loadNode"
|
||||
:allow-drag="allowDrag"
|
||||
:allow-drop="allowDrop"
|
||||
@node-drop="nodeDrop"
|
||||
lazy
|
||||
icon="ArrowRightBold"
|
||||
:indent="12"
|
||||
draggable
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span v-if="data.status" class="text-center font-black text-xl"><SvgIcon :name="node.data.icon" /> {{ node.label }}</span>
|
||||
<span v-else class="text-center font-black text-xl text-red-700"><SvgIcon :name="node.data.icon" /> {{ node.label }}</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="20" class="p-1">
|
||||
<el-card :body-style="{ height: '100%' }">
|
||||
<el-form ref="formRef" :rules="rules" :model="form" label-width="80px" label-position="right">
|
||||
<el-alert :title="content" type="success" effect="dark" :closable="false" center />
|
||||
<el-divider>
|
||||
<strong>菜单配置</strong>
|
||||
</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="菜单ID" prop="id"> <el-input v-model="form.id" disabled /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="父级ID" prop="parent"> <el-input v-model="form.parent" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item required label="菜单名称" prop="name"> <el-input v-model="form.name" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="组件地址" prop="component">
|
||||
<el-autocomplete
|
||||
class="w-full"
|
||||
v-model="form.component"
|
||||
:fetch-suggestions="querySearch"
|
||||
:trigger-on-focus="false"
|
||||
clearable
|
||||
debounce="100"
|
||||
placeholder="输入组件地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item required label="Url" prop="web_path"> <el-input v-model="form.web_path" /> </el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="form.sort" controls-position="right" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio :label="true">启用</el-radio>
|
||||
<el-radio :label="false">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="20">
|
||||
<el-form-item label="图标" prop="icon">
|
||||
<IconSelector clearable v-model="form.icon" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col class="center">
|
||||
<el-divider>
|
||||
<el-button @click="saveMenu()" type="primary" round>保存</el-button>
|
||||
<el-button @click="newMenu()" type="success" round>新建</el-button>
|
||||
<el-button @click="addChildMenu()" type="info" round>添加子级</el-button>
|
||||
<el-button @click="addSameLevelMenu()" type="warning" round>添加同级</el-button>
|
||||
<el-button @click="deleteMenu()" type="danger" round>删除菜单</el-button>
|
||||
</el-divider>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col class="h-full">
|
||||
<el-card :body-style="{ height: '100%' }" class="mt-10"><menuButton :select-menu="form" /></el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
import MenuButton from './components/menuButton/index.vue'
|
||||
const menuButtonRef = ref()
|
||||
defineExpose(menuButtonRef);
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose,menuButtonRef });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
import * as api from './api';
|
||||
import * as menuButoonApi from './components/menuButton/api';
|
||||
import { ElForm, ElTree, FormRules } from 'element-plus';
|
||||
import { ref, onMounted, watch, reactive, toRaw, defineAsyncComponent, nextTick, shallowRef } from 'vue';
|
||||
import XEUtils from 'xe-utils';
|
||||
import { errorMessage, successMessage } from '../../../utils/message';
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
name: string;
|
||||
status: boolean;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
interface APIResponseData {
|
||||
code?: number;
|
||||
data: [];
|
||||
msg?: string;
|
||||
}
|
||||
|
||||
interface Form<T> {
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
interface ComponentFileItem {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
// 引入组件
|
||||
const menuButton = defineAsyncComponent(() => import('./components/menuButton/index.vue'));
|
||||
const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
|
||||
const SvgIcon = defineAsyncComponent(() => import('/@/components/svgIcon/index.vue'));
|
||||
const placeholder = ref('请输入菜单名称');
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
icon: 'icon',
|
||||
isLeaf: (data: Tree[], node: Node) => {
|
||||
// @ts-ignore
|
||||
if (node.data.is_catalog) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const validateWebPath = (rule: string, value: string, callback: Function) => {
|
||||
let pattern = /^\/.*?/;
|
||||
if (!pattern.test(value)) {
|
||||
callback(new Error('请输入正确的地址'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return toRaw(data).name.indexOf(value) !== -1;
|
||||
};
|
||||
|
||||
// 懒加载
|
||||
const loadNode = (node: Node, resolve: (data: Tree[]) => void) => {
|
||||
// @ts-ignore
|
||||
if (node.level !== 0) {
|
||||
// @ts-ignore
|
||||
api.lazyLoadMenu({ parent: node.data.id }).then((res: APIResponseData) => {
|
||||
resolve(res.data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 判断是否可以拖动
|
||||
const allowDrag = (node: Node) => {
|
||||
// @ts-ignore
|
||||
if (node.data.is_catalog) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// 判断是否可以被放置
|
||||
const allowDrop = (draggingNode: Node, dropNode: Node, type: string) => {
|
||||
// @ts-ignore
|
||||
if (!dropNode.isLeaf) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const nodeDrop = (draggingNode: Node, dropNode: Node, dropType: string, event: any) => {
|
||||
// @ts-ignore
|
||||
if (!dropNode.isLeaf) {
|
||||
// @ts-ignore
|
||||
api.dragMenu({ menu_id: draggingNode.data.id, parent_id: dropNode.data.id }).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let data = ref([]);
|
||||
|
||||
let isAddNewMenu = ref(false); // 判断当前是新增菜单,还是更新保存当前菜单
|
||||
|
||||
const permissionDrawerVisible = ref(false);
|
||||
|
||||
const content = `
|
||||
1.红色菜单代表状态禁用;
|
||||
2.添加菜单,如果是目录,组件地址为空即可;
|
||||
3.添加根节点菜单,父级ID为空即可;
|
||||
4.支持拖拽菜单;
|
||||
`;
|
||||
|
||||
let form: Form<any> = reactive({
|
||||
id: '',
|
||||
parent: '',
|
||||
name: '',
|
||||
component: '',
|
||||
web_path: '',
|
||||
sort: '',
|
||||
status: '',
|
||||
is_catalog: false,
|
||||
permission: '',
|
||||
icon: '',
|
||||
});
|
||||
|
||||
let menuPermissonList = ref([]);
|
||||
|
||||
const formRef = ref<InstanceType<typeof ElForm>>();
|
||||
|
||||
const querySearch = (queryString: string, cb: any) => {
|
||||
const files: any = import.meta.glob('@views/**/*.vue');
|
||||
let fileLists: Array<any> = [];
|
||||
Object.keys(files).forEach((queryString: string) => {
|
||||
fileLists.push({
|
||||
label: queryString.replace(/(\.\/|\.vue)/g, ''),
|
||||
value: queryString.replace(/(\.\/|\.vue)/g, ''),
|
||||
});
|
||||
});
|
||||
const results = queryString ? fileLists.filter(createFilter(queryString)) : fileLists;
|
||||
// 统一去掉/src/views/前缀
|
||||
results.forEach((val) => {
|
||||
val.label = val.label.replace('/src/views/', '');
|
||||
val.value = val.value.replace('/src/views/', '');
|
||||
});
|
||||
cb(results);
|
||||
};
|
||||
|
||||
const createFilter = (queryString: string) => {
|
||||
return (file: ComponentFileItem) => {
|
||||
return file.value.toLowerCase().indexOf(queryString.toLowerCase()) !== -1;
|
||||
};
|
||||
};
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
// @ts-ignore
|
||||
web_path: [{ validator: validateWebPath, trigger: 'blur' }],
|
||||
});
|
||||
|
||||
const getData = () => {
|
||||
api.GetList({}).then((ret: APIResponseData) => {
|
||||
const responseData = ret.data;
|
||||
const result = XEUtils.toArrayTree(responseData, {
|
||||
parentKey: 'parent',
|
||||
children: 'children',
|
||||
strict: true,
|
||||
});
|
||||
data.value = result;
|
||||
});
|
||||
};
|
||||
|
||||
const getPermissions = (menu: object) => {
|
||||
menuButoonApi.GetList(menu).then((res: APIResponseData) => {
|
||||
menuPermissonList.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
const saveMenu = () => {
|
||||
formRef.value?.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
if (!isAddNewMenu.value) {
|
||||
// 保存菜单
|
||||
|
||||
form.component == '' ? (form.is_catalog = true) : (form.is_catalog = false);
|
||||
api.UpdateObj(form).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
getData();
|
||||
});
|
||||
} else {
|
||||
// 新增菜单
|
||||
form.component == '' ? (form.is_catalog = true) : (form.is_catalog = false);
|
||||
api.AddObj(form).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
getData();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
errorMessage('请填写检查表单');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const newMenu = () => {
|
||||
formRef.value?.resetFields();
|
||||
isAddNewMenu.value = true;
|
||||
};
|
||||
|
||||
const addChildMenu = () => {
|
||||
let parentId = form.id;
|
||||
formRef.value?.resetFields();
|
||||
form.parent = parentId;
|
||||
isAddNewMenu.value = true;
|
||||
};
|
||||
|
||||
const addSameLevelMenu = () => {
|
||||
let parentId = form.parent;
|
||||
formRef.value?.resetFields();
|
||||
form.parent = parentId;
|
||||
isAddNewMenu.value = true;
|
||||
};
|
||||
|
||||
const deleteMenu = () => {
|
||||
api.DelObj(form).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
getData();
|
||||
});
|
||||
};
|
||||
|
||||
const handleNodeClick = (data: any, node: any, prop: any) => {
|
||||
Object.keys(toRaw(data)).forEach((key: string) => {
|
||||
form[key] = data[key];
|
||||
});
|
||||
delete form.component_name;
|
||||
form.id = data.id;
|
||||
isAddNewMenu.value = false;
|
||||
|
||||
// 点击tree node时,加载对应的权限菜单
|
||||
getPermissions({ menu: form.id });
|
||||
};
|
||||
|
||||
const addPermission = () => {
|
||||
!form.is_catalog ? (permissionDrawerVisible.value = true) : errorMessage('目录没有菜单权限');
|
||||
};
|
||||
const drawerClose = () => {
|
||||
permissionDrawerVisible.value = false;
|
||||
};
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-row {
|
||||
height: 100%;
|
||||
|
||||
.el-col {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.el-card {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,331 +1,363 @@
|
||||
import * as api from "./api";
|
||||
import {dict, useCompute, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions,} from "@fast-crud/fast-crud";
|
||||
import tableSelector from "/@/components/tableSelector/index.vue"
|
||||
import {shallowRef, computed, ref} from "vue";
|
||||
import manyToMany from "/@/components/manyToMany/index.vue"
|
||||
import * as api from './api';
|
||||
import { dict, useCompute, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import tableSelector from '/@/components/tableSelector/index.vue';
|
||||
import {shallowRef, computed, ref, inject} from 'vue';
|
||||
import manyToMany from '/@/components/manyToMany/index.vue';
|
||||
|
||||
const {compute} = useCompute()
|
||||
const { compute } = useCompute();
|
||||
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose, tabActivted }: { crudExpose: CrudExpose; tabActivted: any }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
if (tabActivted.value === 'receive') {
|
||||
return await api.GetSelfReceive(query);
|
||||
}
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
export const createCrudOptions = function ({
|
||||
crudExpose,
|
||||
tabActivted
|
||||
}: { crudExpose: CrudExpose, tabActivted: any }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
if (tabActivted.value === 'receive') {
|
||||
return await api.GetSelfReceive(query);
|
||||
}
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({form, row}: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({row}: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({form}: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
const viewRequest = async ({ row }: { row: any }) => {
|
||||
return await api.GetObj(row.id);
|
||||
};
|
||||
|
||||
const viewRequest = async ({row}: { row: any }) => {
|
||||
return await api.GetObj(row.id);
|
||||
}
|
||||
const IsReadFunc = computed(() => {
|
||||
return tabActivted.value === 'receive';
|
||||
});
|
||||
|
||||
const IsReadFunc = computed(() => {
|
||||
return tabActivted.value === 'receive'
|
||||
})
|
||||
//权限判定
|
||||
const hasPermissions = inject("$hasPermissions")
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
rowHandle: {
|
||||
buttons: {
|
||||
edit: {
|
||||
show: false
|
||||
},
|
||||
view: {
|
||||
click({index,row}) {
|
||||
crudExpose.openView({index,row})
|
||||
if(tabActivted.value === 'receive'){
|
||||
viewRequest({row})
|
||||
crudExpose.doRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: 'id',
|
||||
form: {
|
||||
show: false,
|
||||
}
|
||||
},
|
||||
title: {
|
||||
title: '标题',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: ["text", "colspan"],
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
],
|
||||
component: {span: 24, placeholder: '请输入标题'}
|
||||
}
|
||||
},
|
||||
is_read: {
|
||||
title: '是否已读',
|
||||
type: 'dict-select',
|
||||
column: {
|
||||
show: IsReadFunc,
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{label: '已读', value: true, color: 'success'},
|
||||
{label: '未读', value: false, color: 'danger'}
|
||||
]
|
||||
}),
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
target_type: {
|
||||
title: '目标类型',
|
||||
type: ['dict-radio', 'colspan'],
|
||||
width: 120,
|
||||
dict: dict({
|
||||
data: [{value: 0, label: '按用户'}, {value: 1, label: '按角色'}, {
|
||||
value: 2,
|
||||
label: '按部门'
|
||||
}, {value: 3, label: '通知公告'}]
|
||||
}),
|
||||
form: {
|
||||
component: {
|
||||
optionName: "el-radio-button"
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '必选项',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
target_user: {
|
||||
title: '目标用户',
|
||||
search: {
|
||||
disabled: true
|
||||
},
|
||||
width: 130,
|
||||
form: {
|
||||
component: {
|
||||
name: shallowRef(tableSelector),
|
||||
vModel: "modelValue",
|
||||
displayLabel: compute(({row}) => {
|
||||
if (row) {
|
||||
return row.user_info;
|
||||
}
|
||||
return null
|
||||
}),
|
||||
tableConfig: {
|
||||
url: '/api/system/user/',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
isMultiple: true,
|
||||
columns: [{
|
||||
prop: 'name',
|
||||
label: '用户名称',
|
||||
width: 120
|
||||
}, {
|
||||
prop: 'phone',
|
||||
label: '用户电话',
|
||||
width: 120
|
||||
}]
|
||||
}
|
||||
},
|
||||
show: compute(({form}) => {
|
||||
return form.target_type === 0
|
||||
}),
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
]
|
||||
},
|
||||
column: {
|
||||
show: false,
|
||||
component: {
|
||||
name: shallowRef(manyToMany),
|
||||
vModel: "modelValue",
|
||||
bindValue: compute(({row}) => {
|
||||
return row.user_info
|
||||
}),
|
||||
displayLabel: 'name'
|
||||
}
|
||||
}
|
||||
},
|
||||
target_role: {
|
||||
title: '目标角色',
|
||||
search: {
|
||||
disabled: true
|
||||
},
|
||||
width: 130,
|
||||
form: {
|
||||
component: {
|
||||
name: shallowRef(tableSelector),
|
||||
vModel: "modelValue",
|
||||
displayLabel: compute(({row}) => {
|
||||
if (row) {
|
||||
return row.role_info;
|
||||
}
|
||||
return null
|
||||
}),
|
||||
tableConfig: {
|
||||
url: '/api/system/role/',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
isMultiple: true,
|
||||
columns: [{
|
||||
prop: 'name',
|
||||
label: '角色名称'
|
||||
},
|
||||
{
|
||||
prop: 'key',
|
||||
label: '权限标识'
|
||||
}]
|
||||
}
|
||||
},
|
||||
show: compute(({form}) => {
|
||||
return form.target_type === 1
|
||||
}),
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
]
|
||||
},
|
||||
column: {
|
||||
show: false,
|
||||
component: {
|
||||
name: shallowRef(manyToMany),
|
||||
vModel: "modelValue",
|
||||
bindValue: compute(({row}) => {
|
||||
return row.role_info
|
||||
}),
|
||||
displayLabel: 'name'
|
||||
}
|
||||
}
|
||||
},
|
||||
target_dept: {
|
||||
title: '目标部门',
|
||||
search: {
|
||||
disabled: true
|
||||
},
|
||||
width: 130,
|
||||
type: 'table-selector',
|
||||
form: {
|
||||
component: {
|
||||
name: shallowRef(tableSelector),
|
||||
vModel: "modelValue",
|
||||
displayLabel: compute(({form}) => {
|
||||
return form.target_dept_name;
|
||||
}),
|
||||
tableConfig: {
|
||||
url: '/api/system/dept/all_dept/',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
isTree: true,
|
||||
isMultiple: true,
|
||||
columns: [{
|
||||
prop: 'name',
|
||||
label: '部门名称'
|
||||
},
|
||||
{
|
||||
prop: 'status_label',
|
||||
label: '状态'
|
||||
},
|
||||
{
|
||||
prop: 'parent_name',
|
||||
label: '父级部门'
|
||||
}]
|
||||
}
|
||||
},
|
||||
show: compute(({form}) => {
|
||||
return form.target_type === 2
|
||||
}),
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
]
|
||||
},
|
||||
column: {
|
||||
show: false,
|
||||
component: {
|
||||
name: shallowRef(manyToMany),
|
||||
vModel: "modelValue",
|
||||
bindValue: compute(({row}) => {
|
||||
return row.dept_info
|
||||
}),
|
||||
displayLabel: 'name'
|
||||
}
|
||||
}
|
||||
},
|
||||
content: {
|
||||
title: '内容',
|
||||
column: {
|
||||
width: 300,
|
||||
show: false
|
||||
},
|
||||
type: ["editor-wang5", "colspan"],
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
],
|
||||
component: {
|
||||
disabled: true,
|
||||
id: "1", // 当同一个页面有多个editor时,需要配置不同的id
|
||||
editorConfig: {
|
||||
// 是否只读
|
||||
readOnly: compute((context) => {
|
||||
const {mode} = context
|
||||
if (mode === 'add') {
|
||||
return false
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
},
|
||||
uploader: {
|
||||
type: "form",
|
||||
buildUrl(res: any) {
|
||||
return res.url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
fixed:'right',
|
||||
width:150,
|
||||
buttons: {
|
||||
edit: {
|
||||
show: false,
|
||||
},
|
||||
view: {
|
||||
text:"查看",
|
||||
type:'text',
|
||||
iconRight:'View',
|
||||
show:hasPermissions("messageCenter:Search"),
|
||||
click({ index, row }) {
|
||||
crudExpose.openView({ index, row });
|
||||
if (tabActivted.value === 'receive') {
|
||||
viewRequest({ row });
|
||||
crudExpose.doRefresh();
|
||||
}
|
||||
},
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
show:hasPermissions('messageCenter:Delete')
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: 'id',
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
title: '标题',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: ['text', 'colspan'],
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项',
|
||||
},
|
||||
],
|
||||
component: { span: 24, placeholder: '请输入标题' },
|
||||
},
|
||||
},
|
||||
is_read: {
|
||||
title: '是否已读',
|
||||
type: 'dict-select',
|
||||
column: {
|
||||
show: IsReadFunc.value,
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{ label: '已读', value: true, color: 'success' },
|
||||
{ label: '未读', value: false, color: 'danger' },
|
||||
],
|
||||
}),
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
target_type: {
|
||||
title: '目标类型',
|
||||
type: ['dict-radio', 'colspan'],
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: 0, label: '按用户' },
|
||||
{ value: 1, label: '按角色' },
|
||||
{
|
||||
value: 2,
|
||||
label: '按部门',
|
||||
},
|
||||
{ value: 3, label: '通知公告' },
|
||||
],
|
||||
}),
|
||||
form: {
|
||||
component: {
|
||||
optionName: 'el-radio-button',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '必选项',
|
||||
// @ts-ignore
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
target_user: {
|
||||
title: '目标用户',
|
||||
search: {
|
||||
disabled: true,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
name: shallowRef(tableSelector),
|
||||
vModel: 'modelValue',
|
||||
displayLabel: compute(({ row }) => {
|
||||
if (row) {
|
||||
return row.user_info;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
tableConfig: {
|
||||
url: '/api/system/user/',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
isMultiple: true,
|
||||
columns: [
|
||||
{
|
||||
prop: 'name',
|
||||
label: '用户名称',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
prop: 'phone',
|
||||
label: '用户电话',
|
||||
width: 120,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
show: compute(({ form }) => {
|
||||
return form.target_type === 0;
|
||||
}),
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项',
|
||||
},
|
||||
],
|
||||
},
|
||||
column: {
|
||||
show: false,
|
||||
component: {
|
||||
name: shallowRef(manyToMany),
|
||||
vModel: 'modelValue',
|
||||
bindValue: compute(({ row }) => {
|
||||
return row.user_info;
|
||||
}),
|
||||
displayLabel: 'name',
|
||||
},
|
||||
},
|
||||
},
|
||||
target_role: {
|
||||
title: '目标角色',
|
||||
search: {
|
||||
disabled: true,
|
||||
},
|
||||
width: 130,
|
||||
form: {
|
||||
component: {
|
||||
name: shallowRef(tableSelector),
|
||||
vModel: 'modelValue',
|
||||
displayLabel: compute(({ row }) => {
|
||||
if (row) {
|
||||
return row.role_info;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
tableConfig: {
|
||||
url: '/api/system/role/',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
isMultiple: true,
|
||||
columns: [
|
||||
{
|
||||
prop: 'name',
|
||||
label: '角色名称',
|
||||
},
|
||||
{
|
||||
prop: 'key',
|
||||
label: '权限标识',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
show: compute(({ form }) => {
|
||||
return form.target_type === 1;
|
||||
}),
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项',
|
||||
},
|
||||
],
|
||||
},
|
||||
column: {
|
||||
show: false,
|
||||
component: {
|
||||
name: shallowRef(manyToMany),
|
||||
vModel: 'modelValue',
|
||||
bindValue: compute(({ row }) => {
|
||||
return row.role_info;
|
||||
}),
|
||||
displayLabel: 'name',
|
||||
},
|
||||
},
|
||||
},
|
||||
target_dept: {
|
||||
title: '目标部门',
|
||||
search: {
|
||||
disabled: true,
|
||||
},
|
||||
width: 130,
|
||||
type: 'table-selector',
|
||||
form: {
|
||||
component: {
|
||||
name: shallowRef(tableSelector),
|
||||
vModel: 'modelValue',
|
||||
displayLabel: compute(({ form }) => {
|
||||
return form.target_dept_name;
|
||||
}),
|
||||
tableConfig: {
|
||||
url: '/api/system/dept/all_dept/',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
isTree: true,
|
||||
isMultiple: true,
|
||||
columns: [
|
||||
{
|
||||
prop: 'name',
|
||||
label: '部门名称',
|
||||
},
|
||||
{
|
||||
prop: 'status_label',
|
||||
label: '状态',
|
||||
},
|
||||
{
|
||||
prop: 'parent_name',
|
||||
label: '父级部门',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
show: compute(({ form }) => {
|
||||
return form.target_type === 2;
|
||||
}),
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项',
|
||||
},
|
||||
],
|
||||
},
|
||||
column: {
|
||||
show: false,
|
||||
component: {
|
||||
name: shallowRef(manyToMany),
|
||||
vModel: 'modelValue',
|
||||
bindValue: compute(({ row }) => {
|
||||
return row.dept_info;
|
||||
}),
|
||||
displayLabel: 'name',
|
||||
},
|
||||
},
|
||||
},
|
||||
content: {
|
||||
title: '内容',
|
||||
column: {
|
||||
width: 300,
|
||||
show: false,
|
||||
},
|
||||
type: ['editor-wang5', 'colspan'],
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项',
|
||||
},
|
||||
],
|
||||
component: {
|
||||
disabled: true,
|
||||
id: '1', // 当同一个页面有多个editor时,需要配置不同的id
|
||||
editorConfig: {
|
||||
// 是否只读
|
||||
readOnly: compute((context) => {
|
||||
const { mode } = context;
|
||||
if (mode === 'add') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
},
|
||||
uploader: {
|
||||
type: 'form',
|
||||
buildUrl(res: any) {
|
||||
return res.url;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,40 +1,38 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import {apiPrefix} from "/@/views/system/messageCenter/api";
|
||||
import { apiPrefix } from '/@/views/system/messageCenter/api';
|
||||
export function GetUserInfo(query: PageQuery) {
|
||||
return request({
|
||||
url: '/api/system/user/user_info/',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
return request({
|
||||
url: '/api/system/user/user_info/',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
* @param data
|
||||
*/
|
||||
export function updateUserInfo(data: AddReq) {
|
||||
return request({
|
||||
url: '/api/system/user/update_user_info/',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
return request({
|
||||
url: '/api/system/user/update_user_info/',
|
||||
method: 'put',
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取自己接收的消息
|
||||
* @param query
|
||||
* @returns {*}
|
||||
* @constructor
|
||||
*/
|
||||
export function GetSelfReceive (query:PageQuery) {
|
||||
return request({
|
||||
url: '/api/system/message_center/get_self_receive/',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
export function GetSelfReceive(query: PageQuery) {
|
||||
return request({
|
||||
url: '/api/system/message_center/get_self_receive/',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
/***
|
||||
@@ -42,9 +40,24 @@ export function GetSelfReceive (query:PageQuery) {
|
||||
* @param data
|
||||
*/
|
||||
export function UpdatePassword(data: EditReq) {
|
||||
return request({
|
||||
url: '/api/system/user/change_password/',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
return request({
|
||||
url: '/api/system/user/change_password/',
|
||||
method: 'put',
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/***
|
||||
* 上传头像
|
||||
* @param data
|
||||
*/
|
||||
export function uploadAvatar(data: AddReq) {
|
||||
return request({
|
||||
url: 'api/system/file/',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,524 +1,522 @@
|
||||
<template>
|
||||
<div class="personal layout-pd">
|
||||
<el-row>
|
||||
<!-- 个人信息 -->
|
||||
<el-col :xs="24" :sm="16">
|
||||
<el-card shadow="hover" header="个人信息">
|
||||
<div class="personal-user">
|
||||
<div class="personal-user-left">
|
||||
<el-upload class="h100 personal-user-left-upload" :action="uploadAvatar.action" :headers="uploadAvatar.headers" multiple :limit="1">
|
||||
<img v-if="state.personalForm.avatar" :src="state.personalForm.avatar" />
|
||||
<img v-else src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
|
||||
</el-upload>
|
||||
</div>
|
||||
<div class="personal-user-right">
|
||||
<el-row>
|
||||
<el-col :span="24" class="personal-title mb18">{{ currentTime }},{{state.personalForm.username}},生活变的再糟糕,也不妨碍我变得更好! </el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="8" class="personal-item mb6">
|
||||
<div class="personal-item-label">昵称:</div>
|
||||
<div class="personal-item-value">{{state.personalForm.name}}</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="16" class="personal-item mb6">
|
||||
<div class="personal-item-label">部门:</div>
|
||||
<div class="personal-item-value">
|
||||
<el-tag >{{state.personalForm.dept_info.dept_name}}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="24" class="personal-item mb6">
|
||||
<div class="personal-item-label">角色:</div>
|
||||
<div class="personal-item-value">
|
||||
<el-tag v-for="(item,index) in state.personalForm.role_info" :key="index">{{item.name}}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<div class="personal layout-pd">
|
||||
<el-row>
|
||||
<!-- 个人信息 -->
|
||||
<el-col :xs="24" :sm="16">
|
||||
<el-card shadow="hover" header="个人信息">
|
||||
<div class="personal-user">
|
||||
<div class="personal-user-left">
|
||||
<avatarSelector v-model="selectImgVisible" @uploadImg="uploadImg" ref="avatarSelectorRef"></avatarSelector>
|
||||
</div>
|
||||
<div class="personal-user-right">
|
||||
<el-row>
|
||||
<el-col :span="24" class="personal-title mb18"
|
||||
>{{ currentTime }},{{ state.personalForm.username }},生活变的再糟糕,也不妨碍我变得更好!
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="8" class="personal-item mb6">
|
||||
<div class="personal-item-label">昵称:</div>
|
||||
<div class="personal-item-value">{{ state.personalForm.name }}</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="16" class="personal-item mb6">
|
||||
<div class="personal-item-label">部门:</div>
|
||||
<div class="personal-item-value">
|
||||
<el-tag>{{ state.personalForm.dept_info.dept_name }}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="24" class="personal-item mb6">
|
||||
<div class="personal-item-label">角色:</div>
|
||||
<div class="personal-item-value">
|
||||
<el-tag v-for="(item, index) in state.personalForm.role_info" :key="index">{{ item.name }}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 消息通知 -->
|
||||
<el-col :xs="24" :sm="8" class="pl15 personal-info">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<span>消息通知</span>
|
||||
<span class="personal-info-more" @click="msgMore">更多</span>
|
||||
</template>
|
||||
<div class="personal-info-box">
|
||||
<ul class="personal-info-ul">
|
||||
<li v-for="(v, k) in state.newsInfoList" :key="k" class="personal-info-li">
|
||||
<div class="personal-info-li-title">
|
||||
[{{v.creator_name}},{{v.create_datetime}}] {{v.title}}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 消息通知 -->
|
||||
<el-col :xs="24" :sm="8" class="pl15 personal-info">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<span>消息通知</span>
|
||||
<span class="personal-info-more" @click="msgMore">更多</span>
|
||||
</template>
|
||||
<div class="personal-info-box">
|
||||
<ul class="personal-info-ul">
|
||||
<li v-for="(v, k) in state.newsInfoList" :key="k" class="personal-info-li">
|
||||
<div class="personal-info-li-title">[{{ v.creator_name }},{{ v.create_datetime }}] {{ v.title }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 更新信息 -->
|
||||
<el-col :span="24">
|
||||
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
|
||||
<div class="personal-edit-title">基本信息</div>
|
||||
<el-form :model="state.personalForm" ref="userInfoFormRef" :rules="rules" size="default" label-width="50px" class="mt35 mb35">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="昵称" prop="name">
|
||||
<el-input v-model="state.personalForm.name" placeholder="请输入昵称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="state.personalForm.email" placeholder="请输入邮箱" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input v-model="state.personalForm.mobile" placeholder="请输入手机" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="state.personalForm.gender" placeholder="请选择性别" clearable class="w100">
|
||||
<el-option label="男" :value="1"></el-option>
|
||||
<el-option label="女" :value="0"></el-option>
|
||||
<el-option label="保密" :value="2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">
|
||||
<el-icon>
|
||||
<ele-Position />
|
||||
</el-icon>
|
||||
更新个人信息
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="personal-edit-title mb15">账号安全</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">账户密码</div>
|
||||
<div class="personal-edit-safe-item-left-value">当前密码强度:强</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<el-button text type="primary" @click="passwordFormShow=true">立即修改</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">密保手机</div>
|
||||
<div class="personal-edit-safe-item-left-value">已绑定手机:{{state.personalForm.mobile}}</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<!-- <el-button text type="primary">立即修改</el-button>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 更新信息 -->
|
||||
<el-col :span="24">
|
||||
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
|
||||
<div class="personal-edit-title">基本信息</div>
|
||||
<el-form :model="state.personalForm" ref="userInfoFormRef" :rules="rules" size="default" label-width="50px" class="mt35 mb35">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="昵称" prop="name">
|
||||
<el-input v-model="state.personalForm.name" placeholder="请输入昵称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="state.personalForm.email" placeholder="请输入邮箱" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input v-model="state.personalForm.mobile" placeholder="请输入手机" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="state.personalForm.gender" placeholder="请选择性别" clearable class="w100">
|
||||
<el-option label="男" :value="1"></el-option>
|
||||
<el-option label="女" :value="0"></el-option>
|
||||
<el-option label="保密" :value="2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">
|
||||
<el-icon>
|
||||
<ele-Position />
|
||||
</el-icon>
|
||||
更新个人信息
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="personal-edit-title mb15">账号安全</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">账户密码</div>
|
||||
<div class="personal-edit-safe-item-left-value">当前密码强度:强</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<el-button text type="primary" @click="passwordFormShow = true">立即修改</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">密保手机</div>
|
||||
<div class="personal-edit-safe-item-left-value">已绑定手机:{{ state.personalForm.mobile }}</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<!-- <el-button text type="primary">立即修改</el-button>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">绑定邮箱</div>
|
||||
<div class="personal-edit-safe-item-left-value">已绑定邮箱:{{state.personalForm.email}}</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<!-- <el-button text type="primary">立即设置</el-button>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 密码修改-->
|
||||
<el-dialog v-model="passwordFormShow" title="密码修改">
|
||||
<el-form
|
||||
ref="userPasswordFormRef"
|
||||
:model="userPasswordInfo"
|
||||
required-asterisk
|
||||
label-width="100px"
|
||||
label-position="left"
|
||||
:rules="passwordRules"
|
||||
center
|
||||
>
|
||||
<el-form-item label="原密码" required prop="oldPassword">
|
||||
<el-input
|
||||
v-model="userPasswordInfo.oldPassword"
|
||||
placeholder="请输入原始密码"
|
||||
clearable
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item required prop="newPassword" label="新密码">
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="userPasswordInfo.newPassword"
|
||||
placeholder="请输入新密码"
|
||||
show-password
|
||||
clearable
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item required prop="newPassword2" label="确认密码">
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="userPasswordInfo.newPassword2"
|
||||
placeholder="请再次输入新密码"
|
||||
show-password
|
||||
clearable
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="settingPassword">
|
||||
<i class="fa fa-check"></i>提交
|
||||
</el-button>
|
||||
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">绑定邮箱</div>
|
||||
<div class="personal-edit-safe-item-left-value">已绑定邮箱:{{ state.personalForm.email }}</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<!-- <el-button text type="primary">立即设置</el-button>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 密码修改-->
|
||||
<el-dialog v-model="passwordFormShow" title="密码修改">
|
||||
<el-form
|
||||
ref="userPasswordFormRef"
|
||||
:model="userPasswordInfo"
|
||||
required-asterisk
|
||||
label-width="100px"
|
||||
label-position="left"
|
||||
:rules="passwordRules"
|
||||
center
|
||||
>
|
||||
<el-form-item label="原密码" required prop="oldPassword">
|
||||
<el-input v-model="userPasswordInfo.oldPassword" placeholder="请输入原始密码" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item required prop="newPassword" label="新密码">
|
||||
<el-input type="password" v-model="userPasswordInfo.newPassword" placeholder="请输入新密码" show-password clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item required prop="newPassword2" label="确认密码">
|
||||
<el-input type="password" v-model="userPasswordInfo.newPassword2" placeholder="请再次输入新密码" show-password clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="settingPassword"> <i class="fa fa-check"></i>提交 </el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="personal">
|
||||
import { reactive, computed,onMounted,ref } from 'vue';
|
||||
import { reactive, computed, onMounted, ref, defineAsyncComponent } from 'vue';
|
||||
import { formatAxis } from '/@/utils/formatTime';
|
||||
import * as api from './api'
|
||||
import {ElMessage } from "element-plus";
|
||||
import {getBaseURL} from "/@/utils/baseUrl";
|
||||
import * as api from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { getBaseURL } from '/@/utils/baseUrl';
|
||||
import { Session } from '/@/utils/storage';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useUserInfo } from '/@/stores/userInfo';
|
||||
import { successMessage } from '/@/utils/message';
|
||||
|
||||
// 头像裁剪组件
|
||||
const avatarSelector = defineAsyncComponent(() => import('/@/components/avatarSelector/index.vue'));
|
||||
const avatarSelectorRef = ref(null);
|
||||
// 当前时间提示语
|
||||
const currentTime = computed(() => {
|
||||
return formatAxis(new Date());
|
||||
return formatAxis(new Date());
|
||||
});
|
||||
const userInfoFormRef = ref()
|
||||
const userInfoFormRef = ref();
|
||||
const rules = reactive({
|
||||
name: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
|
||||
mobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确手机号' }]
|
||||
})
|
||||
// 定义变量内容
|
||||
const uploadAvatar = reactive({
|
||||
action:getBaseURL() + 'api/system/file/',
|
||||
headers: {
|
||||
Authorization: 'JWT ' + Session.get('token')
|
||||
},
|
||||
})
|
||||
name: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
|
||||
mobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确手机号' }],
|
||||
});
|
||||
|
||||
let selectImgVisible = ref(false);
|
||||
|
||||
const state = reactive<PersonalState>({
|
||||
newsInfoList:[],
|
||||
personalForm: {
|
||||
avatar:'',
|
||||
username:'',
|
||||
name: '',
|
||||
email: '',
|
||||
mobile: '',
|
||||
gender:'',
|
||||
dept_info:{
|
||||
dept_id:0,
|
||||
dept_name:''
|
||||
},
|
||||
role_info:[{
|
||||
id:0,
|
||||
name:''
|
||||
}]
|
||||
},
|
||||
newsInfoList: [],
|
||||
personalForm: {
|
||||
avatar: '',
|
||||
username: '',
|
||||
name: '',
|
||||
email: '',
|
||||
mobile: '',
|
||||
gender: '',
|
||||
dept_info: {
|
||||
dept_id: 0,
|
||||
dept_name: '',
|
||||
},
|
||||
role_info: [
|
||||
{
|
||||
id: 0,
|
||||
name: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 跳转消息中心
|
||||
*/
|
||||
import {useRouter } from "vue-router";
|
||||
import {Session} from "/@/utils/storage";
|
||||
const route = useRouter()
|
||||
const msgMore=()=>{
|
||||
route.push({path:'/messageCenter'})
|
||||
}
|
||||
const route = useRouter();
|
||||
const msgMore = () => {
|
||||
route.push({ path: '/messageCenter' });
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户个人信息
|
||||
*/
|
||||
const getUserInfo = function (){
|
||||
api.GetUserInfo({}).then((res:any)=>{
|
||||
const {data} = res
|
||||
state.personalForm.avatar = data.avatar || '';
|
||||
state.personalForm.username = data.username || '';
|
||||
state.personalForm.name = data.name || '';
|
||||
state.personalForm.email = data.email || '';
|
||||
state.personalForm.mobile = data.mobile || '';
|
||||
state.personalForm.gender = data.gender || '';
|
||||
state.personalForm.dept_info.dept_name = data.dept_info.dept_name || '';
|
||||
state.personalForm.role_info = data.role_info || [];
|
||||
})
|
||||
}
|
||||
const getUserInfo = function () {
|
||||
api.GetUserInfo({}).then((res: any) => {
|
||||
const { data } = res;
|
||||
state.personalForm.avatar = data.avatar || '';
|
||||
state.personalForm.username = data.username || '';
|
||||
state.personalForm.name = data.name || '';
|
||||
state.personalForm.email = data.email || '';
|
||||
state.personalForm.mobile = data.mobile || '';
|
||||
state.personalForm.gender = data.gender;
|
||||
state.personalForm.dept_info.dept_name = data.dept_info.dept_name || '';
|
||||
state.personalForm.role_info = data.role_info || [];
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
* @param formEl
|
||||
*/
|
||||
const submitForm = async () => {
|
||||
if (!userInfoFormRef.value) return
|
||||
await userInfoFormRef.value.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
api.updateUserInfo(state.personalForm).then((res:any)=>{
|
||||
ElMessage.success('更新成功')
|
||||
getUserInfo()
|
||||
})
|
||||
} else {
|
||||
ElMessage.error('表单验证失败,请检查~')
|
||||
}
|
||||
})
|
||||
}
|
||||
if (!userInfoFormRef.value) return;
|
||||
await userInfoFormRef.value.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
api.updateUserInfo(state.personalForm).then((res: any) => {
|
||||
ElMessage.success('更新成功');
|
||||
getUserInfo();
|
||||
});
|
||||
} else {
|
||||
ElMessage.error('表单验证失败,请检查~');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取消息通知
|
||||
*/
|
||||
const getMsg = () => {
|
||||
api.GetSelfReceive({}).then((res:any)=>{
|
||||
const {data} = res
|
||||
state.newsInfoList = data || [];
|
||||
})
|
||||
}
|
||||
onMounted(()=>{
|
||||
getUserInfo();
|
||||
getMsg();
|
||||
})
|
||||
api.GetSelfReceive({}).then((res: any) => {
|
||||
const { data } = res;
|
||||
state.newsInfoList = data || [];
|
||||
});
|
||||
};
|
||||
onMounted(() => {
|
||||
getUserInfo();
|
||||
getMsg();
|
||||
});
|
||||
|
||||
/**************************密码修改部分************************/
|
||||
const passwordFormShow = ref(false)
|
||||
const userPasswordFormRef = ref()
|
||||
const userPasswordInfo=reactive({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
newPassword2: ''
|
||||
})
|
||||
const passwordFormShow = ref(false);
|
||||
const userPasswordFormRef = ref();
|
||||
const userPasswordInfo = reactive({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
newPassword2: '',
|
||||
});
|
||||
|
||||
const validatePass = (rule, value, callback) => {
|
||||
const pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}')
|
||||
if (value === '') {
|
||||
callback(new Error('请输入密码'))
|
||||
} else if (value === userPasswordInfo.oldPassword) {
|
||||
callback(new Error('原密码与新密码一致'))
|
||||
} else if (!pwdRegex.test(value)) {
|
||||
callback(new Error('您的密码复杂度太低(密码中必须包含字母、数字)'))
|
||||
} else {
|
||||
if (userPasswordInfo.newPassword2 !== '') {
|
||||
userPasswordFormRef.value.validateField('newPassword2')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}');
|
||||
if (value === '') {
|
||||
callback(new Error('请输入密码'));
|
||||
} else if (value === userPasswordInfo.oldPassword) {
|
||||
callback(new Error('原密码与新密码一致'));
|
||||
} else if (!pwdRegex.test(value)) {
|
||||
callback(new Error('您的密码复杂度太低(密码中必须包含字母、数字)'));
|
||||
} else {
|
||||
if (userPasswordInfo.newPassword2 !== '') {
|
||||
userPasswordFormRef.value.validateField('newPassword2');
|
||||
}
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validatePass2 = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'))
|
||||
} else if (value !== userPasswordInfo.newPassword) {
|
||||
callback(new Error('两次输入密码不一致!'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'));
|
||||
} else if (value !== userPasswordInfo.newPassword) {
|
||||
callback(new Error('两次输入密码不一致!'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const passwordRules=reactive({
|
||||
oldPassword: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入原密码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
newPassword: [{ validator: validatePass, trigger: 'blur' }],
|
||||
newPassword2: [{ validator: validatePass2, trigger: 'blur' }]
|
||||
})
|
||||
const passwordRules = reactive({
|
||||
oldPassword: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入原密码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
newPassword: [{ validator: validatePass, trigger: 'blur' }],
|
||||
newPassword2: [{ validator: validatePass2, trigger: 'blur' }],
|
||||
});
|
||||
|
||||
/**
|
||||
* 重新设置密码
|
||||
*/
|
||||
const settingPassword= ()=>{
|
||||
userPasswordFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
api.UpdatePassword(userPasswordInfo).then((res:any)=>{
|
||||
ElMessage.success('密码修改成功')
|
||||
})
|
||||
} else {
|
||||
// 校验失败
|
||||
// 登录表单校验失败
|
||||
ElMessage.error('表单校验失败,请检查')
|
||||
}
|
||||
})
|
||||
}
|
||||
const settingPassword = () => {
|
||||
userPasswordFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
api.UpdatePassword(userPasswordInfo).then((res: any) => {
|
||||
ElMessage.success('密码修改成功');
|
||||
});
|
||||
} else {
|
||||
// 校验失败
|
||||
// 登录表单校验失败
|
||||
ElMessage.error('表单校验失败,请检查');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const uploadImg = (data: any) => {
|
||||
let formdata = new FormData();
|
||||
formdata.append('file', data);
|
||||
api.uploadAvatar(formdata).then((res: any) => {
|
||||
if (res.code === 2000) {
|
||||
selectImgVisible.value = false;
|
||||
state.personalForm.avatar = getBaseURL() + res.data.url;
|
||||
api.updateUserInfo(state.personalForm).then((res: any) => {
|
||||
successMessage('更新成功');
|
||||
getUserInfo();
|
||||
useUserInfo().updateUserInfos();
|
||||
// @ts-ignore
|
||||
avatarSelectorRef.value.updateAvatar(state.personalForm.avatar);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/theme/mixins/index.scss';
|
||||
.personal {
|
||||
.personal-user {
|
||||
height: 130px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.personal-user-left {
|
||||
width: 100px;
|
||||
height: 130px;
|
||||
border-radius: 3px;
|
||||
:deep(.el-upload) {
|
||||
height: 100%;
|
||||
}
|
||||
.personal-user-left-upload {
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
&:hover {
|
||||
img {
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-user-right {
|
||||
flex: 1;
|
||||
padding: 0 15px;
|
||||
.personal-title {
|
||||
font-size: 18px;
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
.personal-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
.personal-item-label {
|
||||
color: var(--el-text-color-secondary);
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
.personal-item-value {
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-info {
|
||||
.personal-info-more {
|
||||
float: right;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.personal-info-box {
|
||||
height: 130px;
|
||||
overflow: hidden;
|
||||
.personal-info-ul {
|
||||
list-style: none;
|
||||
.personal-info-li {
|
||||
font-size: 13px;
|
||||
padding-bottom: 10px;
|
||||
.personal-info-li-title {
|
||||
display: inline-block;
|
||||
@include text-ellipsis(1);
|
||||
color: var(--el-text-color-secondary);
|
||||
text-decoration: none;
|
||||
}
|
||||
& a:hover {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-recommend-row {
|
||||
.personal-recommend-col {
|
||||
.personal-recommend {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
i {
|
||||
right: 0px !important;
|
||||
bottom: 0px !important;
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
bottom: -10px;
|
||||
font-size: 70px;
|
||||
transform: rotate(-30deg);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
.personal-recommend-auto {
|
||||
padding: 15px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 5%;
|
||||
color: var(--next-color-white);
|
||||
.personal-recommend-msg {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-edit {
|
||||
.personal-edit-title {
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
color: var(--el-text-color-regular);
|
||||
&::after {
|
||||
content: '';
|
||||
width: 2px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.personal-edit-safe-box {
|
||||
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
padding: 15px 0;
|
||||
.personal-edit-safe-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.personal-edit-safe-item-left {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.personal-edit-safe-item-left-label {
|
||||
color: var(--el-text-color-regular);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.personal-edit-safe-item-left-value {
|
||||
color: var(--el-text-color-secondary);
|
||||
@include text-ellipsis(1);
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-of-type {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-user {
|
||||
height: 130px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.personal-user-left {
|
||||
width: 100px;
|
||||
height: 130px;
|
||||
border-radius: 3px;
|
||||
:deep(.el-upload) {
|
||||
height: 100%;
|
||||
}
|
||||
.personal-user-left-upload {
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
&:hover {
|
||||
img {
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-user-right {
|
||||
flex: 1;
|
||||
padding: 0 15px;
|
||||
.personal-title {
|
||||
font-size: 18px;
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
.personal-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
.personal-item-label {
|
||||
color: var(--el-text-color-secondary);
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
.personal-item-value {
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-info {
|
||||
.personal-info-more {
|
||||
float: right;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.personal-info-box {
|
||||
height: 130px;
|
||||
overflow: hidden;
|
||||
.personal-info-ul {
|
||||
list-style: none;
|
||||
.personal-info-li {
|
||||
font-size: 13px;
|
||||
padding-bottom: 10px;
|
||||
.personal-info-li-title {
|
||||
display: inline-block;
|
||||
@include text-ellipsis(1);
|
||||
color: var(--el-text-color-secondary);
|
||||
text-decoration: none;
|
||||
}
|
||||
& a:hover {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-recommend-row {
|
||||
.personal-recommend-col {
|
||||
.personal-recommend {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
i {
|
||||
right: 0px !important;
|
||||
bottom: 0px !important;
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
bottom: -10px;
|
||||
font-size: 70px;
|
||||
transform: rotate(-30deg);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
.personal-recommend-auto {
|
||||
padding: 15px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 5%;
|
||||
color: var(--next-color-white);
|
||||
.personal-recommend-msg {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-edit {
|
||||
.personal-edit-title {
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
color: var(--el-text-color-regular);
|
||||
&::after {
|
||||
content: '';
|
||||
width: 2px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.personal-edit-safe-box {
|
||||
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
padding: 15px 0;
|
||||
.personal-edit-safe-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.personal-edit-safe-item-left {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.personal-edit-safe-item-left-label {
|
||||
color: var(--el-text-color-regular);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.personal-edit-safe-item-left-value {
|
||||
color: var(--el-text-color-secondary);
|
||||
@include text-ellipsis(1);
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-of-type {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
236
web/src/views/system/role/crud.tsx
Normal file
236
web/src/views/system/role/crud.tsx
Normal file
@@ -0,0 +1,236 @@
|
||||
import { CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose, compute } from '@fast-crud/fast-crud';
|
||||
import _ from 'lodash-es';
|
||||
import * as api from './api';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
import { successMessage } from '../../../utils/message';
|
||||
import {inject} from "vue";
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
|
||||
//此处为crudOptions配置
|
||||
export const createCrudOptions = function ({ crudExpose, rolePermission }: { crudExpose: CrudExpose; rolePermission: any }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: any) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
//权限判定
|
||||
const hasPermissions = inject("$hasPermissions")
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
show:hasPermissions('role:Update')
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
show:hasPermissions('role:Delete')
|
||||
},
|
||||
custom: {
|
||||
text: '权限配置',
|
||||
type: 'text',
|
||||
show:hasPermissions('role:Update'),
|
||||
tooltip: {
|
||||
placement: 'top',
|
||||
content: '权限配置',
|
||||
},
|
||||
click: (context: any): void => {
|
||||
const { row } = context;
|
||||
// eslint-disable-next-line no-mixed-spaces-and-tabs
|
||||
rolePermission.value.drawer = true;
|
||||
rolePermission.value.editedRoleInfo = row;
|
||||
rolePermission.value.initGet();
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
labelWidth: '100px',
|
||||
wrapper: {
|
||||
is: 'el-dialog',
|
||||
width: '600px',
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: { show: false },
|
||||
column: {
|
||||
//type: 'index',
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
},
|
||||
},
|
||||
id: {
|
||||
title: 'ID',
|
||||
type: 'text',
|
||||
column: { show: false },
|
||||
search: { show: false },
|
||||
form: { show: false },
|
||||
},
|
||||
name: {
|
||||
title: '角色名称',
|
||||
type: 'text',
|
||||
search: { show: true },
|
||||
column: {
|
||||
minWidth: 120,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '角色名称必填' }],
|
||||
component: {
|
||||
placeholder: '请输入角色名称',
|
||||
},
|
||||
},
|
||||
},
|
||||
key: {
|
||||
title: '权限标识',
|
||||
type: 'text',
|
||||
search: { show: false },
|
||||
column: {
|
||||
width: 120,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '权限标识必填' }],
|
||||
placeholder: '输入权限标识',
|
||||
},
|
||||
},
|
||||
sort: {
|
||||
title: '排序',
|
||||
search: { show: false },
|
||||
type: 'number',
|
||||
column: {
|
||||
width: 90,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '排序必填' }],
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
title: '是否管理员',
|
||||
search: { show: false },
|
||||
type: 'dict-radio',
|
||||
dict: dict({
|
||||
data: [
|
||||
{
|
||||
label: '是',
|
||||
value: true,
|
||||
color: 'success',
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: false,
|
||||
color: 'danger',
|
||||
},
|
||||
],
|
||||
}),
|
||||
column: {
|
||||
width: 130,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '是否管理员必填' }],
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
status: {
|
||||
title: '状态',
|
||||
search: { show: true },
|
||||
type: 'dict-radio',
|
||||
column: {
|
||||
width:100,
|
||||
component: {
|
||||
name: 'fs-dict-switch',
|
||||
activeText: '',
|
||||
inactiveText: '',
|
||||
style: '--el-switch-on-color: #409eff; --el-switch-off-color: #dcdfe6',
|
||||
onChange: compute((context) => {
|
||||
return () => {
|
||||
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool'),
|
||||
}),
|
||||
},
|
||||
update_datetime: {
|
||||
title: '更新时间',
|
||||
type: 'text',
|
||||
search: { show: false },
|
||||
column: {
|
||||
width: 170,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
placeholder: '输入关键词搜索',
|
||||
},
|
||||
},
|
||||
},
|
||||
create_datetime: {
|
||||
title: '创建时间',
|
||||
type: 'text',
|
||||
search: { show: false },
|
||||
column: {
|
||||
sortable: 'custom',
|
||||
width: 170,
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
placeholder: '输入关键词搜索',
|
||||
},
|
||||
},
|
||||
},
|
||||
// description: {
|
||||
// title: '备注',
|
||||
// type: 'textarea',
|
||||
// search: {show: false},
|
||||
// form: {
|
||||
// component: {
|
||||
// maxlength: 200,
|
||||
// placeholder: '输入备注',
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,237 +0,0 @@
|
||||
import {CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose} from '@fast-crud/fast-crud';
|
||||
import _ from 'lodash-es';
|
||||
import * as api from "./api";
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
|
||||
//此处为crudOptions配置
|
||||
export const createCrudOptions = function ({crudExpose,rolePermission}: {crudExpose: CrudExpose,rolePermission:any}): CreateCrudOptionsTypes {
|
||||
|
||||
const pageRequest = async (query: any) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: "right",
|
||||
width:310,
|
||||
buttons: {
|
||||
custom: {
|
||||
text: "权限配置",
|
||||
type:'warning',
|
||||
tooltip: {
|
||||
placement: "top",
|
||||
content: "删除"
|
||||
},
|
||||
click: (context:any):void => {
|
||||
const {row} = context
|
||||
// eslint-disable-next-line no-mixed-spaces-and-tabs
|
||||
rolePermission.value.drawer=true
|
||||
rolePermission.value.editedRoleInfo = row
|
||||
rolePermission.value.initGet()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
form: {
|
||||
col: {span: 24},
|
||||
labelWidth: '100px',
|
||||
wrapper: {
|
||||
is: 'el-dialog',
|
||||
width: '600px',
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: {show: false},
|
||||
column: {
|
||||
//type: 'index',
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
formatter: (context) => {
|
||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||
let index = context.index ?? 1;
|
||||
let pagination = crudExpose.crudBinding.value.pagination;
|
||||
// @ts-ignore
|
||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||
},
|
||||
},
|
||||
},
|
||||
search: {
|
||||
title: '关键词',
|
||||
column: {show: false},
|
||||
type: 'text',
|
||||
search: {show: true},
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
placeholder: '输入关键词搜索',
|
||||
},
|
||||
},
|
||||
},
|
||||
id: {
|
||||
title: 'ID',
|
||||
type: 'text',
|
||||
column: {show: false},
|
||||
search: {show: false},
|
||||
form: {show: false},
|
||||
},
|
||||
name: {
|
||||
title: '角色名称',
|
||||
type: 'text',
|
||||
search: {show: true},
|
||||
column: {
|
||||
minWidth: 120,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{required: true, message: '角色名称必填'}],
|
||||
component: {
|
||||
placeholder: '输入角色名称搜索',
|
||||
},
|
||||
},
|
||||
},
|
||||
key: {
|
||||
title: '权限标识',
|
||||
type: 'text',
|
||||
search: {show: false},
|
||||
column: {
|
||||
width: 120,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{required: true, message: '权限标识必填'}],
|
||||
placeholder: '输入权限标识',
|
||||
},
|
||||
},
|
||||
sort: {
|
||||
title: '排序',
|
||||
search: {show: false},
|
||||
type: 'number',
|
||||
column: {
|
||||
width: 90,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
title: '是否管理员',
|
||||
search: {show: false},
|
||||
type: 'dict-radio',
|
||||
dict: dict({
|
||||
data: [
|
||||
{
|
||||
label: '是',
|
||||
value: true,
|
||||
color: 'success',
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: false,
|
||||
color: 'danger',
|
||||
},
|
||||
],
|
||||
}),
|
||||
column: {
|
||||
width: 130,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
status: {
|
||||
title: '状态',
|
||||
search: {show: true},
|
||||
type: 'dict-radio',
|
||||
dict: dict({
|
||||
data: [
|
||||
{
|
||||
label: '启用',
|
||||
value: true,
|
||||
color: 'success',
|
||||
},
|
||||
{
|
||||
label: '禁用',
|
||||
value: false,
|
||||
color: 'danger',
|
||||
},
|
||||
],
|
||||
}),
|
||||
column: {
|
||||
width: 90,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
value: true,
|
||||
},
|
||||
},
|
||||
update_datetime: {
|
||||
title: '更新时间',
|
||||
type: 'text',
|
||||
search: {show: false},
|
||||
column: {
|
||||
width: 170,
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
placeholder: '输入关键词搜索',
|
||||
},
|
||||
},
|
||||
},
|
||||
create_datetime: {
|
||||
title: '创建时间',
|
||||
type: 'text',
|
||||
search: {show: false},
|
||||
column: {
|
||||
sortable: true,
|
||||
width: 170,
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
placeholder: '输入关键词搜索',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
description: {
|
||||
title: '备注',
|
||||
type: 'textarea',
|
||||
search: {show: false},
|
||||
form: {
|
||||
component: {
|
||||
maxlength: 200,
|
||||
placeholder: '输入备注',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -11,11 +11,11 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud,dict } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './curd';
|
||||
import { useExpose, useCrud, dict } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
import RolePermission from '/@/views/system/rolePermission/index.vue';
|
||||
import * as api from './api'
|
||||
import _ from "lodash-es";
|
||||
import * as api from './api';
|
||||
import _ from 'lodash-es';
|
||||
const rolePermission = ref();
|
||||
defineExpose(rolePermission);
|
||||
// crud组件的ref
|
||||
|
||||
@@ -7,18 +7,24 @@
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header>
|
||||
<div >
|
||||
<el-tag size="large" type="primary">当前角色:{{ editedRoleInfo.name }}</el-tag>
|
||||
<div>
|
||||
<el-tag size="large" type="primary">当前角色:{{ editedRoleInfo.name }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<div style="padding: 1em">
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="6">
|
||||
<el-alert title="针对角色的页面菜单进行授权" description="点击菜单项,可对菜单下的按钮/接口授权" type="warning" />
|
||||
<el-card header="菜单页面授权">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>菜单页面</span>
|
||||
<el-tooltip effect="dark" content="点击菜单项,可对菜单下的按钮/接口授权" placement="right">
|
||||
<div>
|
||||
<span>菜单页面</span>
|
||||
<el-icon>
|
||||
<QuestionFilled/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-button size="mini" type="primary" @click="onSaveAuth">保存菜单授权</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -35,11 +41,18 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="18">
|
||||
<el-alert title="对页面菜单下按钮授权" description="新增或删除对菜单下的按钮/接口授权" type="warning" />
|
||||
<!-- <el-alert title="对页面菜单下按钮授权" description="新增或删除对菜单下的按钮/接口授权" type="warning" />-->
|
||||
<el-card v-if="isBtnPermissionShow">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>按钮/接口授权</span>
|
||||
<el-tooltip effect="dark" content="新增或删除对菜单下的按钮/接口授权" placement="right">
|
||||
<div>
|
||||
<span>按钮/接口授权</span>
|
||||
<el-icon>
|
||||
<QuestionFilled/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
@@ -49,12 +62,12 @@
|
||||
<el-table size="small" :data="buttonPermissionData" border style="width: 100%">
|
||||
<el-table-column prop="menu_button" label="权限名称" width="100">
|
||||
<template #default="scope">
|
||||
<div>{{formatMenuBtn(scope.row.menu_button)}}</div>
|
||||
<div>{{ formatMenuBtn(scope.row.menu_button) }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="data_range" label="权限范围" width="140">
|
||||
<template #default="scope">
|
||||
<div>{{formatDataRange(scope.row.data_range)}}</div>
|
||||
<div>{{ formatDataRange(scope.row.data_range) }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dept" label="权限涉及部门"/>
|
||||
@@ -139,8 +152,8 @@
|
||||
import {ref, defineExpose, reactive, toRefs} from 'vue'
|
||||
import {ElMessageBox} from 'element-plus'
|
||||
import * as api from './api'
|
||||
import type {FormRules,FormInstance} from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type {FormRules, FormInstance} from 'element-plus'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import XEUtils from 'xe-utils'
|
||||
//抽屉是否显示
|
||||
const drawer = ref(false)
|
||||
@@ -190,7 +203,7 @@ let buttonOptions = ref<[]>()
|
||||
let editedMenuInfo = ref()
|
||||
//菜单节点点击事件
|
||||
const menuNodeClick = (node: any, obj: any) => {
|
||||
isBtnPermissionShow.value = !node.is_catalog
|
||||
isBtnPermissionShow.value = !node.is_catalog
|
||||
if (!node.is_catalog) {
|
||||
buttonOptions.value = []
|
||||
editedMenuInfo.value = node
|
||||
@@ -198,7 +211,7 @@ const menuNodeClick = (node: any, obj: any) => {
|
||||
const {data} = res
|
||||
buttonOptions.value = data
|
||||
})
|
||||
api.getObj({menu: node.id,role:editedRoleInfo.value.id}).then((res:any)=>{
|
||||
api.getObj({menu: node.id, role: editedRoleInfo.value.id}).then((res: any) => {
|
||||
const {data} = res
|
||||
buttonPermissionData.value = data
|
||||
})
|
||||
@@ -211,7 +224,7 @@ const menuTree = ref()
|
||||
//是否显示新增表单
|
||||
const dialogFormVisible = ref(false)
|
||||
//部门树
|
||||
const deptTree=ref()
|
||||
const deptTree = ref()
|
||||
//自定义部门数据
|
||||
const deptOptions = ref()
|
||||
//选中的部门数据
|
||||
@@ -220,7 +233,7 @@ const deptCheckedKeys = []
|
||||
const buttonForm = reactive({
|
||||
menu_button: null,
|
||||
role: null,
|
||||
menu:null,
|
||||
menu: null,
|
||||
data_range: null,
|
||||
dept: []
|
||||
})
|
||||
@@ -256,20 +269,20 @@ const onChangeButton = (val: any) => {
|
||||
})
|
||||
//获取权限部门值
|
||||
api.GetDataScopeDept({menu_button: val}).then((res: any) => {
|
||||
deptOptions.value = XEUtils.toArrayTree(res.data,{ parentKey: 'parent', strict: false })
|
||||
deptOptions.value = XEUtils.toArrayTree(res.data, {parentKey: 'parent', strict: false})
|
||||
})
|
||||
|
||||
}
|
||||
//过滤按钮名称
|
||||
const formatMenuBtn = (val:any)=>{
|
||||
let obj:any = buttonOptions.value?.find((item:any)=>{
|
||||
return item.id===val
|
||||
const formatMenuBtn = (val: any) => {
|
||||
let obj: any = buttonOptions.value?.find((item: any) => {
|
||||
return item.id === val
|
||||
})
|
||||
return obj?obj.name:null
|
||||
return obj ? obj.name : null
|
||||
}
|
||||
//过滤权限范围
|
||||
const formatDataRange = (val:any)=>{
|
||||
let obj:any = [
|
||||
const formatDataRange = (val: any) => {
|
||||
let obj: any = [
|
||||
{
|
||||
"value": 0,
|
||||
"label": '仅本人数据权限'
|
||||
@@ -290,17 +303,17 @@ const formatDataRange = (val:any)=>{
|
||||
"value": 4,
|
||||
"label": '自定义数据权限'
|
||||
}
|
||||
].find((item:any)=>{
|
||||
return item.value===val
|
||||
].find((item: any) => {
|
||||
return item.value === val
|
||||
})
|
||||
return obj?obj.label:null
|
||||
return obj ? obj.label : null
|
||||
}
|
||||
//保存按钮表单
|
||||
|
||||
const onSaveButtonForm = async () => {
|
||||
const {id:roleId} = editedRoleInfo.value
|
||||
const {id:menuId} = editedMenuInfo.value
|
||||
const form:any = Object.assign({},buttonForm)
|
||||
const {id: roleId} = editedRoleInfo.value
|
||||
const {id: menuId} = editedMenuInfo.value
|
||||
const form: any = Object.assign({}, buttonForm)
|
||||
form.role = roleId
|
||||
form.menu = menuId
|
||||
//选中的部门
|
||||
@@ -309,7 +322,7 @@ const onSaveButtonForm = async () => {
|
||||
if (!buttonFormRef.value) return
|
||||
await buttonFormRef.value.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
api.CreatePermission(form).then((res:any)=>{
|
||||
api.CreatePermission(form).then((res: any) => {
|
||||
buttonPermissionData.value.push(form)
|
||||
dialogFormVisible.value = false
|
||||
ElMessage({
|
||||
@@ -320,7 +333,7 @@ const onSaveButtonForm = async () => {
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
title:'提交错误',
|
||||
title: '提交错误',
|
||||
message: 'F12控制台看详情',
|
||||
})
|
||||
console.log('提交错误', fields)
|
||||
@@ -329,8 +342,8 @@ const onSaveButtonForm = async () => {
|
||||
|
||||
}
|
||||
//删除按钮权限
|
||||
const onDeleteBtn = (scope:any)=>{
|
||||
const {row,$index} = scope
|
||||
const onDeleteBtn = (scope: any) => {
|
||||
const {row, $index} = scope
|
||||
ElMessageBox.confirm(
|
||||
'您是否要删除数据?',
|
||||
'温馨提示',
|
||||
@@ -339,15 +352,15 @@ const onDeleteBtn = (scope:any)=>{
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
api.DeletePermission({id:row.id}).then(res=>{
|
||||
buttonPermissionData.value.splice($index,1)
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: res.msg,
|
||||
})
|
||||
})
|
||||
).then(() => {
|
||||
api.DeletePermission({id: row.id}).then(res => {
|
||||
buttonPermissionData.value.splice($index, 1)
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: res.msg,
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: 'info',
|
||||
@@ -373,12 +386,12 @@ const onSaveAuth = () => {
|
||||
//合并的菜单数据
|
||||
const menuIdList = [...checkedList, ...halfCheckedList]
|
||||
// console.log(menuIdList)
|
||||
const { id:roleId } = editedRoleInfo.value
|
||||
const {id: roleId} = editedRoleInfo.value
|
||||
const data = {
|
||||
role:roleId,
|
||||
menu:menuIdList
|
||||
role: roleId,
|
||||
menu: menuIdList
|
||||
}
|
||||
api.SaveMenuPermission(data).then((res:any)=>{
|
||||
api.SaveMenuPermission(data).then((res: any) => {
|
||||
ElMessage({
|
||||
message: res.msg,
|
||||
type: 'success',
|
||||
|
||||
@@ -2,6 +2,15 @@ import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/user/';
|
||||
|
||||
export function GetDept(query: PageQuery) {
|
||||
return request({
|
||||
url: "/api/system/dept/dept_lazy_tree/",
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function GetList(query: PageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
|
||||
@@ -1,320 +1,341 @@
|
||||
import * as api from "./api";
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions, } from "@fast-crud/fast-crud";
|
||||
import { request } from "/@/utils/service";
|
||||
import { dictionary } from "/@/utils/dictionary";
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
import * as api from './api';
|
||||
import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||
import { request } from '/@/utils/service';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
import { successMessage } from '/@/utils/message';
|
||||
import { inject } from 'vue';
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: "right",
|
||||
width: 310,
|
||||
buttons: {
|
||||
orderExample: {
|
||||
show:false,
|
||||
text: "重置密码",
|
||||
click: () => {
|
||||
//console.log("reset password")
|
||||
}
|
||||
}
|
||||
},
|
||||
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: { show: false },
|
||||
column: {
|
||||
type: 'index',
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
},
|
||||
},
|
||||
search: {
|
||||
title: '关键词',
|
||||
column: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: {
|
||||
props: {
|
||||
clearable: true
|
||||
},
|
||||
placeholder: '请输入关键词'
|
||||
}
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
props: {
|
||||
clearable: true
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
username: {
|
||||
title: '账号',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
minWidth: 100,
|
||||
type: 'input',
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '账号必填项'
|
||||
}
|
||||
],
|
||||
component: {
|
||||
placeholder: '请输入账号'
|
||||
},
|
||||
}
|
||||
},
|
||||
password: {
|
||||
title: '密码',
|
||||
type: 'input',
|
||||
column: {
|
||||
show: false
|
||||
},
|
||||
editForm: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '密码必填项'
|
||||
}
|
||||
],
|
||||
component: {
|
||||
span: 12,
|
||||
showPassword: true,
|
||||
placeholder: '请输入密码'
|
||||
},
|
||||
// value: vm.systemConfig('base.default_password'),
|
||||
},
|
||||
/* valueResolve(row, key) {
|
||||
//权限判定
|
||||
const hasPermissions = inject('$hasPermissions');
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
table: {
|
||||
remove: {
|
||||
confirmMessage: '是否删除该用户?',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
show: hasPermissions('user:Update'),
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
show: hasPermissions('user:Delete'),
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: { show: false },
|
||||
column: {
|
||||
type: 'index',
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
},
|
||||
},
|
||||
username: {
|
||||
title: '账号',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'input',
|
||||
column: {
|
||||
minWidth: 100, //最小列宽
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '账号必填项',
|
||||
},
|
||||
],
|
||||
component: {
|
||||
placeholder: '请输入账号',
|
||||
},
|
||||
},
|
||||
},
|
||||
password: {
|
||||
title: '密码',
|
||||
type: 'input',
|
||||
column: {
|
||||
show: false,
|
||||
},
|
||||
editForm: {
|
||||
show: false,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '密码必填项',
|
||||
},
|
||||
],
|
||||
component: {
|
||||
span: 12,
|
||||
showPassword: true,
|
||||
placeholder: '请输入密码',
|
||||
},
|
||||
// value: vm.systemConfig('base.default_password'),
|
||||
},
|
||||
/* valueResolve(row, key) {
|
||||
if (row.password) {
|
||||
row.password = vm.$md5(row.password)
|
||||
}
|
||||
} */
|
||||
},
|
||||
name: {
|
||||
title: '姓名',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'input',
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '姓名必填项'
|
||||
}
|
||||
],
|
||||
component: {
|
||||
span: 12,
|
||||
placeholder: '请输入姓名'
|
||||
},
|
||||
}
|
||||
},
|
||||
dept: {
|
||||
title: '部门',
|
||||
search: {
|
||||
disabled: true
|
||||
},
|
||||
type: 'dict-tree',
|
||||
dict: dict({
|
||||
isTree: true,
|
||||
url: '/api/system/dept/all_dept/',
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
getData: async ({ url }: { url: string }) => {
|
||||
return request({
|
||||
url: url,
|
||||
}).then((ret: any) => {
|
||||
return ret.data
|
||||
})
|
||||
}
|
||||
}),
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
],
|
||||
component: {
|
||||
filterable: true,
|
||||
placeholder: '请选择',
|
||||
props: {
|
||||
props: {
|
||||
value: "id",
|
||||
label: "name",
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
role: {
|
||||
title: '角色',
|
||||
search: {
|
||||
disabled: true
|
||||
},
|
||||
type: 'dict-select',
|
||||
dict: dict({
|
||||
url: '/api/system/role/',
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
isTree: true,
|
||||
getData: async ({ url }: { url: string }) => {
|
||||
return request({
|
||||
url: url,
|
||||
params: {
|
||||
page: 1,
|
||||
limit: 10
|
||||
}
|
||||
}).then((ret: any) => {
|
||||
return ret.data
|
||||
})
|
||||
}
|
||||
}),
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
],
|
||||
component: {
|
||||
multiple: true,
|
||||
filterable: true,
|
||||
placeholder: '请选择角色'
|
||||
},
|
||||
}
|
||||
},
|
||||
mobile: {
|
||||
title: '手机号码',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'input',
|
||||
form: {
|
||||
rules: [
|
||||
{
|
||||
max: 20,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
message: '请输入正确的手机号码'
|
||||
}
|
||||
],
|
||||
component: {
|
||||
placeholder: '请输入手机号码'
|
||||
}
|
||||
}
|
||||
},
|
||||
email: {
|
||||
title: '邮箱',
|
||||
column:{
|
||||
width:260
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
{
|
||||
type: 'email',
|
||||
message: '请输入正确的邮箱地址',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
component: {
|
||||
placeholder: '请输入邮箱'
|
||||
}
|
||||
}
|
||||
},
|
||||
gender: {
|
||||
title: '性别',
|
||||
type: 'dict-radio',
|
||||
dict: dict({
|
||||
data: dictionary('gender')
|
||||
}),
|
||||
form: {
|
||||
value: 1,
|
||||
component: {
|
||||
span: 12
|
||||
}
|
||||
},
|
||||
component: { props: { color: 'auto' } } // 自动染色
|
||||
},
|
||||
user_type: {
|
||||
title: '用户类型',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'dict-select',
|
||||
dict: dict({
|
||||
data: dictionary('user_type')
|
||||
}),
|
||||
form: {
|
||||
show: false,
|
||||
value: 0,
|
||||
component: {
|
||||
span: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
is_active: {
|
||||
title: '状态',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'dict-radio',
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool')
|
||||
}),
|
||||
form: {
|
||||
value: true,
|
||||
component: {
|
||||
span: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
avatar: {
|
||||
title: '头像',
|
||||
type: 'avatar-cropper',
|
||||
form:{
|
||||
show:false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
name: {
|
||||
title: '姓名',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'input',
|
||||
column: {
|
||||
minWidth: 100, //最小列宽
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '姓名必填项',
|
||||
},
|
||||
],
|
||||
component: {
|
||||
span: 12,
|
||||
placeholder: '请输入姓名',
|
||||
},
|
||||
},
|
||||
},
|
||||
dept: {
|
||||
title: '部门',
|
||||
search: {
|
||||
disabled: true,
|
||||
},
|
||||
type: 'dict-tree',
|
||||
dict: dict({
|
||||
isTree: true,
|
||||
url: '/api/system/dept/all_dept/',
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
getData: async ({ url }: { url: string }) => {
|
||||
return request({
|
||||
url: url,
|
||||
}).then((ret: any) => {
|
||||
return ret.data;
|
||||
});
|
||||
},
|
||||
}),
|
||||
column: {
|
||||
minWidth: 150, //最小列宽
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项',
|
||||
},
|
||||
],
|
||||
component: {
|
||||
filterable: true,
|
||||
placeholder: '请选择',
|
||||
props: {
|
||||
props: {
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
role: {
|
||||
title: '角色',
|
||||
search: {
|
||||
disabled: true,
|
||||
},
|
||||
type: 'dict-select',
|
||||
dict: dict({
|
||||
url: '/api/system/role/',
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
isTree: true,
|
||||
getData: async ({ url }: { url: string }) => {
|
||||
return request({
|
||||
url: url,
|
||||
params: {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
},
|
||||
}).then((ret: any) => {
|
||||
return ret.data;
|
||||
});
|
||||
},
|
||||
}),
|
||||
column: {
|
||||
minWidth: 100, //最小列宽
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项',
|
||||
},
|
||||
],
|
||||
component: {
|
||||
multiple: true,
|
||||
filterable: true,
|
||||
placeholder: '请选择角色',
|
||||
},
|
||||
},
|
||||
},
|
||||
mobile: {
|
||||
title: '手机号码',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'input',
|
||||
column: {
|
||||
minWidth: 120, //最小列宽
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
{
|
||||
max: 20,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
},
|
||||
],
|
||||
component: {
|
||||
placeholder: '请输入手机号码',
|
||||
},
|
||||
},
|
||||
},
|
||||
email: {
|
||||
title: '邮箱',
|
||||
column: {
|
||||
width: 260,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
{
|
||||
type: 'email',
|
||||
message: '请输入正确的邮箱地址',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
placeholder: '请输入邮箱',
|
||||
},
|
||||
},
|
||||
},
|
||||
gender: {
|
||||
title: '性别',
|
||||
type: 'dict-select',
|
||||
dict: dict({
|
||||
data: dictionary('gender'),
|
||||
}),
|
||||
form: {
|
||||
value: 1,
|
||||
component: {
|
||||
span: 12,
|
||||
},
|
||||
},
|
||||
component: { props: { color: 'auto' } }, // 自动染色
|
||||
},
|
||||
user_type: {
|
||||
title: '用户类型',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'dict-select',
|
||||
dict: dict({
|
||||
data: dictionary('user_type'),
|
||||
}),
|
||||
column: {
|
||||
minWidth: 100, //最小列宽
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
value: 0,
|
||||
component: {
|
||||
span: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
is_active: {
|
||||
title: '锁定',
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: 'dict-radio',
|
||||
column: {
|
||||
component: {
|
||||
name: 'fs-dict-switch',
|
||||
activeText: '',
|
||||
inactiveText: '',
|
||||
style: '--el-switch-on-color: #409eff; --el-switch-off-color: #dcdfe6',
|
||||
onChange: compute((context) => {
|
||||
return () => {
|
||||
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool'),
|
||||
}),
|
||||
},
|
||||
avatar: {
|
||||
title: '头像',
|
||||
type: 'avatar-cropper',
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,13 +1,115 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
<el-row class="mx-2">
|
||||
<el-col xs="24" :sm="8" :md="6" :lg="4" :xl="4" class="p-1">
|
||||
<el-card :body-style="{ height: '100%' }">
|
||||
<p class="font-mono font-black text-center text-xl pb-5">
|
||||
部门列表
|
||||
<el-tooltip effect="dark" :content="content" placement="right">
|
||||
<el-icon>
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</p>
|
||||
<el-input v-model="filterText" :placeholder="placeholder" />
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
class="font-mono font-bold leading-6 text-7xl"
|
||||
:data="data"
|
||||
:props="treeProps"
|
||||
:filter-node-method="filterNode"
|
||||
icon="ArrowRightBold"
|
||||
:indent="12"
|
||||
@node-click="onTreeNodeClick"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="text-center font-black text-xl">{{ node.label }}</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col xs="24" :sm="16" :md="18" :lg="20" :xl="20" class="p-1">
|
||||
<el-card :body-style="{ height: '100%' }">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"></fs-crud>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
import * as api from './api';
|
||||
import { ElTree } from 'element-plus';
|
||||
import { ref, onMounted, watch, toRaw, defineAsyncComponent } from 'vue';
|
||||
import XEUtils from 'xe-utils';
|
||||
import { errorMessage, successMessage } from '../../../utils/message';
|
||||
import { GetDept } from './api';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
name: string;
|
||||
status: boolean;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
interface APIResponseData {
|
||||
code?: number;
|
||||
data: [];
|
||||
msg?: string;
|
||||
}
|
||||
|
||||
// 引入组件
|
||||
const placeholder = ref('请输入部门名称');
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
icon: 'icon',
|
||||
};
|
||||
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return toRaw(data).name.indexOf(value) !== -1;
|
||||
};
|
||||
|
||||
let data = ref([]);
|
||||
|
||||
const content = `
|
||||
1.部门信息;
|
||||
`;
|
||||
|
||||
const getData = () => {
|
||||
api.GetDept({}).then((ret: APIResponseData) => {
|
||||
const responseData = ret.data;
|
||||
const result = XEUtils.toArrayTree(responseData, {
|
||||
parentKey: 'parent',
|
||||
children: 'children',
|
||||
strict: true,
|
||||
});
|
||||
data.value = result;
|
||||
});
|
||||
};
|
||||
|
||||
//树形点击事件
|
||||
const onTreeNodeClick = (node: any) => {
|
||||
const { id } = node;
|
||||
crudExpose.doSearch({ form: { dept: id } });
|
||||
};
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
@@ -24,3 +126,17 @@ onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-row {
|
||||
height: 100%;
|
||||
|
||||
.el-col {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.el-card {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||
|
||||
export const apiPrefix = '/api/system/api_white_list/';
|
||||
export function GetList(query: PageQuery) {
|
||||
export function GetList(query: UserPageQuery) {
|
||||
return request({
|
||||
url: apiPrefix,
|
||||
method: 'get',
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import * as api from './api';
|
||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||
import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||
import { request } from '/@/utils/service';
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
interface CreateCrudOptionsTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
import { successMessage } from '/@/utils/message';
|
||||
import {inject} from "vue";
|
||||
|
||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: PageQuery) => {
|
||||
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
@@ -20,6 +19,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
//权限判定
|
||||
const hasPermissions = inject("$hasPermissions")
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
@@ -28,6 +31,34 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 150,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
iconRight: 'Edit',
|
||||
type: 'text',
|
||||
show:hasPermissions("api_white_list:Update")
|
||||
},
|
||||
remove: {
|
||||
iconRight: 'Delete',
|
||||
type: 'text',
|
||||
show:hasPermissions("api_white_list:Delete")
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
labelWidth: '110px',
|
||||
wrapper: {
|
||||
is: 'el-dialog',
|
||||
width: '600px',
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
@@ -37,10 +68,11 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
//@ts-ignore
|
||||
formatter: (context) => {
|
||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||
let index = context.index ?? 1;
|
||||
let pagination: any = crudExpose.crudBinding.value.pagination;
|
||||
let pagination: any = crudExpose!.crudBinding.value.pagination;
|
||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||
},
|
||||
},
|
||||
@@ -70,7 +102,7 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
method: {
|
||||
title: '请求方式',
|
||||
sortable: true,
|
||||
sortable: 'custom',
|
||||
search: {
|
||||
disabled: false,
|
||||
},
|
||||
@@ -93,8 +125,15 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
label: 'DELETE',
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
label: 'PATCH',
|
||||
value: 4,
|
||||
},
|
||||
],
|
||||
}),
|
||||
column:{
|
||||
minWidth: 120,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -113,7 +152,7 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
},
|
||||
url: {
|
||||
title: '接口地址',
|
||||
sortable: true,
|
||||
sortable: 'custom',
|
||||
search: {
|
||||
disabled: true,
|
||||
},
|
||||
@@ -133,6 +172,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
});
|
||||
},
|
||||
}),
|
||||
column:{
|
||||
minWidth: 200,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -155,9 +197,11 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
class: { yxtInput: true },
|
||||
},
|
||||
helper: {
|
||||
render(h) {
|
||||
return <el-alert title="请正确填写,以免请求时被拦截。匹配单例使用正则,例如:/api/xx/.*?/" type="warning" />;
|
||||
position: 'label',
|
||||
tooltip: {
|
||||
placement: 'top-start',
|
||||
},
|
||||
text: '请正确填写,以免请求时被拦截。匹配单例使用正则,例如:/api/xx/.*?/',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -166,17 +210,26 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
||||
search: {
|
||||
disabled: false,
|
||||
},
|
||||
width: 150,
|
||||
type: 'dict-radio',
|
||||
column: {
|
||||
minWidth:120,
|
||||
component: {
|
||||
name: 'fs-dict-switch',
|
||||
activeText: '',
|
||||
inactiveText: '',
|
||||
style: '--el-switch-on-color: #409eff; --el-switch-off-color: #dcdfe6',
|
||||
onChange: compute((context) => {
|
||||
return () => {
|
||||
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool'),
|
||||
}),
|
||||
form: {
|
||||
value: true,
|
||||
component: {
|
||||
span: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -6,18 +6,10 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
|
||||
12
web/tailwind.config.js
Normal file
12
web/tailwind.config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{vue,js}'],
|
||||
theme: {
|
||||
extend: {
|
||||
height: {
|
||||
'screen/2': '50vh',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
2623
web/yarn.lock
2623
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user