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)
|
[](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」**
|
💡 **「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://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/
|
media/
|
||||||
__pypackages__/
|
__pypackages__/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
gunicorn.pid
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ LOGGING = {
|
|||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_PARSER_CLASSES': (
|
'DEFAULT_PARSER_CLASSES': (
|
||||||
'rest_framework.parsers.JSONParser',
|
'rest_framework.parsers.JSONParser',
|
||||||
|
'rest_framework.parsers.MultiPartParser',
|
||||||
),
|
),
|
||||||
"DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S", # 日期时间格式配置
|
"DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S", # 日期时间格式配置
|
||||||
"DATE_FORMAT": "%Y-%m-%d",
|
"DATE_FORMAT": "%Y-%m-%d",
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": true,
|
"is_catalog": true,
|
||||||
"web_path": "/system",
|
"web_path": "/system",
|
||||||
"component": "layout/routerView/parent",
|
"component": "",
|
||||||
"component_name": "menu",
|
"component_name": "",
|
||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 19,
|
"parent": 41,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": false,
|
"visible": false,
|
||||||
"parent": 19,
|
"parent": 41,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 19,
|
"parent": 41,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 19,
|
"parent": 41,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 19,
|
"parent": 41,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -284,7 +284,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 19,
|
"parent": 41,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 19,
|
"parent": 41,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -376,8 +376,8 @@
|
|||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": true,
|
"is_catalog": true,
|
||||||
"web_path": "/generalConfig",
|
"web_path": "/generalConfig",
|
||||||
"component": "layout/routerView/parent",
|
"component": "",
|
||||||
"component_name": "config",
|
"component_name": "",
|
||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
@@ -395,7 +395,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 27,
|
"parent": 49,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -442,7 +442,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 27,
|
"parent": 49,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -489,7 +489,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 27,
|
"parent": 49,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -536,7 +536,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 27,
|
"parent": 49,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -575,8 +575,8 @@
|
|||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": true,
|
"is_catalog": true,
|
||||||
"web_path": "/log",
|
"web_path": "/log",
|
||||||
"component": "layout/routerView/parent",
|
"component": "",
|
||||||
"component_name": "log",
|
"component_name": "",
|
||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
@@ -594,7 +594,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 32,
|
"parent": 54,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
@@ -623,7 +623,7 @@
|
|||||||
"status": true,
|
"status": true,
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"parent": 32,
|
"parent": 54,
|
||||||
"children": [],
|
"children": [],
|
||||||
"menu_button": [
|
"menu_button": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser, UserManager
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from application import dispatch
|
from application import dispatch
|
||||||
from dvadmin.utils.models import CoreModel, table_prefix
|
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):
|
class Users(CoreModel, AbstractUser):
|
||||||
username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号",
|
username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号",
|
||||||
help_text="用户账号")
|
help_text="用户账号")
|
||||||
@@ -48,6 +76,7 @@ class Users(CoreModel, AbstractUser):
|
|||||||
blank=True,
|
blank=True,
|
||||||
help_text="关联部门",
|
help_text="关联部门",
|
||||||
)
|
)
|
||||||
|
objects = CustomUserManager()
|
||||||
|
|
||||||
def set_password(self, raw_password):
|
def set_password(self, raw_password):
|
||||||
super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest())
|
super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest())
|
||||||
@@ -76,20 +105,6 @@ class Post(CoreModel):
|
|||||||
ordering = ("sort",)
|
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):
|
class Dept(CoreModel):
|
||||||
name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
|
name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
|
||||||
key = models.CharField(max_length=64, unique=True, null=True, blank=True, verbose_name="关联字符",
|
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):
|
def media_file_name(instance, filename):
|
||||||
h = instance.md5sum
|
h = instance.md5sum
|
||||||
basename, ext = os.path.splitext(filename)
|
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):
|
class FileList(CoreModel):
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ class DeptViewSet(CustomModelViewSet):
|
|||||||
data = serializer.data
|
data = serializer.data
|
||||||
return SuccessResponse(data=data)
|
return SuccessResponse(data=data)
|
||||||
|
|
||||||
|
@action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated], extra_filter_class=[])
|
||||||
def dept_lazy_tree(self, request, *args, **kwargs):
|
def dept_lazy_tree(self, request, *args, **kwargs):
|
||||||
parent = self.request.query_params.get('parent')
|
parent = self.request.query_params.get('parent')
|
||||||
is_superuser = request.user.is_superuser
|
is_superuser = request.user.is_superuser
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from dvadmin.system.models import FileList
|
from dvadmin.system.models import FileList
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
@@ -9,7 +8,7 @@ class FileSerializer(CustomModelSerializer):
|
|||||||
url = serializers.SerializerMethodField(read_only=True)
|
url = serializers.SerializerMethodField(read_only=True)
|
||||||
|
|
||||||
def get_url(self, instance):
|
def get_url(self, instance):
|
||||||
return 'media/' + str(instance.url)
|
return f'media/{str(instance.url)}'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FileList
|
model = FileList
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from captcha.views import CaptchaStore, captcha_image
|
from captcha.views import CaptchaStore, captcha_image
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
from django.contrib.auth.hashers import make_password, check_password
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
@@ -14,9 +12,7 @@ from rest_framework import serializers
|
|||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
||||||
from rest_framework_simplejwt.views import TokenObtainPairView
|
from rest_framework_simplejwt.views import TokenObtainPairView
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from application import dispatch
|
from application import dispatch
|
||||||
from dvadmin.system.models import Users
|
from dvadmin.system.models import Users
|
||||||
from dvadmin.utils.json_response import ErrorResponse, DetailResponse
|
from dvadmin.utils.json_response import ErrorResponse, DetailResponse
|
||||||
@@ -58,6 +54,7 @@ class LoginSerializer(TokenObtainPairSerializer):
|
|||||||
captcha = serializers.CharField(
|
captcha = serializers.CharField(
|
||||||
max_length=6, required=False, allow_null=True, allow_blank=True
|
max_length=6, required=False, allow_null=True, allow_blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Users
|
model = Users
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
@@ -79,13 +76,18 @@ class LoginSerializer(TokenObtainPairSerializer):
|
|||||||
raise CustomValidationError("验证码过期")
|
raise CustomValidationError("验证码过期")
|
||||||
else:
|
else:
|
||||||
if self.image_code and (
|
if self.image_code and (
|
||||||
self.image_code.response == captcha
|
self.image_code.response == captcha
|
||||||
or self.image_code.challenge == captcha
|
or self.image_code.challenge == captcha
|
||||||
):
|
):
|
||||||
self.image_code and self.image_code.delete()
|
self.image_code and self.image_code.delete()
|
||||||
else:
|
else:
|
||||||
self.image_code and self.image_code.delete()
|
self.image_code and self.image_code.delete()
|
||||||
raise CustomValidationError("图片验证码错误")
|
raise CustomValidationError("图片验证码错误")
|
||||||
|
|
||||||
|
user = Users.objects.get(username=attrs['username'])
|
||||||
|
if not user.is_active:
|
||||||
|
raise CustomValidationError("账号被锁定")
|
||||||
|
|
||||||
data = super().validate(attrs)
|
data = super().validate(attrs)
|
||||||
data["name"] = self.user.name
|
data["name"] = self.user.name
|
||||||
data["userId"] = self.user.id
|
data["userId"] = self.user.id
|
||||||
@@ -107,6 +109,7 @@ class LoginSerializer(TokenObtainPairSerializer):
|
|||||||
save_login_log(request=request)
|
save_login_log(request=request)
|
||||||
return {"code": 2000, "msg": "请求成功", "data": data}
|
return {"code": 2000, "msg": "请求成功", "data": data}
|
||||||
|
|
||||||
|
|
||||||
class LoginView(TokenObtainPairView):
|
class LoginView(TokenObtainPairView):
|
||||||
"""
|
"""
|
||||||
登录接口
|
登录接口
|
||||||
|
|||||||
@@ -53,9 +53,6 @@ class MenuCreateSerializer(CustomModelSerializer):
|
|||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WebRouterSerializer(CustomModelSerializer):
|
class WebRouterSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
前端菜单路由的简单序列化器
|
前端菜单路由的简单序列化器
|
||||||
@@ -63,11 +60,11 @@ class WebRouterSerializer(CustomModelSerializer):
|
|||||||
path = serializers.CharField(source="web_path")
|
path = serializers.CharField(source="web_path")
|
||||||
title = serializers.CharField(source="name")
|
title = serializers.CharField(source="name")
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = ('id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component',
|
fields = (
|
||||||
'component_name', 'cache', 'visible')
|
'id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component',
|
||||||
|
'component_name', 'cache', 'visible', 'status')
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
@@ -86,6 +83,7 @@ class MenuViewSet(CustomModelViewSet):
|
|||||||
update_serializer_class = MenuCreateSerializer
|
update_serializer_class = MenuCreateSerializer
|
||||||
search_fields = ['name', 'status']
|
search_fields = ['name', 'status']
|
||||||
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']
|
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']
|
||||||
|
|
||||||
# extra_filter_class = []
|
# extra_filter_class = []
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[])
|
@action(methods=['GET'], detail=False, permission_classes=[])
|
||||||
@@ -101,12 +99,36 @@ class MenuViewSet(CustomModelViewSet):
|
|||||||
data = serializer.data
|
data = serializer.data
|
||||||
return SuccessResponse(data=data, total=len(data), msg="获取成功")
|
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
|
request.query_params._mutable = True
|
||||||
params = request.query_params
|
params = request.query_params
|
||||||
parent = params.get('parent', None)
|
parent = params.get('parent', None)
|
||||||
page = params.get('page',None)
|
page = params.get('page', None)
|
||||||
limit = params.get('limit', None)
|
limit = params.get('limit', None)
|
||||||
if page:
|
if page:
|
||||||
del params['page']
|
del params['page']
|
||||||
@@ -114,11 +136,11 @@ class MenuViewSet(CustomModelViewSet):
|
|||||||
del params['limit']
|
del params['limit']
|
||||||
if params:
|
if params:
|
||||||
if parent:
|
if parent:
|
||||||
queryset = self.queryset.filter(status=1, parent=parent)
|
queryset = self.queryset.filter(parent=parent)
|
||||||
else:
|
else:
|
||||||
queryset = self.queryset.filter(status=1)
|
queryset = self.queryset.filter()
|
||||||
else:
|
else:
|
||||||
queryset = self.queryset.filter(status=1, parent__isnull=True)
|
queryset = self.queryset.filter(parent__isnull=True)
|
||||||
queryset = self.filter_queryset(queryset)
|
queryset = self.filter_queryset(queryset)
|
||||||
serializer = MenuSerializer(queryset, many=True, request=request)
|
serializer = MenuSerializer(queryset, many=True, request=request)
|
||||||
data = serializer.data
|
data = serializer.data
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class MessageCenterTargetUserListSerializer(CustomModelSerializer):
|
|||||||
def get_is_read(self, instance):
|
def get_is_read(self, instance):
|
||||||
user_id = self.request.user.id
|
user_id = self.request.user.id
|
||||||
message_center_id = instance.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:
|
if queryset:
|
||||||
return queryset.is_read
|
return queryset.is_read
|
||||||
return False
|
return False
|
||||||
@@ -95,21 +95,22 @@ class MessageCenterTargetUserListSerializer(CustomModelSerializer):
|
|||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
def websocket_push(user_id, message):
|
def websocket_push(user_id, message):
|
||||||
"""
|
"""
|
||||||
主动推送消息
|
主动推送消息
|
||||||
"""
|
"""
|
||||||
username = "user_"+str(user_id)
|
username = "user_" + str(user_id)
|
||||||
print(103,message)
|
|
||||||
channel_layer = get_channel_layer()
|
channel_layer = get_channel_layer()
|
||||||
async_to_sync(channel_layer.group_send)(
|
async_to_sync(channel_layer.group_send)(
|
||||||
username,
|
username,
|
||||||
{
|
{
|
||||||
"type": "push.message",
|
"type": "push.message",
|
||||||
"json": message
|
"json": message
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MessageCenterCreateSerializer(CustomModelSerializer):
|
class MessageCenterCreateSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
消息中心-新增-序列化器
|
消息中心-新增-序列化器
|
||||||
@@ -122,10 +123,10 @@ class MessageCenterCreateSerializer(CustomModelSerializer):
|
|||||||
# 在保存之前,根据目标类型,把目标用户查询出来并保存
|
# 在保存之前,根据目标类型,把目标用户查询出来并保存
|
||||||
users = initial_data.get('target_user', [])
|
users = initial_data.get('target_user', [])
|
||||||
if target_type in [1]: # 按角色
|
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)
|
users = Users.objects.filter(role__id__in=target_role).values_list('id', flat=True)
|
||||||
if target_type in [2]: # 按部门
|
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)
|
users = Users.objects.filter(dept__id__in=target_dept).values_list('id', flat=True)
|
||||||
if target_type in [3]: # 系统通知
|
if target_type in [3]: # 系统通知
|
||||||
users = Users.objects.values_list('id', flat=True)
|
users = Users.objects.values_list('id', flat=True)
|
||||||
@@ -141,7 +142,7 @@ class MessageCenterCreateSerializer(CustomModelSerializer):
|
|||||||
for user in users:
|
for user in users:
|
||||||
unread_count = MessageCenterTargetUser.objects.filter(users__id=user, is_read=False).count()
|
unread_count = MessageCenterTargetUser.objects.filter(users__id=user, is_read=False).count()
|
||||||
websocket_push(user, message={"sender": 'system', "contentType": 'SYSTEM',
|
websocket_push(user, message={"sender": 'system', "contentType": 'SYSTEM',
|
||||||
"content": '您有一条新消息~', "unread": unread_count})
|
"content": '您有一条新消息~', "unread": unread_count})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -184,7 +185,7 @@ class MessageCenterViewSet(CustomModelViewSet):
|
|||||||
# 主动推送消息
|
# 主动推送消息
|
||||||
unread_count = MessageCenterTargetUser.objects.filter(users__id=user_id, is_read=False).count()
|
unread_count = MessageCenterTargetUser.objects.filter(users__id=user_id, is_read=False).count()
|
||||||
websocket_push(user_id, message={"sender": 'system', "contentType": 'TEXT',
|
websocket_push(user_id, message={"sender": 'system', "contentType": 'TEXT',
|
||||||
"content": '您查看了一条消息~', "unread": unread_count})
|
"content": '您查看了一条消息~', "unread": unread_count})
|
||||||
return DetailResponse(data=serializer.data, msg="获取成功")
|
return DetailResponse(data=serializer.data, msg="获取成功")
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
@@ -195,7 +196,6 @@ class MessageCenterViewSet(CustomModelViewSet):
|
|||||||
self_user_id = self.request.user.id
|
self_user_id = self.request.user.id
|
||||||
# queryset = MessageCenterTargetUser.objects.filter(users__id=self_user_id).order_by('-create_datetime')
|
# queryset = MessageCenterTargetUser.objects.filter(users__id=self_user_id).order_by('-create_datetime')
|
||||||
queryset = MessageCenter.objects.filter(target_user__id=self_user_id)
|
queryset = MessageCenter.objects.filter(target_user__id=self_user_id)
|
||||||
print(queryset)
|
|
||||||
# queryset = self.filter_queryset(queryset)
|
# queryset = self.filter_queryset(queryset)
|
||||||
page = self.paginate_queryset(queryset)
|
page = self.paginate_queryset(queryset)
|
||||||
if page is not None:
|
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)
|
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)
|
queryset = SystemConfig.objects.filter(parent=instance)
|
||||||
serializer = SystemConfigSerializer(queryset, many=True)
|
serializer = SystemConfigSerializer(queryset, many=True)
|
||||||
return serializer.data
|
return serializer.data
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class UserSerializer(CustomModelSerializer):
|
|||||||
exclude = ["password"]
|
exclude = ["password"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"post": {"required": False},
|
"post": {"required": False},
|
||||||
|
"mobile": {"required": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_dept_name_all(self, instance):
|
def get_dept_name_all(self, instance):
|
||||||
@@ -60,9 +61,6 @@ class UserSerializer(CustomModelSerializer):
|
|||||||
return serializer.data
|
return serializer.data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserCreateSerializer(CustomModelSerializer):
|
class UserCreateSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
用户新增-序列化器
|
用户新增-序列化器
|
||||||
@@ -82,10 +80,10 @@ class UserCreateSerializer(CustomModelSerializer):
|
|||||||
"""
|
"""
|
||||||
对密码进行验证
|
对密码进行验证
|
||||||
"""
|
"""
|
||||||
password = self.initial_data.get("password")
|
md5 = hashlib.md5()
|
||||||
if password:
|
md5.update(value.encode('utf-8'))
|
||||||
return make_password(value)
|
md5_password = md5.hexdigest()
|
||||||
return value
|
return make_password(md5_password)
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
data = super().save(**kwargs)
|
data = super().save(**kwargs)
|
||||||
@@ -100,6 +98,7 @@ class UserCreateSerializer(CustomModelSerializer):
|
|||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"post": {"required": False},
|
"post": {"required": False},
|
||||||
|
"mobile": {"required": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -114,14 +113,15 @@ class UserUpdateSerializer(CustomModelSerializer):
|
|||||||
CustomUniqueValidator(queryset=Users.objects.all(), message="账号必须唯一")
|
CustomUniqueValidator(queryset=Users.objects.all(), message="账号必须唯一")
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# password = serializers.CharField(required=False, allow_blank=True)
|
# password = serializers.CharField(required=False, allow_blank=True)
|
||||||
mobile = serializers.CharField(
|
# mobile = serializers.CharField(
|
||||||
max_length=50,
|
# max_length=50,
|
||||||
validators=[
|
# validators=[
|
||||||
CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一")
|
# CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一")
|
||||||
],
|
# ],
|
||||||
allow_blank=True
|
# allow_blank=True
|
||||||
)
|
# )
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
data = super().save(**kwargs)
|
data = super().save(**kwargs)
|
||||||
@@ -136,6 +136,7 @@ class UserUpdateSerializer(CustomModelSerializer):
|
|||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"post": {"required": False, "read_only": True},
|
"post": {"required": False, "read_only": True},
|
||||||
|
"mobile": {"required": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -159,6 +160,7 @@ class UserInfoUpdateSerializer(CustomModelSerializer):
|
|||||||
fields = ['email', 'mobile', 'avatar', 'name', 'gender']
|
fields = ['email', 'mobile', 'avatar', 'name', 'gender']
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"post": {"required": False, "read_only": True},
|
"post": {"required": False, "read_only": True},
|
||||||
|
"mobile": {"required": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -293,6 +295,11 @@ class UserViewSet(CustomModelViewSet):
|
|||||||
'dept_id': dept.id,
|
'dept_id': dept.id,
|
||||||
'dept_name': dept.name
|
'dept_name': dept.name
|
||||||
}
|
}
|
||||||
|
else:
|
||||||
|
result['dept_info'] = {
|
||||||
|
'dept_id': None,
|
||||||
|
'dept_name': "暂无部门"
|
||||||
|
}
|
||||||
role = getattr(user, 'role', None)
|
role = getattr(user, 'role', None)
|
||||||
if role:
|
if role:
|
||||||
result['role_info'] = role.values('id', 'name', 'key')
|
result['role_info'] = role.values('id', 'name', 'key')
|
||||||
|
|||||||
@@ -38,12 +38,16 @@ def CustomExceptionHandler(ex, context):
|
|||||||
# 调用默认的异常处理函数
|
# 调用默认的异常处理函数
|
||||||
response = exception_handler(ex, context)
|
response = exception_handler(ex, context)
|
||||||
if isinstance(ex, AuthenticationFailed):
|
if isinstance(ex, AuthenticationFailed):
|
||||||
code = 401
|
# 如果是身份验证错误
|
||||||
code_type = response.data.get('detail').code
|
if response and response.data.get('detail') == "Given token not valid for any token type":
|
||||||
if code_type == 'no_active_account':
|
code = 401
|
||||||
code=400
|
msg = ex.detail
|
||||||
|
elif response and response.data.get('detail') == "Token is blacklisted":
|
||||||
|
# token在黑名单
|
||||||
return ErrorResponse(status=HTTP_401_UNAUTHORIZED)
|
return ErrorResponse(status=HTTP_401_UNAUTHORIZED)
|
||||||
msg = ex.detail
|
else:
|
||||||
|
code = 401
|
||||||
|
msg = ex.detail
|
||||||
elif isinstance(ex,Http404):
|
elif isinstance(ex,Http404):
|
||||||
code = 400
|
code = 400
|
||||||
msg = "接口地址不正确"
|
msg = "接口地址不正确"
|
||||||
|
|||||||
@@ -1,28 +1,37 @@
|
|||||||
import logging
|
import logging
|
||||||
import os.path
|
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 loguru import logger
|
||||||
|
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
# 1.🎖️先声明一个类继承logging.Handler(制作一件品如的衣服)
|
# 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,
|
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)
|
super(InterceptTimedRotatingFileHandler, self).__init__(filename)
|
||||||
filename = os.path.abspath(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()
|
when = when.lower()
|
||||||
# 2.🎖️需要本地用不同的文件名做为不同日志的筛选器
|
# 2.🎖️需要本地用不同的文件名做为不同日志的筛选器
|
||||||
|
logger.configure(
|
||||||
|
handlers=[
|
||||||
|
dict(sink=sys.stderr, format=format),
|
||||||
|
],
|
||||||
|
)
|
||||||
self.logger_ = logger.bind(sime=filename, ip="-", port="-", username="张三")
|
self.logger_ = logger.bind(sime=filename, ip="-", port="-", username="张三")
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
key_map = {
|
key_map = {
|
||||||
@@ -74,7 +83,7 @@ class InterceptTimedRotatingFileHandler(RotatingFileHandler):
|
|||||||
# self.logger_.remove(file_key[filename_fmt_key])
|
# self.logger_.remove(file_key[filename_fmt_key])
|
||||||
self.logger_.add(
|
self.logger_.add(
|
||||||
filename_fmt,
|
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,
|
retention=retention,
|
||||||
encoding=encoding,
|
encoding=encoding,
|
||||||
level=self.level,
|
level=self.level,
|
||||||
@@ -100,13 +109,17 @@ class InterceptTimedRotatingFileHandler(RotatingFileHandler):
|
|||||||
# 设置自定义属性
|
# 设置自定义属性
|
||||||
port = "-"
|
port = "-"
|
||||||
ip = "-"
|
ip = "-"
|
||||||
locals_self = frame.f_locals.get('self', None)
|
details = frame.f_locals.get('details', None)
|
||||||
msg = self.format(record)
|
msg = self.format(record)
|
||||||
if locals_self and hasattr(locals_self, 'client_address'):
|
bind = {}
|
||||||
ip, port = locals_self.client_address
|
if details and details.get('client'):
|
||||||
# - 127.0.0.1:56525 -
|
ip, port = details.get('client').split(':')
|
||||||
msg = f"{ip}:{port} - {msg}"
|
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_ \
|
self.logger_ \
|
||||||
.opt(depth=depth, exception=record.exc_info, colors=True) \
|
.opt(depth=depth, exception=record.exc_info, colors=True) \
|
||||||
.bind(ip=ip, port=port) \
|
.bind(**bind) \
|
||||||
.log(level, msg)
|
.log(level, msg)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
7
|
|
||||||
@@ -28,6 +28,6 @@ server {
|
|||||||
proxy_send_timeout 600s;
|
proxy_send_timeout 600s;
|
||||||
real_ip_header X-Forwarded-For;
|
real_ip_header X-Forwarded-For;
|
||||||
rewrite ^/api/(.*)$ /$1 break; #重写
|
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": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.0.10",
|
"@element-plus/icons-vue": "^2.0.10",
|
||||||
"@fast-crud/fast-crud": "^1.9.0",
|
"@fast-crud/fast-crud": "^1.11.10",
|
||||||
"@fast-crud/fast-extends": "^1.9.0",
|
"@fast-crud/fast-extends": "^1.11.10",
|
||||||
"@fast-crud/ui-element": "^1.9.0",
|
"@fast-crud/ui-element": "^1.11.10",
|
||||||
|
"@fast-crud/ui-interface": "^1.11.9",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
|
"autoprefixer": "^10.4.14",
|
||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"countup.js": "^2.3.2",
|
"countup.js": "^2.3.2",
|
||||||
"cropperjs": "^1.5.13",
|
"cropperjs": "^1.5.13",
|
||||||
@@ -32,15 +34,18 @@
|
|||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.0.28",
|
"pinia": "^2.0.28",
|
||||||
"pinia-plugin-persist": "^1.0.0",
|
"pinia-plugin-persist": "^1.0.0",
|
||||||
|
"postcss": "^8.4.21",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"qrcodejs2-fixes": "^0.0.2",
|
"qrcodejs2-fixes": "^0.0.2",
|
||||||
"qs": "^6.11.0",
|
"qs": "^6.11.0",
|
||||||
"screenfull": "^6.0.2",
|
"screenfull": "^6.0.2",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"splitpanes": "^3.1.5",
|
"splitpanes": "^3.1.5",
|
||||||
|
"tailwindcss": "^3.2.7",
|
||||||
"ts-md5": "^1.3.1",
|
"ts-md5": "^1.3.1",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
|
"vue-cropper": "^1.0.8",
|
||||||
"vue-grid-layout": "^3.0.0-beta1",
|
"vue-grid-layout": "^3.0.0-beta1",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
"vue-router": "^4.1.6",
|
"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',
|
two4: 'Links',
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
accountPlaceholder1: 'The user name admin or not is common',
|
accountPlaceholder1: 'Please enter your login account',
|
||||||
accountPlaceholder2: 'Password: 123456',
|
accountPlaceholder2: 'Please enter your login password',
|
||||||
accountPlaceholder3: 'Please enter the verification code',
|
accountPlaceholder3: 'Please enter the verification code',
|
||||||
accountBtnText: 'Sign in',
|
accountBtnText: 'Sign in',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export default {
|
|||||||
two4: '友情链接',
|
two4: '友情链接',
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
accountPlaceholder1: '用户名 admin 或不输均为 common',
|
accountPlaceholder1: '请输入登录账号',
|
||||||
accountPlaceholder2: '密码:123456',
|
accountPlaceholder2: '请输入登录密码',
|
||||||
accountPlaceholder3: '请输入验证码',
|
accountPlaceholder3: '请输入验证码',
|
||||||
accountBtnText: '登 录',
|
accountBtnText: '登 录',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export default {
|
|||||||
two4: '友情連結',
|
two4: '友情連結',
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
accountPlaceholder1: '用戶名admin或不輸均為common',
|
accountPlaceholder1: '請輸入登入賬號',
|
||||||
accountPlaceholder2: '密碼:123456',
|
accountPlaceholder2: '請輸入登入密碼',
|
||||||
accountPlaceholder3: '請輸入驗證碼',
|
accountPlaceholder3: '請輸入驗證碼',
|
||||||
accountBtnText: '登入',
|
accountBtnText: '登入',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
<div class="layout-navbars-breadcrumb-user-icon">
|
<div class="layout-navbars-breadcrumb-user-icon">
|
||||||
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
|
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
|
||||||
<template #reference>
|
<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')">
|
<el-icon :title="$t('message.user.title4')">
|
||||||
<ele-Bell />
|
<ele-Bell />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
||||||
<span class="layout-navbars-breadcrumb-user-link">
|
<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 }}
|
{{ userInfos.userName === '' ? 'common' : userInfos.userName }}
|
||||||
<el-icon class="el-icon--right">
|
<el-icon class="el-icon--right">
|
||||||
<ele-ArrowDown />
|
<ele-ArrowDown />
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item command="/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item>
|
<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 command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item>
|
||||||
<el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
|
<el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
@@ -208,8 +208,8 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//消息中心的未读数量
|
//消息中心的未读数量
|
||||||
import {messageCenterStore} from "/@/stores/messageCenter";
|
import { messageCenterStore } from '/@/stores/messageCenter';
|
||||||
const messageCenter = messageCenterStore()
|
const messageCenter = messageCenterStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import {createApp} from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import {directive} from '/@/utils/directive';
|
import { directive } from '/@/utils/directive';
|
||||||
import {i18n} from '/@/i18n/index';
|
import { i18n } from '/@/i18n/index';
|
||||||
import other from '/@/utils/other';
|
import other from '/@/utils/other';
|
||||||
|
import '/@/assets/style/tailwind.css'; // 先引入tailwind css, 以免element-plus冲突
|
||||||
import ElementPlus from 'element-plus';
|
import ElementPlus from 'element-plus';
|
||||||
import 'element-plus/dist/index.css';
|
import 'element-plus/dist/index.css';
|
||||||
import '/@/theme/index.scss';
|
import '/@/theme/index.scss';
|
||||||
@@ -15,7 +16,7 @@ import fastCrud from './settings.ts';
|
|||||||
import pinia from './stores';
|
import pinia from './stores';
|
||||||
import permission from '/@/plugin/permission/index';
|
import permission from '/@/plugin/permission/index';
|
||||||
// @ts-ignore
|
// @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/icon/default-icon/symbol.js'; //基本彩色图标库
|
||||||
import 'e-icon-picker/index.css'; // 基本样式,包含基本图标
|
import 'e-icon-picker/index.css'; // 基本样式,包含基本图标
|
||||||
import 'font-awesome/css/font-awesome.min.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 eIconList from 'e-icon-picker/icon/default-icon/eIconList.js';
|
||||||
import iconfont from '/@/assets/iconfont/iconfont.json'; //引入json文件
|
import iconfont from '/@/assets/iconfont/iconfont.json'; //引入json文件
|
||||||
import '/@/assets/iconfont/iconfont.css'; //引入css
|
import '/@/assets/iconfont/iconfont.css'; //引入css
|
||||||
|
// 自动注册插件
|
||||||
|
import { scanAndInstallPlugins } from '/@/views/plugins/index';
|
||||||
|
|
||||||
let forIconfont = analyzingIconForIconfont(iconfont); //解析class
|
let forIconfont = analyzingIconForIconfont(iconfont); //解析class
|
||||||
iconList.addIcon(forIconfont.list); // 添加iconfont dvadmin3的icon
|
iconList.addIcon(forIconfont.list); // 添加iconfont dvadmin3的icon
|
||||||
iconList.addIcon(elementPlus); // 添加element plus的图标
|
iconList.addIcon(elementPlus); // 添加element plus的图标
|
||||||
@@ -31,10 +35,12 @@ iconList.addIcon(fontAwesome470); // 添加fontAwesome 470版本的图标
|
|||||||
|
|
||||||
let app = createApp(App);
|
let app = createApp(App);
|
||||||
|
|
||||||
|
scanAndInstallPlugins(app);
|
||||||
|
|
||||||
app.use(eIconPicker, {
|
app.use(eIconPicker, {
|
||||||
addIconList: eIconList, //全局添加图标
|
addIconList: eIconList, //全局添加图标
|
||||||
removeIconList: [], //全局删除图标
|
removeIconList: [], //全局删除图标
|
||||||
zIndex: 3100, //选择器弹层的最低层,全局配置
|
zIndex: 3100, //选择器弹层的最低层,全局配置
|
||||||
});
|
});
|
||||||
|
|
||||||
pinia.use(piniaPersist);
|
pinia.use(piniaPersist);
|
||||||
@@ -42,6 +48,6 @@ directive(app);
|
|||||||
other.elSvg(app);
|
other.elSvg(app);
|
||||||
|
|
||||||
app.use(permission);
|
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();
|
app.config.globalProperties.mittBus = mitt();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useRoutesList } from '/@/stores/routesList';
|
|||||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||||
import { useMenuApi } from '/@/api/menu/index';
|
import { useMenuApi } from '/@/api/menu/index';
|
||||||
import { handleMenu } from '../utils/menu';
|
import { handleMenu } from '../utils/menu';
|
||||||
import {BtnPermissionStore} from "/@/plugin/permission/store.permission";
|
import { BtnPermissionStore } from '/@/plugin/permission/store.permission';
|
||||||
|
|
||||||
const menuApi = useMenuApi();
|
const menuApi = useMenuApi();
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { setLogger } from '@fast-crud/fast-crud';
|
|||||||
import ui from '@fast-crud/ui-element';
|
import ui from '@fast-crud/ui-element';
|
||||||
import { request } from '/@/utils/service';
|
import { request } from '/@/utils/service';
|
||||||
//扩展包
|
//扩展包
|
||||||
import {FsExtendsEditor} from "@fast-crud/fast-extends";
|
import { FsExtendsEditor } from '@fast-crud/fast-extends';
|
||||||
import "@fast-crud/fast-extends/dist/style.css";
|
import '@fast-crud/fast-extends/dist/style.css';
|
||||||
export default {
|
export default {
|
||||||
async install(app: any, options: any) {
|
async install(app: any, options: any) {
|
||||||
// 先安装ui
|
// 先安装ui
|
||||||
@@ -18,7 +18,7 @@ export default {
|
|||||||
//i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件
|
//i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件
|
||||||
// 此处配置公共的dictRequest(字典请求)
|
// 此处配置公共的dictRequest(字典请求)
|
||||||
async dictRequest({ dict }: any) {
|
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配置
|
//公共crud配置
|
||||||
commonOptions() {
|
commonOptions() {
|
||||||
@@ -28,6 +28,9 @@ export default {
|
|||||||
//你项目后台接口大概率与fast-crud所需要的返回结构不一致,所以需要配置此项
|
//你项目后台接口大概率与fast-crud所需要的返回结构不一致,所以需要配置此项
|
||||||
//请参考文档http://fast-crud.docmirror.cn/api/crud-options/request.html
|
//请参考文档http://fast-crud.docmirror.cn/api/crud-options/request.html
|
||||||
transformQuery: ({ page, form, sort }: any) => {
|
transformQuery: ({ page, form, sort }: any) => {
|
||||||
|
if (sort.asc !== undefined) {
|
||||||
|
form['ordering'] = `${sort.asc ? '' : '-'}${sort.prop}`;
|
||||||
|
}
|
||||||
//转换为你pageRequest所需要的请求参数结构
|
//转换为你pageRequest所需要的请求参数结构
|
||||||
return { page: page.currentPage, limit: page.pageSize, ...form };
|
return { page: page.currentPage, limit: page.pageSize, ...form };
|
||||||
},
|
},
|
||||||
@@ -55,11 +58,11 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
//富文本
|
//富文本
|
||||||
app.use(FsExtendsEditor,{
|
app.use(FsExtendsEditor, {
|
||||||
wangEditor:{
|
wangEditor: {
|
||||||
width:300,
|
width: 300,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
setLogger({ level: 'error' });
|
setLogger({ level: 'error' });
|
||||||
// 设置自动染色
|
// 设置自动染色
|
||||||
const dictComponentList = ['dict-cascader', 'dict-checkbox', 'dict-radio', 'dict-select', 'dict-switch', 'dict-tree'];
|
const dictComponentList = ['dict-cascader', 'dict-checkbox', 'dict-radio', 'dict-select', 'dict-switch', 'dict-tree'];
|
||||||
|
|||||||
@@ -5,11 +5,17 @@
|
|||||||
|
|
||||||
// 用户信息
|
// 用户信息
|
||||||
export interface UserInfosState {
|
export interface UserInfosState {
|
||||||
authBtnList: string[];
|
avatar: string;
|
||||||
photo: string;
|
|
||||||
roles: string[];
|
|
||||||
time: number;
|
|
||||||
userName: 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 {
|
export interface UserInfosStates {
|
||||||
userInfos: UserInfosState;
|
userInfos: UserInfosState;
|
||||||
@@ -92,3 +98,6 @@ export interface ThemeConfigStates {
|
|||||||
export interface DictionaryStates {
|
export interface DictionaryStates {
|
||||||
data: any;
|
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 { defineStore } from 'pinia';
|
||||||
import Cookies from 'js-cookie';
|
|
||||||
import { UserInfosStates } from './interface';
|
import { UserInfosStates } from './interface';
|
||||||
import { Session } from '/@/utils/storage';
|
import { Session } from '/@/utils/storage';
|
||||||
import { request } from '../utils/service';
|
import { request } from '../utils/service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户信息
|
* 用户信息
|
||||||
* @methods setUserInfos 设置用户信息
|
* @methods setUserInfos 设置用户信息
|
||||||
@@ -11,14 +9,37 @@ import { request } from '../utils/service';
|
|||||||
export const useUserInfo = defineStore('userInfo', {
|
export const useUserInfo = defineStore('userInfo', {
|
||||||
state: (): UserInfosStates => ({
|
state: (): UserInfosStates => ({
|
||||||
userInfos: {
|
userInfos: {
|
||||||
|
avatar: '',
|
||||||
userName: '',
|
userName: '',
|
||||||
photo: '',
|
name: '',
|
||||||
time: 0,
|
email: '',
|
||||||
roles: [],
|
mobile: '',
|
||||||
authBtnList: [],
|
gender: '',
|
||||||
|
dept_info: {
|
||||||
|
dept_id: 0,
|
||||||
|
dept_name: '',
|
||||||
|
},
|
||||||
|
role_info: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
actions: {
|
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() {
|
async setUserInfos() {
|
||||||
// 存储用户信息到浏览器缓存
|
// 存储用户信息到浏览器缓存
|
||||||
if (Session.get('userInfo')) {
|
if (Session.get('userInfo')) {
|
||||||
@@ -26,17 +47,21 @@ export const useUserInfo = defineStore('userInfo', {
|
|||||||
} else {
|
} else {
|
||||||
let userInfos: any = await this.getApiUserInfo();
|
let userInfos: any = await this.getApiUserInfo();
|
||||||
this.userInfos.userName = userInfos.data.name;
|
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.avatar = userInfos.data.avatar;
|
||||||
this.userInfos.time = new Date().getTime()
|
this.userInfos.name = userInfos.data.name;
|
||||||
this.userInfos.roles = ['admin']
|
this.userInfos.email = userInfos.data.email;
|
||||||
Session.set('userInfo', this.userInfos)
|
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() {
|
async getApiUserInfo() {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/system/user/user_info/',
|
url: '/api/system/user/user_info/',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
});
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -62,15 +62,15 @@ function createService() {
|
|||||||
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 400:
|
case 400:
|
||||||
Local.clear();
|
// Local.clear();
|
||||||
Session.clear();
|
// Session.clear();
|
||||||
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
||||||
window.location.reload();
|
// window.location.reload();
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
Local.clear();
|
Local.clear();
|
||||||
Session.clear();
|
Session.clear();
|
||||||
dataAxios.msg = '登录授权过期,请重新登录';
|
dataAxios.msg = '登录认证失败,请重新登录';
|
||||||
ElMessageBox.alert(dataAxios.msg, '提示', {
|
ElMessageBox.alert(dataAxios.msg, '提示', {
|
||||||
confirmButtonText: 'OK',
|
confirmButtonText: 'OK',
|
||||||
callback: (action: Action) => {
|
callback: (action: Action) => {
|
||||||
@@ -100,7 +100,15 @@ function createService() {
|
|||||||
error.message = '请求错误';
|
error.message = '请求错误';
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
error.message = '未授权,请登录';
|
Local.clear();
|
||||||
|
Session.clear();
|
||||||
|
error.message = '登录授权过期,请重新登录';
|
||||||
|
ElMessageBox.alert(error.message, '提示', {
|
||||||
|
confirmButtonText: 'OK',
|
||||||
|
callback: (action: Action) => {
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
})
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
error.message = '拒绝访问';
|
error.message = '拒绝访问';
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
* @param {String} jsonString 需要解析的 json 字符串
|
* @param {String} jsonString 需要解析的 json 字符串
|
||||||
* @param {String} defaultValue 默认值
|
* @param {String} defaultValue 默认值
|
||||||
*/
|
*/
|
||||||
import { uiContext } from "@fast-crud/fast-crud";
|
import { uiContext } from '@fast-crud/fast-crud';
|
||||||
|
|
||||||
export function parse(jsonString = "{}", defaultValue = {}) {
|
export function parse(jsonString = '{}', defaultValue = {}) {
|
||||||
let result = defaultValue;
|
let result = defaultValue;
|
||||||
try {
|
try {
|
||||||
result = JSON.parse(jsonString);
|
result = JSON.parse(jsonString);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,8 +21,8 @@ export function parse(jsonString = "{}", defaultValue = {}) {
|
|||||||
* @param {String} msg 状态信息
|
* @param {String} msg 状态信息
|
||||||
* @param {Number} code 状态码
|
* @param {Number} code 状态码
|
||||||
*/
|
*/
|
||||||
export function response(data = {}, msg = "", code = 0) {
|
export function response(data = {}, msg = '', code = 0) {
|
||||||
return [200, { code, msg, data }];
|
return [200, { code, msg, data }];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,8 +30,8 @@ export function response(data = {}, msg = "", code = 0) {
|
|||||||
* @param {Any} data 返回值
|
* @param {Any} data 返回值
|
||||||
* @param {String} msg 状态信息
|
* @param {String} msg 状态信息
|
||||||
*/
|
*/
|
||||||
export function responseSuccess(data = {}, msg = "成功") {
|
export function responseSuccess(data = {}, msg = '成功') {
|
||||||
return response(data, msg);
|
return response(data, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,30 +40,62 @@ export function responseSuccess(data = {}, msg = "成功") {
|
|||||||
* @param {String} msg 状态信息
|
* @param {String} msg 状态信息
|
||||||
* @param {Number} code 状态码
|
* @param {Number} code 状态码
|
||||||
*/
|
*/
|
||||||
export function responseError(data = {}, msg = "请求失败", code = 500) {
|
export function responseError(data = {}, msg = '请求失败', code = 500) {
|
||||||
return response(data, msg, code);
|
return response(data, msg, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 记录和显示错误
|
* @description 记录和显示错误
|
||||||
* @param {Error} error 错误对象
|
* @param {Error} error 错误对象
|
||||||
*/
|
*/
|
||||||
export function errorLog(error:any,notification=true) {
|
export function errorLog(error: any, notification = true) {
|
||||||
// 打印到控制台
|
// 打印到控制台
|
||||||
console.error(error);
|
console.error(error);
|
||||||
// 显示提示
|
// 显示提示
|
||||||
if(notification){
|
if (notification) {
|
||||||
uiContext.get().notification.error({ message: error.message });
|
uiContext.get().notification.error({ message: error.message });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 创建一个错误
|
* @description 创建一个错误
|
||||||
* @param {String} msg 错误信息
|
* @param {String} msg 错误信息
|
||||||
*/
|
*/
|
||||||
export function errorCreate(msg:any,notification=true) {
|
export function errorCreate(msg: any, notification = true) {
|
||||||
const error = new Error(msg);
|
const error = new Error(msg);
|
||||||
errorLog(error,notification);
|
errorLog(error, notification);
|
||||||
// throw error;
|
// 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')
|
const token = Session.get('token')
|
||||||
if(!token){
|
if(!token){
|
||||||
message.warning('websocket认证失败')
|
// message.warning('websocket认证失败')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const wsUrl = `${getWsBaseURL()}ws/${token}/`
|
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 { 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 const apiPrefix = '/api/system/area/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id,
|
url: apiPrefix + id,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddObj(obj: AddReq) {
|
export function AddObj(obj: AddReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateObj(obj: EditReq) {
|
export function UpdateObj(obj: EditReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + obj.id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DelObj(id: DelReq) {
|
export function DelObj(id: DelReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + id + '/',
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
data: { id },
|
data: { id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import * as api from './api';
|
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';
|
import { dictionary } from '/@/utils/dictionary';
|
||||||
interface CreateCrudOptionsTypes {
|
import { successMessage } from '/@/utils/message';
|
||||||
crudOptions: CrudOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -26,8 +23,8 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
* @param row
|
* @param row
|
||||||
* @returns {Promise<unknown>}
|
* @returns {Promise<unknown>}
|
||||||
*/
|
*/
|
||||||
const loadContentMethod = (tree: any, treeNode: any, resolve: any) => {
|
const loadContentMethod = (tree: any, treeNode: any, resolve: Function) => {
|
||||||
api.GetList({ pcode: tree.code }).then((res: any) => {
|
pageRequest({ pcode: tree.code }).then((res: APIResponseData) => {
|
||||||
resolve(res.data);
|
resolve(res.data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -40,6 +37,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
|
rowHandle: {
|
||||||
|
//固定右侧
|
||||||
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
|
buttons: {
|
||||||
|
view: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
iconRight: 'Edit',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
iconRight: 'Delete',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
pagination: {
|
pagination: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@@ -90,8 +105,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
treeNode: true,
|
treeNode: true,
|
||||||
width: 160,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -108,6 +125,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 90,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -124,6 +144,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -140,6 +163,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rules: [
|
rules: [
|
||||||
@@ -153,6 +179,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
initials: {
|
initials: {
|
||||||
title: '首字母',
|
title: '首字母',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -169,14 +198,26 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
search: {
|
search: {
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
width: 90,
|
|
||||||
type: 'dict-radio',
|
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({
|
dict: dict({
|
||||||
data: dictionary('button_status_bool'),
|
data: dictionary('button_status_bool'),
|
||||||
}),
|
}),
|
||||||
form: {
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,18 +6,10 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './crud';
|
import { createCrudOptions } from './crud';
|
||||||
// crud组件的ref
|
|
||||||
const crudRef = ref();
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||||
// crud 配置的ref
|
|
||||||
const crudBinding = ref();
|
|
||||||
// 暴露的方法
|
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
|
||||||
// 你的crud配置
|
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
|
||||||
// 初始化crud配置
|
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { request } from '/@/utils/service';
|
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';
|
import XEUtils from 'xe-utils';
|
||||||
|
|
||||||
export const apiPrefix = '/api/system/system_config/';
|
export const apiPrefix = '/api/system/system_config/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -52,10 +52,10 @@ export function GetAssociationTable() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveContent (data:any) {
|
export function saveContent(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + 'save_content/',
|
url: apiPrefix + 'save_content/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,49 @@
|
|||||||
import { request } from '/@/utils/service';
|
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 const apiPrefix = '/api/system/dept/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id,
|
url: apiPrefix + id,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddObj(obj: AddReq) {
|
export function AddObj(obj: AddReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateObj(obj: EditReq) {
|
export function UpdateObj(obj: EditReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + obj.id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DelObj(id: DelReq) {
|
export function DelObj(obj: DelReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
data: { id },
|
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 * 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 { verifyPhone } from '/@/utils/toolsValidate';
|
||||||
import { request } from '/@/utils/service';
|
|
||||||
import { dictionary } from '/@/utils/dictionary';
|
import { dictionary } from '/@/utils/dictionary';
|
||||||
interface CreateCrudOptionsTypes {
|
import { successMessage } from '/@/utils/message';
|
||||||
crudOptions: CrudOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const { getFormRef, getFormData } = crudExpose;
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -64,14 +60,26 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
fiexd: 'right',
|
fiexd: 'right',
|
||||||
width: 310,
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
buttons: {
|
buttons: {
|
||||||
|
view: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
iconRight: 'Edit',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
iconRight: 'Delete',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
addChildren: {
|
addChildren: {
|
||||||
text: '添加子级',
|
text: '添加子级',
|
||||||
type: 'warning',
|
type: 'text',
|
||||||
click(context) {
|
click(context) {
|
||||||
const rowId = context.row.id;
|
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',
|
type: 'email',
|
||||||
message: '请输入正确的邮箱地址',
|
message: '请输入正确的邮箱地址',
|
||||||
|
// @ts-ignore
|
||||||
trigger: ['blur', 'change'],
|
trigger: ['blur', 'change'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -217,16 +226,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
type: 'dict-radio',
|
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({
|
dict: dict({
|
||||||
data: dictionary('button_status_bool'),
|
data: dictionary('button_status_bool'),
|
||||||
}),
|
}),
|
||||||
form: {
|
|
||||||
value: true,
|
|
||||||
component: {
|
|
||||||
span: 12,
|
|
||||||
placeholder: '请选择状态',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,26 +1,272 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page>
|
<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>
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import * as api from './api';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { ElForm, ElTree, FormRules } from 'element-plus';
|
||||||
import { createCrudOptions } from './crud';
|
import { ref, onMounted, reactive, toRaw, watch } from 'vue';
|
||||||
// crud组件的ref
|
import XEUtils from 'xe-utils';
|
||||||
const crudRef = ref();
|
import { errorMessage, successMessage } from '../../../utils/message';
|
||||||
// crud 配置的ref
|
|
||||||
const crudBinding = ref();
|
interface Tree {
|
||||||
// 暴露的方法
|
id: number;
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
name: string;
|
||||||
// 你的crud配置
|
status: boolean;
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
children?: Tree[];
|
||||||
// 初始化crud配置
|
}
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
|
||||||
|
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(() => {
|
onMounted(() => {
|
||||||
crudExpose.doRefresh();
|
getData();
|
||||||
});
|
});
|
||||||
</script>
|
</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 * 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 { dictionary } from '/@/utils/dictionary';
|
||||||
import {nextTick, ref} from 'vue';
|
import { inject, nextTick, ref } from 'vue';
|
||||||
|
import { successMessage } from '/@/utils/message';
|
||||||
|
|
||||||
interface CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
crudOptions: CrudOptions;
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExpose: CrudExpose; subDictRef: any }): CreateCrudOptionsTypes {
|
|
||||||
const pageRequest = async (query: PageQuery) => {
|
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -21,6 +18,10 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
|||||||
const addRequest = async ({ form }: AddReq) => {
|
const addRequest = async ({ form }: AddReq) => {
|
||||||
return await api.AddObj(form);
|
return await api.AddObj(form);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//权限判定
|
||||||
|
const hasPermissions = inject('$hasPermissions');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
@@ -30,25 +31,38 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
|||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
width: 360,
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
buttons: {
|
buttons: {
|
||||||
|
view: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
iconRight: 'Edit',
|
||||||
|
type: 'text',
|
||||||
|
show: hasPermissions('dictionary:Update'),
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
iconRight: 'Delete',
|
||||||
|
type: 'text',
|
||||||
|
show: hasPermissions('dictionary:Delete'),
|
||||||
|
},
|
||||||
custom: {
|
custom: {
|
||||||
text: '字典配置',
|
text: '字典配置',
|
||||||
type: 'success',
|
type: 'text',
|
||||||
|
show: hasPermissions('dictionary:Update'),
|
||||||
tooltip: {
|
tooltip: {
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
content: '字典配置',
|
content: '字典配置',
|
||||||
},
|
},
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
click: (context: any) => {
|
click: (ctx: any) => {
|
||||||
const {row} = context
|
const { row } = ctx;
|
||||||
subDictRef.value.drawer = true;
|
context!.subDictRef.value.drawer = true;
|
||||||
nextTick(()=>{
|
nextTick(() => {
|
||||||
subDictRef.value.setSearchFormData({ form: { parent: row.id } });
|
context!.subDictRef.value.setSearchFormData({ form: { parent: row.id } });
|
||||||
subDictRef.value.doRefresh();
|
context!.subDictRef.value.doRefresh();
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -65,7 +79,7 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
|||||||
formatter: (context) => {
|
formatter: (context) => {
|
||||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||||
let index = context.index ?? 1;
|
let index = context.index ?? 1;
|
||||||
let pagination = crudExpose.crudBinding.value.pagination;
|
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||||
},
|
},
|
||||||
@@ -105,6 +119,9 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column: {
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -129,6 +146,9 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column: {
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -149,34 +169,36 @@ export const createCrudOptions = function ({ crudExpose, subDictRef }: { crudExp
|
|||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
title: '状态',
|
title: '状态',
|
||||||
width: 90,
|
|
||||||
search: {
|
search: {
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: 'dict-radio',
|
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({
|
dict: dict({
|
||||||
data: dictionary('button_status_bool'),
|
data: dictionary('button_status_bool'),
|
||||||
}),
|
}),
|
||||||
component: {
|
|
||||||
props: {
|
|
||||||
options: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
rules: [
|
|
||||||
// 表单校验规则
|
|
||||||
{ required: true, message: '状态必填项' },
|
|
||||||
],
|
|
||||||
value: true,
|
|
||||||
component: {
|
|
||||||
placeholder: '请选择状态',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
sort: {
|
sort: {
|
||||||
title: '排序',
|
title: '排序',
|
||||||
width: 90,
|
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
column: {
|
||||||
|
minWidth: 80,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
value: 1,
|
value: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,24 +6,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, watch } from 'vue';
|
import { ref, onMounted, defineAsyncComponent } from 'vue';
|
||||||
import type { Ref } from 'vue';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
|
||||||
import { createCrudOptions } from './crud';
|
import { createCrudOptions } from './crud';
|
||||||
import subDict from './subDict/index.vue';
|
const subDict = defineAsyncComponent(() => import('./subDict/index.vue'));
|
||||||
|
|
||||||
//字典配置ref
|
|
||||||
const subDictRef = ref();
|
const subDictRef = ref();
|
||||||
// crud组件的ref
|
|
||||||
const crudRef = ref();
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { subDictRef } });
|
||||||
// crud 配置的ref
|
|
||||||
const crudBinding = ref();
|
|
||||||
// 暴露的方法
|
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
|
||||||
// 你的crud配置
|
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose, subDictRef });
|
|
||||||
// 初始化crud配置
|
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
import { request } from '/@/utils/service';
|
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 const apiPrefix = '/api/system/dictionary/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id,
|
url: apiPrefix + id,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddObj(obj: AddReq) {
|
export function AddObj(obj: AddReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateObj(obj: EditReq) {
|
export function UpdateObj(obj: EditReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + obj.id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DelObj(id: DelReq) {
|
export function DelObj(id: DelReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + id + '/',
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
data: { id },
|
data: { id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
import { dict, UserPageQuery, AddReq, DelReq, EditReq, CrudOptions, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||||
import { request } from '/@/utils/service';
|
|
||||||
import { dictionary } from '/@/utils/dictionary';
|
import { dictionary } from '/@/utils/dictionary';
|
||||||
import {watch} from "vue";
|
|
||||||
interface CreateCrudOptionsTypes {
|
|
||||||
crudOptions: CrudOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -30,6 +25,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
|
rowHandle: {
|
||||||
|
//固定右侧
|
||||||
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
|
buttons: {
|
||||||
|
view: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
iconRight: 'Edit',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
iconRight: 'Delete',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
columns: {
|
columns: {
|
||||||
_index: {
|
_index: {
|
||||||
title: '序号',
|
title: '序号',
|
||||||
@@ -42,7 +55,7 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
formatter: (context) => {
|
formatter: (context) => {
|
||||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||||
let index = context.index ?? 1;
|
let index = context.index ?? 1;
|
||||||
let pagination = crudExpose.crudBinding.value.pagination;
|
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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-page>
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||||
</fs-page>
|
</fs-page>
|
||||||
@@ -7,27 +7,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {ref, onMounted, defineProps, computed, watch} from 'vue';
|
import { ref, onMounted, defineAsyncComponent } from 'vue';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './crud';
|
import { createCrudOptions } from './crud';
|
||||||
|
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||||
import { ElMessageBox } from 'element-plus';
|
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 drawer = ref(false);
|
||||||
|
|
||||||
//抽屉关闭确认
|
//抽屉关闭确认
|
||||||
const handleClose = (done: () => void) => {
|
const handleClose = (done: () => void) => {
|
||||||
|
|
||||||
ElMessageBox.confirm('您确定要关闭?', {
|
ElMessageBox.confirm('您确定要关闭?', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
@@ -40,11 +30,13 @@ const handleClose = (done: () => void) => {
|
|||||||
// catch error
|
// 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(() => {
|
onMounted(() => {
|
||||||
// console.log(48,currentRow)
|
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
import { request } from '/@/utils/service';
|
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 const apiPrefix = '/api/system/file/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
data: query,
|
data: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id,
|
url: apiPrefix + id,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddObj(obj: AddReq) {
|
export function AddObj(obj: AddReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateObj(obj: EditReq) {
|
export function UpdateObj(obj: EditReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + obj.id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DelObj(id: DelReq) {
|
export function DelObj(id: DelReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + id + '/',
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
data: { id },
|
data: { id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
import { UserPageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||||
import { request } from '/@/utils/service';
|
|
||||||
import { dictionary } from '/@/utils/dictionary';
|
|
||||||
interface CreateCrudOptionsTypes {
|
|
||||||
crudOptions: CrudOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -35,6 +30,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
|
rowHandle: {
|
||||||
|
//固定右侧
|
||||||
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
|
buttons: {
|
||||||
|
view: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
iconRight: 'Edit',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
iconRight: 'Delete',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
columns: {
|
columns: {
|
||||||
_index: {
|
_index: {
|
||||||
title: '序号',
|
title: '序号',
|
||||||
@@ -47,8 +60,8 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
formatter: (context) => {
|
formatter: (context) => {
|
||||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||||
let index = context.index ?? 1;
|
let index = context.index ?? 1;
|
||||||
let pagination = crudExpose.crudBinding.value.pagination;
|
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -81,6 +94,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入文件名称',
|
placeholder: '请输入文件名称',
|
||||||
@@ -93,13 +109,18 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
search: {
|
search: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
|
column:{
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
md5sum: {
|
md5sum: {
|
||||||
title: '文件MD5',
|
title: '文件MD5',
|
||||||
width: 200,
|
|
||||||
search: {
|
search: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,18 +6,10 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './crud';
|
import { createCrudOptions } from './crud';
|
||||||
// crud组件的ref
|
|
||||||
const crudRef = ref();
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||||
// crud 配置的ref
|
|
||||||
const crudBinding = ref();
|
|
||||||
// 暴露的方法
|
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
|
||||||
// 你的crud配置
|
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
|
||||||
// 初始化crud配置
|
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
import { request } from '/@/utils/service';
|
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 const apiPrefix = '/api/system/login_log/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
data: query,
|
data: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id,
|
url: apiPrefix + id,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddObj(obj: AddReq) {
|
export function AddObj(obj: AddReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateObj(obj: EditReq) {
|
export function UpdateObj(obj: EditReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + obj.id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DelObj(id: DelReq) {
|
export function DelObj(id: DelReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + id + '/',
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
data: { id },
|
data: { id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
import { UserPageQuery, AddReq, DelReq, EditReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict } from '@fast-crud/fast-crud';
|
||||||
import { request } from '/@/utils/service';
|
|
||||||
import { dictionary } from '/@/utils/dictionary';
|
|
||||||
interface CreateCrudOptionsTypes {
|
|
||||||
crudOptions: CrudOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -36,7 +31,12 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
|
fixed:'right',
|
||||||
|
width: 100,
|
||||||
buttons: {
|
buttons: {
|
||||||
|
view: {
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
edit: {
|
edit: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@@ -54,6 +54,12 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
width: '70px',
|
width: '70px',
|
||||||
columnSetDisabled: true, //禁止在列设置中选择
|
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: {
|
search: {
|
||||||
@@ -84,8 +90,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
search: {
|
search: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
width: 140,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
component: {
|
component: {
|
||||||
@@ -98,8 +106,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
search: {
|
search: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
width: 130,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
component: {
|
component: {
|
||||||
@@ -113,8 +123,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
disabled: true,
|
disabled: true,
|
||||||
width: 180,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入运营商',
|
placeholder: '请输入运营商',
|
||||||
@@ -123,8 +135,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
continent: {
|
continent: {
|
||||||
title: '大州',
|
title: '大州',
|
||||||
width: 80,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 90,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
component: {
|
component: {
|
||||||
@@ -135,8 +149,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
country: {
|
country: {
|
||||||
title: '国家',
|
title: '国家',
|
||||||
width: 80,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 90,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入国家',
|
placeholder: '请输入国家',
|
||||||
@@ -146,8 +162,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
province: {
|
province: {
|
||||||
title: '省份',
|
title: '省份',
|
||||||
width: 80,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 80,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入省份',
|
placeholder: '请输入省份',
|
||||||
@@ -157,8 +175,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
city: {
|
city: {
|
||||||
title: '城市',
|
title: '城市',
|
||||||
width: 80,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 80,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入城市',
|
placeholder: '请输入城市',
|
||||||
@@ -169,8 +189,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
district: {
|
district: {
|
||||||
title: '县区',
|
title: '县区',
|
||||||
key: '',
|
key: '',
|
||||||
width: 80,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 80,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入县区',
|
placeholder: '请输入县区',
|
||||||
@@ -181,6 +203,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
area_code: {
|
area_code: {
|
||||||
title: '区域代码',
|
title: '区域代码',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 90,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入区域代码',
|
placeholder: '请输入区域代码',
|
||||||
@@ -190,8 +215,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
country_english: {
|
country_english: {
|
||||||
title: '英文全称',
|
title: '英文全称',
|
||||||
width: 120,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入英文全称',
|
placeholder: '请输入英文全称',
|
||||||
@@ -202,6 +229,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
country_code: {
|
country_code: {
|
||||||
title: '简称',
|
title: '简称',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入简称',
|
placeholder: '请输入简称',
|
||||||
@@ -213,6 +243,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
title: '经度',
|
title: '经度',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入经度',
|
placeholder: '请输入经度',
|
||||||
@@ -224,6 +257,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
title: '纬度',
|
title: '纬度',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入纬度',
|
placeholder: '请输入纬度',
|
||||||
@@ -233,26 +269,31 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
login_type: {
|
login_type: {
|
||||||
title: '登录类型',
|
title: '登录类型',
|
||||||
type: 'select',
|
type: 'dict-select',
|
||||||
search: {
|
search: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
dict: {
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: '普通登录', value: 1 },
|
{ label: '普通登录', value: 1 },
|
||||||
{ label: '微信扫码登录', value: 2 },
|
{ label: '微信扫码登录', value: 2 },
|
||||||
],
|
],
|
||||||
|
}),
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请选择登录类型',
|
placeholder: '请选择登录类型',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
component: { props: { color: 'auto' } }, // 自动染色
|
|
||||||
},
|
},
|
||||||
os: {
|
os: {
|
||||||
title: '操作系统',
|
title: '操作系统',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入操作系统',
|
placeholder: '请输入操作系统',
|
||||||
@@ -262,6 +303,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
browser: {
|
browser: {
|
||||||
title: '浏览器名',
|
title: '浏览器名',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入浏览器名',
|
placeholder: '请输入浏览器名',
|
||||||
@@ -272,6 +316,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
title: 'agent信息',
|
title: 'agent信息',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: '请输入agent信息',
|
placeholder: '请输入agent信息',
|
||||||
|
|||||||
@@ -6,18 +6,10 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './crud';
|
import { createCrudOptions } from './crud';
|
||||||
// crud组件的ref
|
|
||||||
const crudRef = ref();
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||||
// crud 配置的ref
|
|
||||||
const crudBinding = ref();
|
|
||||||
// 暴露的方法
|
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
|
||||||
// 你的crud配置
|
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
|
||||||
// 初始化crud配置
|
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
import { request } from '/@/utils/service';
|
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 const apiPrefix = '/api/system/operation_log/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id,
|
url: apiPrefix + id,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddObj(obj: AddReq) {
|
export function AddObj(obj: AddReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateObj(obj: EditReq) {
|
export function UpdateObj(obj: EditReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + obj.id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DelObj(id: DelReq) {
|
export function DelObj(id: DelReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + id + '/',
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
data: { id },
|
data: { id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
import { UserPageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||||
import { request } from '/@/utils/service';
|
|
||||||
import { dictionary } from '/@/utils/dictionary';
|
|
||||||
interface CreateCrudOptionsTypes {
|
|
||||||
crudOptions: CrudOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -36,7 +31,12 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
|
fixed:'right',
|
||||||
|
width: 100,
|
||||||
buttons: {
|
buttons: {
|
||||||
|
view: {
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
edit: {
|
edit: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@@ -57,8 +57,8 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
formatter: (context) => {
|
formatter: (context) => {
|
||||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||||
let index = context.index ?? 1;
|
let index = context.index ?? 1;
|
||||||
let pagination = crudExpose.crudBinding.value.pagination;
|
let pagination = crudExpose!.crudBinding.value.pagination;
|
||||||
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -91,6 +91,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
component: {
|
component: {
|
||||||
@@ -104,6 +107,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
component: {
|
component: {
|
||||||
@@ -142,6 +148,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
search: {
|
search: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
component: {
|
component: {
|
||||||
@@ -165,6 +174,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
component: {
|
component: {
|
||||||
@@ -176,6 +188,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
request_browser: {
|
request_browser: {
|
||||||
title: '请求浏览器',
|
title: '请求浏览器',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
@@ -187,6 +202,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
@@ -199,6 +217,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
@@ -210,6 +231,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
@@ -217,6 +241,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
creator_name: {
|
creator_name: {
|
||||||
title: '操作人',
|
title: '操作人',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,18 +6,10 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './crud';
|
import { createCrudOptions } from './crud';
|
||||||
// crud组件的ref
|
|
||||||
const crudRef = ref();
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||||
// crud 配置的ref
|
|
||||||
const crudBinding = ref();
|
|
||||||
// 暴露的方法
|
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
|
||||||
// 你的crud配置
|
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
|
||||||
// 初始化crud配置
|
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item class="login-animation3">
|
<el-form-item class="login-animation3" v-if="isShowCaptcha">
|
||||||
<el-col :span="15">
|
<el-col :span="15">
|
||||||
<el-input
|
<el-input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-button class="login-content-captcha">
|
<el-button class="login-content-captcha">
|
||||||
<el-image :src="ruleForm.captchaImgBase" @click="refreshCaptcha" />
|
<el-image :src="ruleForm.captchaImgBase" @click="refreshCaptcha" />
|
||||||
<!-- TODO 完成点击刷新验证码 -->
|
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -59,7 +58,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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 { useRoute, useRouter } from 'vue-router';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
@@ -74,7 +73,8 @@ import { NextLoading } from '/@/utils/loading';
|
|||||||
import * as loginApi from '/@/views/system/login/api';
|
import * as loginApi from '/@/views/system/login/api';
|
||||||
import { useUserInfo } from '/@/stores/userInfo';
|
import { useUserInfo } from '/@/stores/userInfo';
|
||||||
import { DictionaryStore } from '/@/stores/dictionary';
|
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';
|
import { Md5 } from 'ts-md5';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -89,8 +89,8 @@ export default defineComponent({
|
|||||||
const state = reactive({
|
const state = reactive({
|
||||||
isShowPassword: false,
|
isShowPassword: false,
|
||||||
ruleForm: {
|
ruleForm: {
|
||||||
username: 'superadmin',
|
username: '',
|
||||||
password: 'admin123456',
|
password: '',
|
||||||
captcha: '',
|
captcha: '',
|
||||||
captchaKey: '',
|
captchaKey: '',
|
||||||
captchaImgBase: '',
|
captchaImgBase: '',
|
||||||
@@ -103,6 +103,10 @@ export default defineComponent({
|
|||||||
const currentTime = computed(() => {
|
const currentTime = computed(() => {
|
||||||
return formatAxis(new Date());
|
return formatAxis(new Date());
|
||||||
});
|
});
|
||||||
|
// 是否关闭验证码
|
||||||
|
const isShowCaptcha = computed(() => {
|
||||||
|
return SystemConfigStore().systemConfig['base.captcha_state'];
|
||||||
|
});
|
||||||
|
|
||||||
const getCaptcha = async () => {
|
const getCaptcha = async () => {
|
||||||
loginApi.getCaptcha().then((ret: any) => {
|
loginApi.getCaptcha().then((ret: any) => {
|
||||||
@@ -117,31 +121,43 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const loginClick = async () => {
|
const loginClick = async () => {
|
||||||
loginApi.login({ ...state.ruleForm, password: Md5.hashStr(state.ruleForm.password) }).then((ret: any) => {
|
loginApi.login({ ...state.ruleForm, password: Md5.hashStr(state.ruleForm.password) }).then((res: any) => {
|
||||||
Session.set('token', ret.data.access);
|
if (res.code === 2000) {
|
||||||
Cookies.set('username', ret.data.name);
|
Session.set('token', res.data.access);
|
||||||
if (!themeConfig.value.isRequestRoutes) {
|
Cookies.set('username', res.data.name);
|
||||||
// 前端控制路由,2、请注意执行顺序
|
if (!themeConfig.value.isRequestRoutes) {
|
||||||
initFrontEndControlRoutes();
|
// 前端控制路由,2、请注意执行顺序
|
||||||
loginSuccess();
|
initFrontEndControlRoutes();
|
||||||
} else {
|
loginSuccess();
|
||||||
// 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
} else {
|
||||||
// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
|
// 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||||
initBackEndControlRoutes();
|
// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
|
||||||
// 执行完 initBackEndControlRoutes,再执行 signInSuccess
|
initBackEndControlRoutes();
|
||||||
loginSuccess();
|
// 执行完 initBackEndControlRoutes,再执行 signInSuccess
|
||||||
|
loginSuccess();
|
||||||
|
}
|
||||||
|
} else if (res.code === 4000) {
|
||||||
|
// 登录错误之后,刷新验证码
|
||||||
|
refreshCaptcha();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const getUserInfo = () => {
|
const getUserInfo = () => {
|
||||||
useUserInfo().setUserInfos();
|
useUserInfo().setUserInfos();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const enterClickLogin = (e: any) => {
|
||||||
|
if (e.keyCode == 13 || e.keyCode == 100) {
|
||||||
|
loginClick();
|
||||||
|
}
|
||||||
|
};
|
||||||
// 登录成功后的跳转
|
// 登录成功后的跳转
|
||||||
const loginSuccess = () => {
|
const loginSuccess = () => {
|
||||||
//登录成功获取用户信息,获取系统字典数据
|
//登录成功获取用户信息,获取系统字典数据
|
||||||
getUserInfo();
|
getUserInfo();
|
||||||
//获取所有字典
|
//获取所有字典
|
||||||
DictionaryStore().getSystemDictionarys();
|
DictionaryStore().getSystemDictionarys();
|
||||||
|
|
||||||
// 初始化登录成功时间问候语
|
// 初始化登录成功时间问候语
|
||||||
let currentTimeInfo = currentTime.value;
|
let currentTimeInfo = currentTime.value;
|
||||||
// 登录成功,跳到转首页
|
// 登录成功,跳到转首页
|
||||||
@@ -164,12 +180,19 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getCaptcha();
|
getCaptcha();
|
||||||
|
//获取系统配置
|
||||||
|
SystemConfigStore().getSystemConfigs();
|
||||||
|
window.addEventListener('keyup', enterClickLogin, false);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('keyup', enterClickLogin, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
refreshCaptcha,
|
refreshCaptcha,
|
||||||
loginClick,
|
loginClick,
|
||||||
loginSuccess,
|
loginSuccess,
|
||||||
|
isShowCaptcha,
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import { request } from '/@/utils/service';
|
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 const apiPrefix = '/api/system/menu/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id+'/',
|
url: apiPrefix + id + '/',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -32,10 +33,33 @@ export function UpdateObj(obj: EditReq) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DelObj(id: DelReq) {
|
export function DelObj(obj: DelReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'delete',
|
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>
|
<template>
|
||||||
<el-drawer
|
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||||
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>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {ref, onMounted} from 'vue';
|
import { ref, defineProps, watch } from 'vue';
|
||||||
import {useExpose, useCrud} from '@fast-crud/fast-crud';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import {createCrudOptions} from './curd';
|
import { createCrudOptions } from './crud';
|
||||||
import {ElMessageBox} from "element-plus";
|
|
||||||
import * as api from './api'
|
|
||||||
// 弹窗是否显示
|
|
||||||
const drawer = ref(false)
|
|
||||||
// 当前选择的菜单信息
|
// 当前选择的菜单信息
|
||||||
const selectOptions:any = ref({name:null})
|
let selectOptions: any = ref({ name: null });
|
||||||
|
const props = defineProps<{
|
||||||
|
selectMenu: object;
|
||||||
|
}>();
|
||||||
|
|
||||||
//抽屉关闭确认
|
watch(props.selectMenu, (val: any) => {
|
||||||
const handleClose = (done: () => void) => {
|
if (!val.is_catalog) {
|
||||||
ElMessageBox.confirm('您确定要关闭?', {
|
selectOptions.value = val;
|
||||||
confirmButtonText: '确定',
|
doRefresh();
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({drawer, selectOptions,initGet})
|
const { crudRef, crudBinding, crudExpose, context } = useFs({ createCrudOptions, context: { selectOptions } });
|
||||||
|
const { doRefresh } = crudExpose;
|
||||||
|
|
||||||
|
defineExpose({ selectOptions });
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,37 +1,8 @@
|
|||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose, menuButtonRef }: { crudExpose: CrudExpose; menuButtonRef: any }): CreateCrudOptionsTypes {
|
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const hasPermissions: any = inject('$hasPermissions');
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
//验证路由地址
|
|
||||||
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) => {
|
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -41,21 +12,11 @@ export const createCrudOptions = function ({ crudExpose, menuButtonRef }: { crud
|
|||||||
const delRequest = async ({ row }: DelReq) => {
|
const delRequest = async ({ row }: DelReq) => {
|
||||||
return await api.DelObj(row.id);
|
return await api.DelObj(row.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addRequest = async ({ form }: AddReq) => {
|
const addRequest = async ({ form }: AddReq) => {
|
||||||
return await api.AddObj(form);
|
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 {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
@@ -64,404 +25,7 @@ export const createCrudOptions = function ({ crudExpose, menuButtonRef }: { crud
|
|||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
pagination: {
|
columns: {},
|
||||||
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: '状态必填项' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,30 +1,384 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page>
|
<fs-page>
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
<el-row class="mx-2">
|
||||||
<MenuButton ref="menuButtonRef"></MenuButton>
|
<el-col :span="4" class="p-1">
|
||||||
</fs-page>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import * as api from './api';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import * as menuButoonApi from './components/menuButton/api';
|
||||||
import { createCrudOptions } from './crud';
|
import { ElForm, ElTree, FormRules } from 'element-plus';
|
||||||
import MenuButton from './components/menuButton/index.vue'
|
import { ref, onMounted, watch, reactive, toRaw, defineAsyncComponent, nextTick, shallowRef } from 'vue';
|
||||||
const menuButtonRef = ref()
|
import XEUtils from 'xe-utils';
|
||||||
defineExpose(menuButtonRef);
|
import { errorMessage, successMessage } from '../../../utils/message';
|
||||||
// crud组件的ref
|
|
||||||
const crudRef = ref();
|
interface Tree {
|
||||||
// crud 配置的ref
|
id: number;
|
||||||
const crudBinding = ref();
|
name: string;
|
||||||
// 暴露的方法
|
status: boolean;
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
children?: Tree[];
|
||||||
// 你的crud配置
|
}
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose,menuButtonRef });
|
|
||||||
// 初始化crud配置
|
interface APIResponseData {
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
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(() => {
|
onMounted(() => {
|
||||||
crudExpose.doRefresh();
|
getData();
|
||||||
});
|
});
|
||||||
</script>
|
</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 * as api from './api';
|
||||||
import {dict, useCompute, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions,} from "@fast-crud/fast-crud";
|
import { dict, useCompute, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
||||||
import tableSelector from "/@/components/tableSelector/index.vue"
|
import tableSelector from '/@/components/tableSelector/index.vue';
|
||||||
import {shallowRef, computed, ref} from "vue";
|
import {shallowRef, computed, ref, inject} from 'vue';
|
||||||
import manyToMany from "/@/components/manyToMany/index.vue"
|
import manyToMany from '/@/components/manyToMany/index.vue';
|
||||||
|
|
||||||
const {compute} = useCompute()
|
const { compute } = useCompute();
|
||||||
|
|
||||||
interface CreateCrudOptionsTypes {
|
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 ({
|
const viewRequest = async ({ row }: { row: any }) => {
|
||||||
crudExpose,
|
return await api.GetObj(row.id);
|
||||||
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 }) => {
|
const IsReadFunc = computed(() => {
|
||||||
return await api.GetObj(row.id);
|
return tabActivted.value === 'receive';
|
||||||
}
|
});
|
||||||
|
|
||||||
const IsReadFunc = computed(() => {
|
//权限判定
|
||||||
return tabActivted.value === 'receive'
|
const hasPermissions = inject("$hasPermissions")
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
pageRequest,
|
pageRequest,
|
||||||
addRequest,
|
addRequest,
|
||||||
editRequest,
|
editRequest,
|
||||||
delRequest
|
delRequest,
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
buttons: {
|
fixed:'right',
|
||||||
edit: {
|
width:150,
|
||||||
show: false
|
buttons: {
|
||||||
},
|
edit: {
|
||||||
view: {
|
show: false,
|
||||||
click({index,row}) {
|
},
|
||||||
crudExpose.openView({index,row})
|
view: {
|
||||||
if(tabActivted.value === 'receive'){
|
text:"查看",
|
||||||
viewRequest({row})
|
type:'text',
|
||||||
crudExpose.doRefresh()
|
iconRight:'View',
|
||||||
}
|
show:hasPermissions("messageCenter:Search"),
|
||||||
}
|
click({ index, row }) {
|
||||||
}
|
crudExpose.openView({ index, row });
|
||||||
}
|
if (tabActivted.value === 'receive') {
|
||||||
},
|
viewRequest({ row });
|
||||||
columns: {
|
crudExpose.doRefresh();
|
||||||
id: {
|
}
|
||||||
title: 'id',
|
},
|
||||||
form: {
|
},
|
||||||
show: false,
|
remove: {
|
||||||
}
|
iconRight: 'Delete',
|
||||||
},
|
type: 'text',
|
||||||
title: {
|
show:hasPermissions('messageCenter:Delete')
|
||||||
title: '标题',
|
},
|
||||||
search: {
|
},
|
||||||
show: true,
|
},
|
||||||
},
|
columns: {
|
||||||
type: ["text", "colspan"],
|
id: {
|
||||||
form: {
|
title: 'id',
|
||||||
rules: [ // 表单校验规则
|
form: {
|
||||||
{
|
show: false,
|
||||||
required: true,
|
},
|
||||||
message: '必填项'
|
},
|
||||||
}
|
title: {
|
||||||
],
|
title: '标题',
|
||||||
component: {span: 24, placeholder: '请输入标题'}
|
search: {
|
||||||
}
|
show: true,
|
||||||
},
|
},
|
||||||
is_read: {
|
type: ['text', 'colspan'],
|
||||||
title: '是否已读',
|
column:{
|
||||||
type: 'dict-select',
|
minWidth: 120,
|
||||||
column: {
|
},
|
||||||
show: IsReadFunc,
|
form: {
|
||||||
},
|
rules: [
|
||||||
dict: dict({
|
// 表单校验规则
|
||||||
data: [
|
{
|
||||||
{label: '已读', value: true, color: 'success'},
|
required: true,
|
||||||
{label: '未读', value: false, color: 'danger'}
|
message: '必填项',
|
||||||
]
|
},
|
||||||
}),
|
],
|
||||||
form: {
|
component: { span: 24, placeholder: '请输入标题' },
|
||||||
show: false
|
},
|
||||||
}
|
},
|
||||||
},
|
is_read: {
|
||||||
target_type: {
|
title: '是否已读',
|
||||||
title: '目标类型',
|
type: 'dict-select',
|
||||||
type: ['dict-radio', 'colspan'],
|
column: {
|
||||||
width: 120,
|
show: IsReadFunc.value,
|
||||||
dict: dict({
|
},
|
||||||
data: [{value: 0, label: '按用户'}, {value: 1, label: '按角色'}, {
|
dict: dict({
|
||||||
value: 2,
|
data: [
|
||||||
label: '按部门'
|
{ label: '已读', value: true, color: 'success' },
|
||||||
}, {value: 3, label: '通知公告'}]
|
{ label: '未读', value: false, color: 'danger' },
|
||||||
}),
|
],
|
||||||
form: {
|
}),
|
||||||
component: {
|
form: {
|
||||||
optionName: "el-radio-button"
|
show: false,
|
||||||
},
|
},
|
||||||
rules: [
|
},
|
||||||
{
|
target_type: {
|
||||||
required: true,
|
title: '目标类型',
|
||||||
message: '必选项',
|
type: ['dict-radio', 'colspan'],
|
||||||
trigger: ['blur', 'change']
|
column:{
|
||||||
}
|
minWidth: 120,
|
||||||
]
|
},
|
||||||
}
|
dict: dict({
|
||||||
},
|
data: [
|
||||||
target_user: {
|
{ value: 0, label: '按用户' },
|
||||||
title: '目标用户',
|
{ value: 1, label: '按角色' },
|
||||||
search: {
|
{
|
||||||
disabled: true
|
value: 2,
|
||||||
},
|
label: '按部门',
|
||||||
width: 130,
|
},
|
||||||
form: {
|
{ value: 3, label: '通知公告' },
|
||||||
component: {
|
],
|
||||||
name: shallowRef(tableSelector),
|
}),
|
||||||
vModel: "modelValue",
|
form: {
|
||||||
displayLabel: compute(({row}) => {
|
component: {
|
||||||
if (row) {
|
optionName: 'el-radio-button',
|
||||||
return row.user_info;
|
},
|
||||||
}
|
rules: [
|
||||||
return null
|
{
|
||||||
}),
|
required: true,
|
||||||
tableConfig: {
|
message: '必选项',
|
||||||
url: '/api/system/user/',
|
// @ts-ignore
|
||||||
label: 'name',
|
trigger: ['blur', 'change'],
|
||||||
value: 'id',
|
},
|
||||||
isMultiple: true,
|
],
|
||||||
columns: [{
|
},
|
||||||
prop: 'name',
|
},
|
||||||
label: '用户名称',
|
target_user: {
|
||||||
width: 120
|
title: '目标用户',
|
||||||
}, {
|
search: {
|
||||||
prop: 'phone',
|
disabled: true,
|
||||||
label: '用户电话',
|
},
|
||||||
width: 120
|
form: {
|
||||||
}]
|
component: {
|
||||||
}
|
name: shallowRef(tableSelector),
|
||||||
},
|
vModel: 'modelValue',
|
||||||
show: compute(({form}) => {
|
displayLabel: compute(({ row }) => {
|
||||||
return form.target_type === 0
|
if (row) {
|
||||||
}),
|
return row.user_info;
|
||||||
rules: [ // 表单校验规则
|
}
|
||||||
{
|
return null;
|
||||||
required: true,
|
}),
|
||||||
message: '必填项'
|
tableConfig: {
|
||||||
}
|
url: '/api/system/user/',
|
||||||
]
|
label: 'name',
|
||||||
},
|
value: 'id',
|
||||||
column: {
|
isMultiple: true,
|
||||||
show: false,
|
columns: [
|
||||||
component: {
|
{
|
||||||
name: shallowRef(manyToMany),
|
prop: 'name',
|
||||||
vModel: "modelValue",
|
label: '用户名称',
|
||||||
bindValue: compute(({row}) => {
|
width: 120,
|
||||||
return row.user_info
|
},
|
||||||
}),
|
{
|
||||||
displayLabel: 'name'
|
prop: 'phone',
|
||||||
}
|
label: '用户电话',
|
||||||
}
|
width: 120,
|
||||||
},
|
},
|
||||||
target_role: {
|
],
|
||||||
title: '目标角色',
|
},
|
||||||
search: {
|
},
|
||||||
disabled: true
|
show: compute(({ form }) => {
|
||||||
},
|
return form.target_type === 0;
|
||||||
width: 130,
|
}),
|
||||||
form: {
|
rules: [
|
||||||
component: {
|
// 表单校验规则
|
||||||
name: shallowRef(tableSelector),
|
{
|
||||||
vModel: "modelValue",
|
required: true,
|
||||||
displayLabel: compute(({row}) => {
|
message: '必填项',
|
||||||
if (row) {
|
},
|
||||||
return row.role_info;
|
],
|
||||||
}
|
},
|
||||||
return null
|
column: {
|
||||||
}),
|
show: false,
|
||||||
tableConfig: {
|
component: {
|
||||||
url: '/api/system/role/',
|
name: shallowRef(manyToMany),
|
||||||
label: 'name',
|
vModel: 'modelValue',
|
||||||
value: 'id',
|
bindValue: compute(({ row }) => {
|
||||||
isMultiple: true,
|
return row.user_info;
|
||||||
columns: [{
|
}),
|
||||||
prop: 'name',
|
displayLabel: 'name',
|
||||||
label: '角色名称'
|
},
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
prop: 'key',
|
target_role: {
|
||||||
label: '权限标识'
|
title: '目标角色',
|
||||||
}]
|
search: {
|
||||||
}
|
disabled: true,
|
||||||
},
|
},
|
||||||
show: compute(({form}) => {
|
width: 130,
|
||||||
return form.target_type === 1
|
form: {
|
||||||
}),
|
component: {
|
||||||
rules: [ // 表单校验规则
|
name: shallowRef(tableSelector),
|
||||||
{
|
vModel: 'modelValue',
|
||||||
required: true,
|
displayLabel: compute(({ row }) => {
|
||||||
message: '必填项'
|
if (row) {
|
||||||
}
|
return row.role_info;
|
||||||
]
|
}
|
||||||
},
|
return null;
|
||||||
column: {
|
}),
|
||||||
show: false,
|
tableConfig: {
|
||||||
component: {
|
url: '/api/system/role/',
|
||||||
name: shallowRef(manyToMany),
|
label: 'name',
|
||||||
vModel: "modelValue",
|
value: 'id',
|
||||||
bindValue: compute(({row}) => {
|
isMultiple: true,
|
||||||
return row.role_info
|
columns: [
|
||||||
}),
|
{
|
||||||
displayLabel: 'name'
|
prop: 'name',
|
||||||
}
|
label: '角色名称',
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
target_dept: {
|
prop: 'key',
|
||||||
title: '目标部门',
|
label: '权限标识',
|
||||||
search: {
|
},
|
||||||
disabled: true
|
],
|
||||||
},
|
},
|
||||||
width: 130,
|
},
|
||||||
type: 'table-selector',
|
show: compute(({ form }) => {
|
||||||
form: {
|
return form.target_type === 1;
|
||||||
component: {
|
}),
|
||||||
name: shallowRef(tableSelector),
|
rules: [
|
||||||
vModel: "modelValue",
|
// 表单校验规则
|
||||||
displayLabel: compute(({form}) => {
|
{
|
||||||
return form.target_dept_name;
|
required: true,
|
||||||
}),
|
message: '必填项',
|
||||||
tableConfig: {
|
},
|
||||||
url: '/api/system/dept/all_dept/',
|
],
|
||||||
label: 'name',
|
},
|
||||||
value: 'id',
|
column: {
|
||||||
isTree: true,
|
show: false,
|
||||||
isMultiple: true,
|
component: {
|
||||||
columns: [{
|
name: shallowRef(manyToMany),
|
||||||
prop: 'name',
|
vModel: 'modelValue',
|
||||||
label: '部门名称'
|
bindValue: compute(({ row }) => {
|
||||||
},
|
return row.role_info;
|
||||||
{
|
}),
|
||||||
prop: 'status_label',
|
displayLabel: 'name',
|
||||||
label: '状态'
|
},
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
prop: 'parent_name',
|
target_dept: {
|
||||||
label: '父级部门'
|
title: '目标部门',
|
||||||
}]
|
search: {
|
||||||
}
|
disabled: true,
|
||||||
},
|
},
|
||||||
show: compute(({form}) => {
|
width: 130,
|
||||||
return form.target_type === 2
|
type: 'table-selector',
|
||||||
}),
|
form: {
|
||||||
rules: [ // 表单校验规则
|
component: {
|
||||||
{
|
name: shallowRef(tableSelector),
|
||||||
required: true,
|
vModel: 'modelValue',
|
||||||
message: '必填项'
|
displayLabel: compute(({ form }) => {
|
||||||
}
|
return form.target_dept_name;
|
||||||
]
|
}),
|
||||||
},
|
tableConfig: {
|
||||||
column: {
|
url: '/api/system/dept/all_dept/',
|
||||||
show: false,
|
label: 'name',
|
||||||
component: {
|
value: 'id',
|
||||||
name: shallowRef(manyToMany),
|
isTree: true,
|
||||||
vModel: "modelValue",
|
isMultiple: true,
|
||||||
bindValue: compute(({row}) => {
|
columns: [
|
||||||
return row.dept_info
|
{
|
||||||
}),
|
prop: 'name',
|
||||||
displayLabel: 'name'
|
label: '部门名称',
|
||||||
}
|
},
|
||||||
}
|
{
|
||||||
},
|
prop: 'status_label',
|
||||||
content: {
|
label: '状态',
|
||||||
title: '内容',
|
},
|
||||||
column: {
|
{
|
||||||
width: 300,
|
prop: 'parent_name',
|
||||||
show: false
|
label: '父级部门',
|
||||||
},
|
},
|
||||||
type: ["editor-wang5", "colspan"],
|
],
|
||||||
form: {
|
},
|
||||||
rules: [ // 表单校验规则
|
},
|
||||||
{
|
show: compute(({ form }) => {
|
||||||
required: true,
|
return form.target_type === 2;
|
||||||
message: '必填项'
|
}),
|
||||||
}
|
rules: [
|
||||||
],
|
// 表单校验规则
|
||||||
component: {
|
{
|
||||||
disabled: true,
|
required: true,
|
||||||
id: "1", // 当同一个页面有多个editor时,需要配置不同的id
|
message: '必填项',
|
||||||
editorConfig: {
|
},
|
||||||
// 是否只读
|
],
|
||||||
readOnly: compute((context) => {
|
},
|
||||||
const {mode} = context
|
column: {
|
||||||
if (mode === 'add') {
|
show: false,
|
||||||
return false
|
component: {
|
||||||
}
|
name: shallowRef(manyToMany),
|
||||||
return true;
|
vModel: 'modelValue',
|
||||||
}),
|
bindValue: compute(({ row }) => {
|
||||||
},
|
return row.dept_info;
|
||||||
uploader: {
|
}),
|
||||||
type: "form",
|
displayLabel: 'name',
|
||||||
buildUrl(res: any) {
|
},
|
||||||
return res.url;
|
},
|
||||||
}
|
},
|
||||||
}
|
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 { request } from '/@/utils/service';
|
||||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
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) {
|
export function GetUserInfo(query: PageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/system/user/user_info/',
|
url: '/api/system/user/user_info/',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新用户信息
|
* 更新用户信息
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
export function updateUserInfo(data: AddReq) {
|
export function updateUserInfo(data: AddReq) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/system/user/update_user_info/',
|
url: '/api/system/user/update_user_info/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取自己接收的消息
|
* 获取自己接收的消息
|
||||||
* @param query
|
* @param query
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export function GetSelfReceive (query:PageQuery) {
|
export function GetSelfReceive(query: PageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/system/message_center/get_self_receive/',
|
url: '/api/system/message_center/get_self_receive/',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@@ -42,9 +40,24 @@ export function GetSelfReceive (query:PageQuery) {
|
|||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
export function UpdatePassword(data: EditReq) {
|
export function UpdatePassword(data: EditReq) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/system/user/change_password/',
|
url: '/api/system/user/change_password/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
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>
|
<template>
|
||||||
<div class="personal layout-pd">
|
<div class="personal layout-pd">
|
||||||
<el-row>
|
<el-row>
|
||||||
<!-- 个人信息 -->
|
<!-- 个人信息 -->
|
||||||
<el-col :xs="24" :sm="16">
|
<el-col :xs="24" :sm="16">
|
||||||
<el-card shadow="hover" header="个人信息">
|
<el-card shadow="hover" header="个人信息">
|
||||||
<div class="personal-user">
|
<div class="personal-user">
|
||||||
<div class="personal-user-left">
|
<div class="personal-user-left">
|
||||||
<el-upload class="h100 personal-user-left-upload" :action="uploadAvatar.action" :headers="uploadAvatar.headers" multiple :limit="1">
|
<avatarSelector v-model="selectImgVisible" @uploadImg="uploadImg" ref="avatarSelectorRef"></avatarSelector>
|
||||||
<img v-if="state.personalForm.avatar" :src="state.personalForm.avatar" />
|
</div>
|
||||||
<img v-else src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
|
<div class="personal-user-right">
|
||||||
</el-upload>
|
<el-row>
|
||||||
</div>
|
<el-col :span="24" class="personal-title mb18"
|
||||||
<div class="personal-user-right">
|
>{{ currentTime }},{{ state.personalForm.username }},生活变的再糟糕,也不妨碍我变得更好!
|
||||||
<el-row>
|
</el-col>
|
||||||
<el-col :span="24" class="personal-title mb18">{{ currentTime }},{{state.personalForm.username}},生活变的再糟糕,也不妨碍我变得更好! </el-col>
|
<el-col :span="24">
|
||||||
<el-col :span="24">
|
<el-row>
|
||||||
<el-row>
|
<el-col :xs="24" :sm="8" class="personal-item mb6">
|
||||||
<el-col :xs="24" :sm="8" class="personal-item mb6">
|
<div class="personal-item-label">昵称:</div>
|
||||||
<div class="personal-item-label">昵称:</div>
|
<div class="personal-item-value">{{ state.personalForm.name }}</div>
|
||||||
<div class="personal-item-value">{{state.personalForm.name}}</div>
|
</el-col>
|
||||||
</el-col>
|
<el-col :xs="24" :sm="16" class="personal-item mb6">
|
||||||
<el-col :xs="24" :sm="16" class="personal-item mb6">
|
<div class="personal-item-label">部门:</div>
|
||||||
<div class="personal-item-label">部门:</div>
|
<div class="personal-item-value">
|
||||||
<div class="personal-item-value">
|
<el-tag>{{ state.personalForm.dept_info.dept_name }}</el-tag>
|
||||||
<el-tag >{{state.personalForm.dept_info.dept_name}}</el-tag>
|
</div>
|
||||||
</div>
|
</el-col>
|
||||||
</el-col>
|
</el-row>
|
||||||
</el-row>
|
</el-col>
|
||||||
</el-col>
|
<el-col :span="24">
|
||||||
<el-col :span="24">
|
<el-row>
|
||||||
<el-row>
|
<el-col :xs="24" :sm="24" class="personal-item mb6">
|
||||||
<el-col :xs="24" :sm="24" class="personal-item mb6">
|
<div class="personal-item-label">角色:</div>
|
||||||
<div class="personal-item-label">角色:</div>
|
<div class="personal-item-value">
|
||||||
<div class="personal-item-value">
|
<el-tag v-for="(item, index) in state.personalForm.role_info" :key="index">{{ item.name }}</el-tag>
|
||||||
<el-tag v-for="(item,index) in state.personalForm.role_info" :key="index">{{item.name}}</el-tag>
|
</div>
|
||||||
</div>
|
</el-col>
|
||||||
</el-col>
|
</el-row>
|
||||||
</el-row>
|
</el-col>
|
||||||
</el-col>
|
</el-row>
|
||||||
</el-row>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-card>
|
||||||
</el-card>
|
</el-col>
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- 消息通知 -->
|
<!-- 消息通知 -->
|
||||||
<el-col :xs="24" :sm="8" class="pl15 personal-info">
|
<el-col :xs="24" :sm="8" class="pl15 personal-info">
|
||||||
<el-card shadow="hover">
|
<el-card shadow="hover">
|
||||||
<template #header>
|
<template #header>
|
||||||
<span>消息通知</span>
|
<span>消息通知</span>
|
||||||
<span class="personal-info-more" @click="msgMore">更多</span>
|
<span class="personal-info-more" @click="msgMore">更多</span>
|
||||||
</template>
|
</template>
|
||||||
<div class="personal-info-box">
|
<div class="personal-info-box">
|
||||||
<ul class="personal-info-ul">
|
<ul class="personal-info-ul">
|
||||||
<li v-for="(v, k) in state.newsInfoList" :key="k" class="personal-info-li">
|
<li v-for="(v, k) in state.newsInfoList" :key="k" class="personal-info-li">
|
||||||
<div class="personal-info-li-title">
|
<div class="personal-info-li-title">[{{ v.creator_name }},{{ v.create_datetime }}] {{ v.title }}</div>
|
||||||
[{{v.creator_name}},{{v.create_datetime}}] {{v.title}}
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
</el-card>
|
||||||
</div>
|
</el-col>
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- 更新信息 -->
|
<!-- 更新信息 -->
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
|
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
|
||||||
<div class="personal-edit-title">基本信息</div>
|
<div class="personal-edit-title">基本信息</div>
|
||||||
<el-form :model="state.personalForm" ref="userInfoFormRef" :rules="rules" size="default" label-width="50px" class="mt35 mb35">
|
<el-form :model="state.personalForm" ref="userInfoFormRef" :rules="rules" size="default" label-width="50px" class="mt35 mb35">
|
||||||
<el-row :gutter="35">
|
<el-row :gutter="35">
|
||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||||
<el-form-item label="昵称" prop="name">
|
<el-form-item label="昵称" prop="name">
|
||||||
<el-input v-model="state.personalForm.name" placeholder="请输入昵称" clearable></el-input>
|
<el-input v-model="state.personalForm.name" placeholder="请输入昵称" clearable></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||||
<el-form-item label="邮箱">
|
<el-form-item label="邮箱">
|
||||||
<el-input v-model="state.personalForm.email" placeholder="请输入邮箱" clearable></el-input>
|
<el-input v-model="state.personalForm.email" placeholder="请输入邮箱" clearable></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||||
<el-form-item label="手机" prop="mobile">
|
<el-form-item label="手机" prop="mobile">
|
||||||
<el-input v-model="state.personalForm.mobile" placeholder="请输入手机" clearable></el-input>
|
<el-input v-model="state.personalForm.mobile" placeholder="请输入手机" clearable></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||||
<el-form-item label="性别">
|
<el-form-item label="性别">
|
||||||
<el-select v-model="state.personalForm.gender" placeholder="请选择性别" clearable class="w100">
|
<el-select v-model="state.personalForm.gender" placeholder="请选择性别" clearable class="w100">
|
||||||
<el-option label="男" :value="1"></el-option>
|
<el-option label="男" :value="1"></el-option>
|
||||||
<el-option label="女" :value="0"></el-option>
|
<el-option label="女" :value="0"></el-option>
|
||||||
<el-option label="保密" :value="2"></el-option>
|
<el-option label="保密" :value="2"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="submitForm">
|
<el-button type="primary" @click="submitForm">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<ele-Position />
|
<ele-Position />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
更新个人信息
|
更新个人信息
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div class="personal-edit-title mb15">账号安全</div>
|
<div class="personal-edit-title mb15">账号安全</div>
|
||||||
<div class="personal-edit-safe-box">
|
<div class="personal-edit-safe-box">
|
||||||
<div class="personal-edit-safe-item">
|
<div class="personal-edit-safe-item">
|
||||||
<div class="personal-edit-safe-item-left">
|
<div class="personal-edit-safe-item-left">
|
||||||
<div class="personal-edit-safe-item-left-label">账户密码</div>
|
<div class="personal-edit-safe-item-left-label">账户密码</div>
|
||||||
<div class="personal-edit-safe-item-left-value">当前密码强度:强</div>
|
<div class="personal-edit-safe-item-left-value">当前密码强度:强</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="personal-edit-safe-item-right">
|
<div class="personal-edit-safe-item-right">
|
||||||
<el-button text type="primary" @click="passwordFormShow=true">立即修改</el-button>
|
<el-button text type="primary" @click="passwordFormShow = true">立即修改</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="personal-edit-safe-box">
|
<div class="personal-edit-safe-box">
|
||||||
<div class="personal-edit-safe-item">
|
<div class="personal-edit-safe-item">
|
||||||
<div class="personal-edit-safe-item-left">
|
<div class="personal-edit-safe-item-left">
|
||||||
<div class="personal-edit-safe-item-left-label">密保手机</div>
|
<div class="personal-edit-safe-item-left-label">密保手机</div>
|
||||||
<div class="personal-edit-safe-item-left-value">已绑定手机:{{state.personalForm.mobile}}</div>
|
<div class="personal-edit-safe-item-left-value">已绑定手机:{{ state.personalForm.mobile }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="personal-edit-safe-item-right">
|
<div class="personal-edit-safe-item-right">
|
||||||
<!-- <el-button text type="primary">立即修改</el-button>-->
|
<!-- <el-button text type="primary">立即修改</el-button>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="personal-edit-safe-box">
|
<div class="personal-edit-safe-box">
|
||||||
<div class="personal-edit-safe-item">
|
<div class="personal-edit-safe-item">
|
||||||
<div class="personal-edit-safe-item-left">
|
<div class="personal-edit-safe-item-left">
|
||||||
<div class="personal-edit-safe-item-left-label">绑定邮箱</div>
|
<div class="personal-edit-safe-item-left-label">绑定邮箱</div>
|
||||||
<div class="personal-edit-safe-item-left-value">已绑定邮箱:{{state.personalForm.email}}</div>
|
<div class="personal-edit-safe-item-left-value">已绑定邮箱:{{ state.personalForm.email }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="personal-edit-safe-item-right">
|
<div class="personal-edit-safe-item-right">
|
||||||
<!-- <el-button text type="primary">立即设置</el-button>-->
|
<!-- <el-button text type="primary">立即设置</el-button>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<!-- 密码修改-->
|
<!-- 密码修改-->
|
||||||
<el-dialog v-model="passwordFormShow" title="密码修改">
|
<el-dialog v-model="passwordFormShow" title="密码修改">
|
||||||
<el-form
|
<el-form
|
||||||
ref="userPasswordFormRef"
|
ref="userPasswordFormRef"
|
||||||
:model="userPasswordInfo"
|
:model="userPasswordInfo"
|
||||||
required-asterisk
|
required-asterisk
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
:rules="passwordRules"
|
:rules="passwordRules"
|
||||||
center
|
center
|
||||||
>
|
>
|
||||||
<el-form-item label="原密码" required prop="oldPassword">
|
<el-form-item label="原密码" required prop="oldPassword">
|
||||||
<el-input
|
<el-input v-model="userPasswordInfo.oldPassword" placeholder="请输入原始密码" clearable></el-input>
|
||||||
v-model="userPasswordInfo.oldPassword"
|
</el-form-item>
|
||||||
placeholder="请输入原始密码"
|
<el-form-item required prop="newPassword" label="新密码">
|
||||||
clearable
|
<el-input type="password" v-model="userPasswordInfo.newPassword" placeholder="请输入新密码" show-password clearable></el-input>
|
||||||
></el-input>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item required prop="newPassword2" label="确认密码">
|
||||||
<el-form-item required prop="newPassword" label="新密码">
|
<el-input type="password" v-model="userPasswordInfo.newPassword2" placeholder="请再次输入新密码" show-password clearable></el-input>
|
||||||
<el-input
|
</el-form-item>
|
||||||
type="password"
|
</el-form>
|
||||||
v-model="userPasswordInfo.newPassword"
|
<template #footer>
|
||||||
placeholder="请输入新密码"
|
<span class="dialog-footer">
|
||||||
show-password
|
<el-button type="primary" @click="settingPassword"> <i class="fa fa-check"></i>提交 </el-button>
|
||||||
clearable
|
</span>
|
||||||
></el-input>
|
</template>
|
||||||
</el-form-item>
|
</el-dialog>
|
||||||
<el-form-item required prop="newPassword2" label="确认密码">
|
</div>
|
||||||
<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>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="personal">
|
<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 { formatAxis } from '/@/utils/formatTime';
|
||||||
import * as api from './api'
|
import * as api from './api';
|
||||||
import {ElMessage } from "element-plus";
|
import { ElMessage } from 'element-plus';
|
||||||
import {getBaseURL} from "/@/utils/baseUrl";
|
import { getBaseURL } from '/@/utils/baseUrl';
|
||||||
import { Session } from '/@/utils/storage';
|
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(() => {
|
const currentTime = computed(() => {
|
||||||
return formatAxis(new Date());
|
return formatAxis(new Date());
|
||||||
});
|
});
|
||||||
const userInfoFormRef = ref()
|
const userInfoFormRef = ref();
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
|
name: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
|
||||||
mobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确手机号' }]
|
mobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确手机号' }],
|
||||||
})
|
});
|
||||||
// 定义变量内容
|
|
||||||
const uploadAvatar = reactive({
|
let selectImgVisible = ref(false);
|
||||||
action:getBaseURL() + 'api/system/file/',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'JWT ' + Session.get('token')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const state = reactive<PersonalState>({
|
const state = reactive<PersonalState>({
|
||||||
newsInfoList:[],
|
newsInfoList: [],
|
||||||
personalForm: {
|
personalForm: {
|
||||||
avatar:'',
|
avatar: '',
|
||||||
username:'',
|
username: '',
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
mobile: '',
|
mobile: '',
|
||||||
gender:'',
|
gender: '',
|
||||||
dept_info:{
|
dept_info: {
|
||||||
dept_id:0,
|
dept_id: 0,
|
||||||
dept_name:''
|
dept_name: '',
|
||||||
},
|
},
|
||||||
role_info:[{
|
role_info: [
|
||||||
id:0,
|
{
|
||||||
name:''
|
id: 0,
|
||||||
}]
|
name: '',
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转消息中心
|
* 跳转消息中心
|
||||||
*/
|
*/
|
||||||
import {useRouter } from "vue-router";
|
const route = useRouter();
|
||||||
import {Session} from "/@/utils/storage";
|
const msgMore = () => {
|
||||||
const route = useRouter()
|
route.push({ path: '/messageCenter' });
|
||||||
const msgMore=()=>{
|
};
|
||||||
route.push({path:'/messageCenter'})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户个人信息
|
* 获取用户个人信息
|
||||||
*/
|
*/
|
||||||
const getUserInfo = function (){
|
const getUserInfo = function () {
|
||||||
api.GetUserInfo({}).then((res:any)=>{
|
api.GetUserInfo({}).then((res: any) => {
|
||||||
const {data} = res
|
const { data } = res;
|
||||||
state.personalForm.avatar = data.avatar || '';
|
state.personalForm.avatar = data.avatar || '';
|
||||||
state.personalForm.username = data.username || '';
|
state.personalForm.username = data.username || '';
|
||||||
state.personalForm.name = data.name || '';
|
state.personalForm.name = data.name || '';
|
||||||
state.personalForm.email = data.email || '';
|
state.personalForm.email = data.email || '';
|
||||||
state.personalForm.mobile = data.mobile || '';
|
state.personalForm.mobile = data.mobile || '';
|
||||||
state.personalForm.gender = data.gender || '';
|
state.personalForm.gender = data.gender;
|
||||||
state.personalForm.dept_info.dept_name = data.dept_info.dept_name || '';
|
state.personalForm.dept_info.dept_name = data.dept_info.dept_name || '';
|
||||||
state.personalForm.role_info = data.role_info || [];
|
state.personalForm.role_info = data.role_info || [];
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新用户信息
|
* 更新用户信息
|
||||||
* @param formEl
|
* @param formEl
|
||||||
*/
|
*/
|
||||||
const submitForm = async () => {
|
const submitForm = async () => {
|
||||||
if (!userInfoFormRef.value) return
|
if (!userInfoFormRef.value) return;
|
||||||
await userInfoFormRef.value.validate((valid, fields) => {
|
await userInfoFormRef.value.validate((valid, fields) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
api.updateUserInfo(state.personalForm).then((res:any)=>{
|
api.updateUserInfo(state.personalForm).then((res: any) => {
|
||||||
ElMessage.success('更新成功')
|
ElMessage.success('更新成功');
|
||||||
getUserInfo()
|
getUserInfo();
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error('表单验证失败,请检查~')
|
ElMessage.error('表单验证失败,请检查~');
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取消息通知
|
* 获取消息通知
|
||||||
*/
|
*/
|
||||||
const getMsg = () => {
|
const getMsg = () => {
|
||||||
api.GetSelfReceive({}).then((res:any)=>{
|
api.GetSelfReceive({}).then((res: any) => {
|
||||||
const {data} = res
|
const { data } = res;
|
||||||
state.newsInfoList = data || [];
|
state.newsInfoList = data || [];
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
onMounted(()=>{
|
onMounted(() => {
|
||||||
getUserInfo();
|
getUserInfo();
|
||||||
getMsg();
|
getMsg();
|
||||||
})
|
});
|
||||||
|
|
||||||
/**************************密码修改部分************************/
|
/**************************密码修改部分************************/
|
||||||
const passwordFormShow = ref(false)
|
const passwordFormShow = ref(false);
|
||||||
const userPasswordFormRef = ref()
|
const userPasswordFormRef = ref();
|
||||||
const userPasswordInfo=reactive({
|
const userPasswordInfo = reactive({
|
||||||
oldPassword: '',
|
oldPassword: '',
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
newPassword2: ''
|
newPassword2: '',
|
||||||
})
|
});
|
||||||
|
|
||||||
const validatePass = (rule, value, callback) => {
|
const validatePass = (rule, value, callback) => {
|
||||||
const pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}')
|
const pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}');
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
callback(new Error('请输入密码'))
|
callback(new Error('请输入密码'));
|
||||||
} else if (value === userPasswordInfo.oldPassword) {
|
} else if (value === userPasswordInfo.oldPassword) {
|
||||||
callback(new Error('原密码与新密码一致'))
|
callback(new Error('原密码与新密码一致'));
|
||||||
} else if (!pwdRegex.test(value)) {
|
} else if (!pwdRegex.test(value)) {
|
||||||
callback(new Error('您的密码复杂度太低(密码中必须包含字母、数字)'))
|
callback(new Error('您的密码复杂度太低(密码中必须包含字母、数字)'));
|
||||||
} else {
|
} else {
|
||||||
if (userPasswordInfo.newPassword2 !== '') {
|
if (userPasswordInfo.newPassword2 !== '') {
|
||||||
userPasswordFormRef.value.validateField('newPassword2')
|
userPasswordFormRef.value.validateField('newPassword2');
|
||||||
}
|
}
|
||||||
callback()
|
callback();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
const validatePass2 = (rule, value, callback) => {
|
const validatePass2 = (rule, value, callback) => {
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
callback(new Error('请再次输入密码'))
|
callback(new Error('请再次输入密码'));
|
||||||
} else if (value !== userPasswordInfo.newPassword) {
|
} else if (value !== userPasswordInfo.newPassword) {
|
||||||
callback(new Error('两次输入密码不一致!'))
|
callback(new Error('两次输入密码不一致!'));
|
||||||
} else {
|
} else {
|
||||||
callback()
|
callback();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const passwordRules=reactive({
|
const passwordRules = reactive({
|
||||||
oldPassword: [
|
oldPassword: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入原密码',
|
message: '请输入原密码',
|
||||||
trigger: 'blur'
|
trigger: 'blur',
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
newPassword: [{ validator: validatePass, trigger: 'blur' }],
|
newPassword: [{ validator: validatePass, trigger: 'blur' }],
|
||||||
newPassword2: [{ validator: validatePass2, trigger: 'blur' }]
|
newPassword2: [{ validator: validatePass2, trigger: 'blur' }],
|
||||||
})
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新设置密码
|
* 重新设置密码
|
||||||
*/
|
*/
|
||||||
const settingPassword= ()=>{
|
const settingPassword = () => {
|
||||||
userPasswordFormRef.value.validate((valid) => {
|
userPasswordFormRef.value.validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
api.UpdatePassword(userPasswordInfo).then((res:any)=>{
|
api.UpdatePassword(userPasswordInfo).then((res: any) => {
|
||||||
ElMessage.success('密码修改成功')
|
ElMessage.success('密码修改成功');
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
// 校验失败
|
// 校验失败
|
||||||
// 登录表单校验失败
|
// 登录表单校验失败
|
||||||
ElMessage.error('表单校验失败,请检查')
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '/@/theme/mixins/index.scss';
|
@import '/@/theme/mixins/index.scss';
|
||||||
.personal {
|
.personal {
|
||||||
.personal-user {
|
.personal-user {
|
||||||
height: 130px;
|
height: 130px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
.personal-user-left {
|
.personal-user-left {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 130px;
|
height: 130px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
:deep(.el-upload) {
|
:deep(.el-upload) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.personal-user-left-upload {
|
.personal-user-left-upload {
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
img {
|
img {
|
||||||
animation: logoAnimation 0.3s ease-in-out;
|
animation: logoAnimation 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.personal-user-right {
|
.personal-user-right {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
.personal-title {
|
.personal-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@include text-ellipsis(1);
|
@include text-ellipsis(1);
|
||||||
}
|
}
|
||||||
.personal-item {
|
.personal-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
.personal-item-label {
|
.personal-item-label {
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
@include text-ellipsis(1);
|
@include text-ellipsis(1);
|
||||||
}
|
}
|
||||||
.personal-item-value {
|
.personal-item-value {
|
||||||
@include text-ellipsis(1);
|
@include text-ellipsis(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.personal-info {
|
.personal-info {
|
||||||
.personal-info-more {
|
.personal-info-more {
|
||||||
float: right;
|
float: right;
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.personal-info-box {
|
.personal-info-box {
|
||||||
height: 130px;
|
height: 130px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.personal-info-ul {
|
.personal-info-ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
.personal-info-li {
|
.personal-info-li {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
.personal-info-li-title {
|
.personal-info-li-title {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@include text-ellipsis(1);
|
@include text-ellipsis(1);
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
& a:hover {
|
& a:hover {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.personal-recommend-row {
|
.personal-recommend-row {
|
||||||
.personal-recommend-col {
|
.personal-recommend-col {
|
||||||
.personal-recommend {
|
.personal-recommend {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
i {
|
i {
|
||||||
right: 0px !important;
|
right: 0px !important;
|
||||||
bottom: 0px !important;
|
bottom: 0px !important;
|
||||||
transition: all ease 0.3s;
|
transition: all ease 0.3s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i {
|
i {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -10px;
|
right: -10px;
|
||||||
bottom: -10px;
|
bottom: -10px;
|
||||||
font-size: 70px;
|
font-size: 70px;
|
||||||
transform: rotate(-30deg);
|
transform: rotate(-30deg);
|
||||||
transition: all ease 0.3s;
|
transition: all ease 0.3s;
|
||||||
}
|
}
|
||||||
.personal-recommend-auto {
|
.personal-recommend-auto {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 5%;
|
top: 5%;
|
||||||
color: var(--next-color-white);
|
color: var(--next-color-white);
|
||||||
.personal-recommend-msg {
|
.personal-recommend-msg {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.personal-edit {
|
.personal-edit {
|
||||||
.personal-edit-title {
|
.personal-edit-title {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
width: 2px;
|
width: 2px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
background: var(--el-color-primary);
|
background: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.personal-edit-safe-box {
|
.personal-edit-safe-box {
|
||||||
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
|
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
|
||||||
padding: 15px 0;
|
padding: 15px 0;
|
||||||
.personal-edit-safe-item {
|
.personal-edit-safe-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
.personal-edit-safe-item-left {
|
.personal-edit-safe-item-left {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.personal-edit-safe-item-left-label {
|
.personal-edit-safe-item-left-label {
|
||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.personal-edit-safe-item-left-value {
|
.personal-edit-safe-item-left-value {
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
@include text-ellipsis(1);
|
@include text-ellipsis(1);
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useExpose, useCrud,dict } from '@fast-crud/fast-crud';
|
import { useExpose, useCrud, dict } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './curd';
|
import { createCrudOptions } from './crud';
|
||||||
import RolePermission from '/@/views/system/rolePermission/index.vue';
|
import RolePermission from '/@/views/system/rolePermission/index.vue';
|
||||||
import * as api from './api'
|
import * as api from './api';
|
||||||
import _ from "lodash-es";
|
import _ from 'lodash-es';
|
||||||
const rolePermission = ref();
|
const rolePermission = ref();
|
||||||
defineExpose(rolePermission);
|
defineExpose(rolePermission);
|
||||||
// crud组件的ref
|
// crud组件的ref
|
||||||
|
|||||||
@@ -7,18 +7,24 @@
|
|||||||
:before-close="handleClose"
|
:before-close="handleClose"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div >
|
<div>
|
||||||
<el-tag size="large" type="primary">当前角色:{{ editedRoleInfo.name }}</el-tag>
|
<el-tag size="large" type="primary">当前角色:{{ editedRoleInfo.name }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div style="padding: 1em">
|
<div style="padding: 1em">
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="6">
|
<el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="6">
|
||||||
<el-alert title="针对角色的页面菜单进行授权" description="点击菜单项,可对菜单下的按钮/接口授权" type="warning" />
|
|
||||||
<el-card header="菜单页面授权">
|
<el-card header="菜单页面授权">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-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>
|
<el-button size="mini" type="primary" @click="onSaveAuth">保存菜单授权</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -35,11 +41,18 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="18">
|
<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">
|
<el-card v-if="isBtnPermissionShow">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div>
|
||||||
@@ -49,12 +62,12 @@
|
|||||||
<el-table size="small" :data="buttonPermissionData" border style="width: 100%">
|
<el-table size="small" :data="buttonPermissionData" border style="width: 100%">
|
||||||
<el-table-column prop="menu_button" label="权限名称" width="100">
|
<el-table-column prop="menu_button" label="权限名称" width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div>{{formatMenuBtn(scope.row.menu_button)}}</div>
|
<div>{{ formatMenuBtn(scope.row.menu_button) }}</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="data_range" label="权限范围" width="140">
|
<el-table-column prop="data_range" label="权限范围" width="140">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div>{{formatDataRange(scope.row.data_range)}}</div>
|
<div>{{ formatDataRange(scope.row.data_range) }}</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="dept" label="权限涉及部门"/>
|
<el-table-column prop="dept" label="权限涉及部门"/>
|
||||||
@@ -139,8 +152,8 @@
|
|||||||
import {ref, defineExpose, reactive, toRefs} from 'vue'
|
import {ref, defineExpose, reactive, toRefs} from 'vue'
|
||||||
import {ElMessageBox} from 'element-plus'
|
import {ElMessageBox} from 'element-plus'
|
||||||
import * as api from './api'
|
import * as api from './api'
|
||||||
import type {FormRules,FormInstance} from 'element-plus'
|
import type {FormRules, FormInstance} from 'element-plus'
|
||||||
import { ElMessage } from 'element-plus'
|
import {ElMessage} from 'element-plus'
|
||||||
import XEUtils from 'xe-utils'
|
import XEUtils from 'xe-utils'
|
||||||
//抽屉是否显示
|
//抽屉是否显示
|
||||||
const drawer = ref(false)
|
const drawer = ref(false)
|
||||||
@@ -190,7 +203,7 @@ let buttonOptions = ref<[]>()
|
|||||||
let editedMenuInfo = ref()
|
let editedMenuInfo = ref()
|
||||||
//菜单节点点击事件
|
//菜单节点点击事件
|
||||||
const menuNodeClick = (node: any, obj: any) => {
|
const menuNodeClick = (node: any, obj: any) => {
|
||||||
isBtnPermissionShow.value = !node.is_catalog
|
isBtnPermissionShow.value = !node.is_catalog
|
||||||
if (!node.is_catalog) {
|
if (!node.is_catalog) {
|
||||||
buttonOptions.value = []
|
buttonOptions.value = []
|
||||||
editedMenuInfo.value = node
|
editedMenuInfo.value = node
|
||||||
@@ -198,7 +211,7 @@ const menuNodeClick = (node: any, obj: any) => {
|
|||||||
const {data} = res
|
const {data} = res
|
||||||
buttonOptions.value = data
|
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
|
const {data} = res
|
||||||
buttonPermissionData.value = data
|
buttonPermissionData.value = data
|
||||||
})
|
})
|
||||||
@@ -211,7 +224,7 @@ const menuTree = ref()
|
|||||||
//是否显示新增表单
|
//是否显示新增表单
|
||||||
const dialogFormVisible = ref(false)
|
const dialogFormVisible = ref(false)
|
||||||
//部门树
|
//部门树
|
||||||
const deptTree=ref()
|
const deptTree = ref()
|
||||||
//自定义部门数据
|
//自定义部门数据
|
||||||
const deptOptions = ref()
|
const deptOptions = ref()
|
||||||
//选中的部门数据
|
//选中的部门数据
|
||||||
@@ -220,7 +233,7 @@ const deptCheckedKeys = []
|
|||||||
const buttonForm = reactive({
|
const buttonForm = reactive({
|
||||||
menu_button: null,
|
menu_button: null,
|
||||||
role: null,
|
role: null,
|
||||||
menu:null,
|
menu: null,
|
||||||
data_range: null,
|
data_range: null,
|
||||||
dept: []
|
dept: []
|
||||||
})
|
})
|
||||||
@@ -256,20 +269,20 @@ const onChangeButton = (val: any) => {
|
|||||||
})
|
})
|
||||||
//获取权限部门值
|
//获取权限部门值
|
||||||
api.GetDataScopeDept({menu_button: val}).then((res: 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)=>{
|
const formatMenuBtn = (val: any) => {
|
||||||
let obj:any = buttonOptions.value?.find((item:any)=>{
|
let obj: any = buttonOptions.value?.find((item: any) => {
|
||||||
return item.id===val
|
return item.id === val
|
||||||
})
|
})
|
||||||
return obj?obj.name:null
|
return obj ? obj.name : null
|
||||||
}
|
}
|
||||||
//过滤权限范围
|
//过滤权限范围
|
||||||
const formatDataRange = (val:any)=>{
|
const formatDataRange = (val: any) => {
|
||||||
let obj:any = [
|
let obj: any = [
|
||||||
{
|
{
|
||||||
"value": 0,
|
"value": 0,
|
||||||
"label": '仅本人数据权限'
|
"label": '仅本人数据权限'
|
||||||
@@ -290,17 +303,17 @@ const formatDataRange = (val:any)=>{
|
|||||||
"value": 4,
|
"value": 4,
|
||||||
"label": '自定义数据权限'
|
"label": '自定义数据权限'
|
||||||
}
|
}
|
||||||
].find((item:any)=>{
|
].find((item: any) => {
|
||||||
return item.value===val
|
return item.value === val
|
||||||
})
|
})
|
||||||
return obj?obj.label:null
|
return obj ? obj.label : null
|
||||||
}
|
}
|
||||||
//保存按钮表单
|
//保存按钮表单
|
||||||
|
|
||||||
const onSaveButtonForm = async () => {
|
const onSaveButtonForm = async () => {
|
||||||
const {id:roleId} = editedRoleInfo.value
|
const {id: roleId} = editedRoleInfo.value
|
||||||
const {id:menuId} = editedMenuInfo.value
|
const {id: menuId} = editedMenuInfo.value
|
||||||
const form:any = Object.assign({},buttonForm)
|
const form: any = Object.assign({}, buttonForm)
|
||||||
form.role = roleId
|
form.role = roleId
|
||||||
form.menu = menuId
|
form.menu = menuId
|
||||||
//选中的部门
|
//选中的部门
|
||||||
@@ -309,7 +322,7 @@ const onSaveButtonForm = async () => {
|
|||||||
if (!buttonFormRef.value) return
|
if (!buttonFormRef.value) return
|
||||||
await buttonFormRef.value.validate((valid, fields) => {
|
await buttonFormRef.value.validate((valid, fields) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
api.CreatePermission(form).then((res:any)=>{
|
api.CreatePermission(form).then((res: any) => {
|
||||||
buttonPermissionData.value.push(form)
|
buttonPermissionData.value.push(form)
|
||||||
dialogFormVisible.value = false
|
dialogFormVisible.value = false
|
||||||
ElMessage({
|
ElMessage({
|
||||||
@@ -320,7 +333,7 @@ const onSaveButtonForm = async () => {
|
|||||||
} else {
|
} else {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title:'提交错误',
|
title: '提交错误',
|
||||||
message: 'F12控制台看详情',
|
message: 'F12控制台看详情',
|
||||||
})
|
})
|
||||||
console.log('提交错误', fields)
|
console.log('提交错误', fields)
|
||||||
@@ -329,8 +342,8 @@ const onSaveButtonForm = async () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
//删除按钮权限
|
//删除按钮权限
|
||||||
const onDeleteBtn = (scope:any)=>{
|
const onDeleteBtn = (scope: any) => {
|
||||||
const {row,$index} = scope
|
const {row, $index} = scope
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
'您是否要删除数据?',
|
'您是否要删除数据?',
|
||||||
'温馨提示',
|
'温馨提示',
|
||||||
@@ -339,15 +352,15 @@ const onDeleteBtn = (scope:any)=>{
|
|||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
}
|
}
|
||||||
).then(() => {
|
).then(() => {
|
||||||
api.DeletePermission({id:row.id}).then(res=>{
|
api.DeletePermission({id: row.id}).then(res => {
|
||||||
buttonPermissionData.value.splice($index,1)
|
buttonPermissionData.value.splice($index, 1)
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: res.msg,
|
message: res.msg,
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
@@ -373,12 +386,12 @@ const onSaveAuth = () => {
|
|||||||
//合并的菜单数据
|
//合并的菜单数据
|
||||||
const menuIdList = [...checkedList, ...halfCheckedList]
|
const menuIdList = [...checkedList, ...halfCheckedList]
|
||||||
// console.log(menuIdList)
|
// console.log(menuIdList)
|
||||||
const { id:roleId } = editedRoleInfo.value
|
const {id: roleId} = editedRoleInfo.value
|
||||||
const data = {
|
const data = {
|
||||||
role:roleId,
|
role: roleId,
|
||||||
menu:menuIdList
|
menu: menuIdList
|
||||||
}
|
}
|
||||||
api.SaveMenuPermission(data).then((res:any)=>{
|
api.SaveMenuPermission(data).then((res: any) => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: res.msg,
|
message: res.msg,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|||||||
@@ -2,6 +2,15 @@ import { request } from '/@/utils/service';
|
|||||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||||
|
|
||||||
export const apiPrefix = '/api/system/user/';
|
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) {
|
export function GetList(query: PageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
|
|||||||
@@ -1,320 +1,341 @@
|
|||||||
import * as api from "./api";
|
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 { request } from '/@/utils/service';
|
||||||
import { dictionary } from "/@/utils/dictionary";
|
import { dictionary } from '/@/utils/dictionary';
|
||||||
interface CreateCrudOptionsTypes {
|
import { successMessage } from '/@/utils/message';
|
||||||
crudOptions: CrudOptions;
|
import { inject } from 'vue';
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
return await api.UpdateObj(form);
|
return await api.UpdateObj(form);
|
||||||
};
|
};
|
||||||
const delRequest = async ({ row }: DelReq) => {
|
const delRequest = async ({ row }: DelReq) => {
|
||||||
return await api.DelObj(row.id);
|
return await api.DelObj(row.id);
|
||||||
};
|
};
|
||||||
const addRequest = async ({ form }: AddReq) => {
|
const addRequest = async ({ form }: AddReq) => {
|
||||||
return await api.AddObj(form);
|
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
//权限判定
|
||||||
columns: {
|
const hasPermissions = inject('$hasPermissions');
|
||||||
_index: {
|
|
||||||
title: '序号',
|
return {
|
||||||
form: { show: false },
|
crudOptions: {
|
||||||
column: {
|
table: {
|
||||||
type: 'index',
|
remove: {
|
||||||
align: 'center',
|
confirmMessage: '是否删除该用户?',
|
||||||
width: '70px',
|
},
|
||||||
columnSetDisabled: true, //禁止在列设置中选择
|
},
|
||||||
},
|
request: {
|
||||||
},
|
pageRequest,
|
||||||
search: {
|
addRequest,
|
||||||
title: '关键词',
|
editRequest,
|
||||||
column: {
|
delRequest,
|
||||||
show: false
|
},
|
||||||
},
|
rowHandle: {
|
||||||
search: {
|
//固定右侧
|
||||||
show: true,
|
fixed: 'right',
|
||||||
component: {
|
width: 140,
|
||||||
props: {
|
buttons: {
|
||||||
clearable: true
|
view: {
|
||||||
},
|
show: false,
|
||||||
placeholder: '请输入关键词'
|
},
|
||||||
}
|
edit: {
|
||||||
},
|
iconRight: 'Edit',
|
||||||
form: {
|
type: 'text',
|
||||||
show: false,
|
show: hasPermissions('user:Update'),
|
||||||
component: {
|
},
|
||||||
props: {
|
remove: {
|
||||||
clearable: true
|
iconRight: 'Delete',
|
||||||
}
|
type: 'text',
|
||||||
}
|
show: hasPermissions('user:Delete'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
username: {
|
},
|
||||||
title: '账号',
|
columns: {
|
||||||
search: {
|
_index: {
|
||||||
show: true,
|
title: '序号',
|
||||||
},
|
form: { show: false },
|
||||||
minWidth: 100,
|
column: {
|
||||||
type: 'input',
|
type: 'index',
|
||||||
form: {
|
align: 'center',
|
||||||
rules: [ // 表单校验规则
|
width: '70px',
|
||||||
{
|
columnSetDisabled: true, //禁止在列设置中选择
|
||||||
required: true,
|
},
|
||||||
message: '账号必填项'
|
},
|
||||||
}
|
username: {
|
||||||
],
|
title: '账号',
|
||||||
component: {
|
search: {
|
||||||
placeholder: '请输入账号'
|
show: true,
|
||||||
},
|
},
|
||||||
}
|
type: 'input',
|
||||||
},
|
column: {
|
||||||
password: {
|
minWidth: 100, //最小列宽
|
||||||
title: '密码',
|
},
|
||||||
type: 'input',
|
form: {
|
||||||
column: {
|
rules: [
|
||||||
show: false
|
// 表单校验规则
|
||||||
},
|
{
|
||||||
editForm: {
|
required: true,
|
||||||
show: false
|
message: '账号必填项',
|
||||||
},
|
},
|
||||||
form: {
|
],
|
||||||
rules: [ // 表单校验规则
|
component: {
|
||||||
{
|
placeholder: '请输入账号',
|
||||||
required: true,
|
},
|
||||||
message: '密码必填项'
|
},
|
||||||
}
|
},
|
||||||
],
|
password: {
|
||||||
component: {
|
title: '密码',
|
||||||
span: 12,
|
type: 'input',
|
||||||
showPassword: true,
|
column: {
|
||||||
placeholder: '请输入密码'
|
show: false,
|
||||||
},
|
},
|
||||||
// value: vm.systemConfig('base.default_password'),
|
editForm: {
|
||||||
},
|
show: false,
|
||||||
/* valueResolve(row, key) {
|
},
|
||||||
|
form: {
|
||||||
|
rules: [
|
||||||
|
// 表单校验规则
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '密码必填项',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
component: {
|
||||||
|
span: 12,
|
||||||
|
showPassword: true,
|
||||||
|
placeholder: '请输入密码',
|
||||||
|
},
|
||||||
|
// value: vm.systemConfig('base.default_password'),
|
||||||
|
},
|
||||||
|
/* valueResolve(row, key) {
|
||||||
if (row.password) {
|
if (row.password) {
|
||||||
row.password = vm.$md5(row.password)
|
row.password = vm.$md5(row.password)
|
||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
title: '姓名',
|
title: '姓名',
|
||||||
search: {
|
search: {
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
form: {
|
column: {
|
||||||
rules: [ // 表单校验规则
|
minWidth: 100, //最小列宽
|
||||||
{
|
},
|
||||||
required: true,
|
form: {
|
||||||
message: '姓名必填项'
|
rules: [
|
||||||
}
|
// 表单校验规则
|
||||||
],
|
{
|
||||||
component: {
|
required: true,
|
||||||
span: 12,
|
message: '姓名必填项',
|
||||||
placeholder: '请输入姓名'
|
},
|
||||||
},
|
],
|
||||||
}
|
component: {
|
||||||
},
|
span: 12,
|
||||||
dept: {
|
placeholder: '请输入姓名',
|
||||||
title: '部门',
|
},
|
||||||
search: {
|
},
|
||||||
disabled: true
|
},
|
||||||
},
|
dept: {
|
||||||
type: 'dict-tree',
|
title: '部门',
|
||||||
dict: dict({
|
search: {
|
||||||
isTree: true,
|
disabled: true,
|
||||||
url: '/api/system/dept/all_dept/',
|
},
|
||||||
value: 'id',
|
type: 'dict-tree',
|
||||||
label: 'name',
|
dict: dict({
|
||||||
getData: async ({ url }: { url: string }) => {
|
isTree: true,
|
||||||
return request({
|
url: '/api/system/dept/all_dept/',
|
||||||
url: url,
|
value: 'id',
|
||||||
}).then((ret: any) => {
|
label: 'name',
|
||||||
return ret.data
|
getData: async ({ url }: { url: string }) => {
|
||||||
})
|
return request({
|
||||||
}
|
url: url,
|
||||||
}),
|
}).then((ret: any) => {
|
||||||
form: {
|
return ret.data;
|
||||||
rules: [ // 表单校验规则
|
});
|
||||||
{
|
},
|
||||||
required: true,
|
}),
|
||||||
message: '必填项'
|
column: {
|
||||||
}
|
minWidth: 150, //最小列宽
|
||||||
],
|
},
|
||||||
component: {
|
form: {
|
||||||
filterable: true,
|
rules: [
|
||||||
placeholder: '请选择',
|
// 表单校验规则
|
||||||
props: {
|
{
|
||||||
props: {
|
required: true,
|
||||||
value: "id",
|
message: '必填项',
|
||||||
label: "name",
|
},
|
||||||
}
|
],
|
||||||
}
|
component: {
|
||||||
},
|
filterable: true,
|
||||||
},
|
placeholder: '请选择',
|
||||||
},
|
props: {
|
||||||
role: {
|
props: {
|
||||||
title: '角色',
|
value: 'id',
|
||||||
search: {
|
label: 'name',
|
||||||
disabled: true
|
},
|
||||||
},
|
},
|
||||||
type: 'dict-select',
|
},
|
||||||
dict: dict({
|
},
|
||||||
url: '/api/system/role/',
|
},
|
||||||
value: 'id',
|
role: {
|
||||||
label: 'name',
|
title: '角色',
|
||||||
isTree: true,
|
search: {
|
||||||
getData: async ({ url }: { url: string }) => {
|
disabled: true,
|
||||||
return request({
|
},
|
||||||
url: url,
|
type: 'dict-select',
|
||||||
params: {
|
dict: dict({
|
||||||
page: 1,
|
url: '/api/system/role/',
|
||||||
limit: 10
|
value: 'id',
|
||||||
}
|
label: 'name',
|
||||||
}).then((ret: any) => {
|
isTree: true,
|
||||||
return ret.data
|
getData: async ({ url }: { url: string }) => {
|
||||||
})
|
return request({
|
||||||
}
|
url: url,
|
||||||
}),
|
params: {
|
||||||
form: {
|
page: 1,
|
||||||
rules: [ // 表单校验规则
|
limit: 10,
|
||||||
{
|
},
|
||||||
required: true,
|
}).then((ret: any) => {
|
||||||
message: '必填项'
|
return ret.data;
|
||||||
}
|
});
|
||||||
],
|
},
|
||||||
component: {
|
}),
|
||||||
multiple: true,
|
column: {
|
||||||
filterable: true,
|
minWidth: 100, //最小列宽
|
||||||
placeholder: '请选择角色'
|
},
|
||||||
},
|
form: {
|
||||||
}
|
rules: [
|
||||||
},
|
// 表单校验规则
|
||||||
mobile: {
|
{
|
||||||
title: '手机号码',
|
required: true,
|
||||||
search: {
|
message: '必填项',
|
||||||
show: true,
|
},
|
||||||
},
|
],
|
||||||
type: 'input',
|
component: {
|
||||||
form: {
|
multiple: true,
|
||||||
rules: [
|
filterable: true,
|
||||||
{
|
placeholder: '请选择角色',
|
||||||
max: 20,
|
},
|
||||||
message: '请输入正确的手机号码',
|
},
|
||||||
trigger: 'blur'
|
},
|
||||||
},
|
mobile: {
|
||||||
{
|
title: '手机号码',
|
||||||
pattern: /^1[3-9]\d{9}$/,
|
search: {
|
||||||
message: '请输入正确的手机号码'
|
show: true,
|
||||||
}
|
},
|
||||||
],
|
type: 'input',
|
||||||
component: {
|
column: {
|
||||||
placeholder: '请输入手机号码'
|
minWidth: 120, //最小列宽
|
||||||
}
|
},
|
||||||
}
|
form: {
|
||||||
},
|
rules: [
|
||||||
email: {
|
{
|
||||||
title: '邮箱',
|
max: 20,
|
||||||
column:{
|
message: '请输入正确的手机号码',
|
||||||
width:260
|
trigger: 'blur',
|
||||||
},
|
},
|
||||||
form: {
|
{
|
||||||
rules: [
|
pattern: /^1[3-9]\d{9}$/,
|
||||||
{
|
message: '请输入正确的手机号码',
|
||||||
type: 'email',
|
},
|
||||||
message: '请输入正确的邮箱地址',
|
],
|
||||||
trigger: ['blur', 'change']
|
component: {
|
||||||
}
|
placeholder: '请输入手机号码',
|
||||||
],
|
},
|
||||||
component: {
|
},
|
||||||
placeholder: '请输入邮箱'
|
},
|
||||||
}
|
email: {
|
||||||
}
|
title: '邮箱',
|
||||||
},
|
column: {
|
||||||
gender: {
|
width: 260,
|
||||||
title: '性别',
|
},
|
||||||
type: 'dict-radio',
|
form: {
|
||||||
dict: dict({
|
rules: [
|
||||||
data: dictionary('gender')
|
{
|
||||||
}),
|
type: 'email',
|
||||||
form: {
|
message: '请输入正确的邮箱地址',
|
||||||
value: 1,
|
trigger: ['blur', 'change'],
|
||||||
component: {
|
},
|
||||||
span: 12
|
],
|
||||||
}
|
component: {
|
||||||
},
|
placeholder: '请输入邮箱',
|
||||||
component: { props: { color: 'auto' } } // 自动染色
|
},
|
||||||
},
|
},
|
||||||
user_type: {
|
},
|
||||||
title: '用户类型',
|
gender: {
|
||||||
search: {
|
title: '性别',
|
||||||
show: true,
|
type: 'dict-select',
|
||||||
},
|
dict: dict({
|
||||||
type: 'dict-select',
|
data: dictionary('gender'),
|
||||||
dict: dict({
|
}),
|
||||||
data: dictionary('user_type')
|
form: {
|
||||||
}),
|
value: 1,
|
||||||
form: {
|
component: {
|
||||||
show: false,
|
span: 12,
|
||||||
value: 0,
|
},
|
||||||
component: {
|
},
|
||||||
span: 12
|
component: { props: { color: 'auto' } }, // 自动染色
|
||||||
}
|
},
|
||||||
}
|
user_type: {
|
||||||
},
|
title: '用户类型',
|
||||||
is_active: {
|
search: {
|
||||||
title: '状态',
|
show: true,
|
||||||
search: {
|
},
|
||||||
show: true,
|
type: 'dict-select',
|
||||||
},
|
dict: dict({
|
||||||
type: 'dict-radio',
|
data: dictionary('user_type'),
|
||||||
dict: dict({
|
}),
|
||||||
data: dictionary('button_status_bool')
|
column: {
|
||||||
}),
|
minWidth: 100, //最小列宽
|
||||||
form: {
|
},
|
||||||
value: true,
|
form: {
|
||||||
component: {
|
show: false,
|
||||||
span: 12
|
value: 0,
|
||||||
}
|
component: {
|
||||||
}
|
span: 12,
|
||||||
},
|
},
|
||||||
avatar: {
|
},
|
||||||
title: '头像',
|
},
|
||||||
type: 'avatar-cropper',
|
is_active: {
|
||||||
form:{
|
title: '锁定',
|
||||||
show:false
|
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>
|
<template>
|
||||||
<fs-page>
|
<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>
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './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
|
// crud组件的ref
|
||||||
const crudRef = ref();
|
const crudRef = ref();
|
||||||
// crud 配置的ref
|
// crud 配置的ref
|
||||||
@@ -24,3 +126,17 @@ onMounted(() => {
|
|||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
</script>
|
</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 { 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 const apiPrefix = '/api/system/api_white_list/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import * as api from './api';
|
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 { request } from '/@/utils/service';
|
||||||
import { dictionary } from '/@/utils/dictionary';
|
import { dictionary } from '/@/utils/dictionary';
|
||||||
interface CreateCrudOptionsTypes {
|
import { successMessage } from '/@/utils/message';
|
||||||
crudOptions: CrudOptions;
|
import {inject} from "vue";
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -20,6 +19,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
const addRequest = async ({ form }: AddReq) => {
|
const addRequest = async ({ form }: AddReq) => {
|
||||||
return await api.AddObj(form);
|
return await api.AddObj(form);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//权限判定
|
||||||
|
const hasPermissions = inject("$hasPermissions")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
@@ -28,6 +31,34 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
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: {
|
columns: {
|
||||||
_index: {
|
_index: {
|
||||||
title: '序号',
|
title: '序号',
|
||||||
@@ -37,10 +68,11 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
width: '70px',
|
width: '70px',
|
||||||
columnSetDisabled: true, //禁止在列设置中选择
|
columnSetDisabled: true, //禁止在列设置中选择
|
||||||
|
//@ts-ignore
|
||||||
formatter: (context) => {
|
formatter: (context) => {
|
||||||
//计算序号,你可以自定义计算规则,此处为翻页累加
|
//计算序号,你可以自定义计算规则,此处为翻页累加
|
||||||
let index = context.index ?? 1;
|
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;
|
return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -70,7 +102,7 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
method: {
|
method: {
|
||||||
title: '请求方式',
|
title: '请求方式',
|
||||||
sortable: true,
|
sortable: 'custom',
|
||||||
search: {
|
search: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
@@ -93,8 +125,15 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
label: 'DELETE',
|
label: 'DELETE',
|
||||||
value: 3,
|
value: 3,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'PATCH',
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -113,7 +152,7 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
title: '接口地址',
|
title: '接口地址',
|
||||||
sortable: true,
|
sortable: 'custom',
|
||||||
search: {
|
search: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
@@ -133,6 +172,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
column:{
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -155,9 +197,11 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
class: { yxtInput: true },
|
class: { yxtInput: true },
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
render(h) {
|
position: 'label',
|
||||||
return <el-alert title="请正确填写,以免请求时被拦截。匹配单例使用正则,例如:/api/xx/.*?/" type="warning" />;
|
tooltip: {
|
||||||
|
placement: 'top-start',
|
||||||
},
|
},
|
||||||
|
text: '请正确填写,以免请求时被拦截。匹配单例使用正则,例如:/api/xx/.*?/',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -166,17 +210,26 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
search: {
|
search: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
width: 150,
|
|
||||||
type: 'dict-radio',
|
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({
|
dict: dict({
|
||||||
data: dictionary('button_status_bool'),
|
data: dictionary('button_status_bool'),
|
||||||
}),
|
}),
|
||||||
form: {
|
|
||||||
value: true,
|
|
||||||
component: {
|
|
||||||
span: 12,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,18 +6,10 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './crud';
|
import { createCrudOptions } from './crud';
|
||||||
// crud组件的ref
|
|
||||||
const crudRef = ref();
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||||
// crud 配置的ref
|
|
||||||
const crudBinding = ref();
|
|
||||||
// 暴露的方法
|
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
|
||||||
// 你的crud配置
|
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
|
||||||
// 初始化crud配置
|
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
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