mapel1594184

1 Django端

  • 历经千辛万苦,终于排除所有用户模块的bugT.T

1.1 settings.py

"""
Django settings for opwf project.

Generated by \'django-admin startproject\' using Django 2.0.13.

For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
import datetime
import os, sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, \'apps\'))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = \'uorj1ni^mnut@wo@c%)iv)%5=8dxlml4-j0!f3b%4#f*8a5)3t\'

# SECURITY WARNING: don\'t run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = [\'*\']


# Application definition

INSTALLED_APPS = [
    \'django.contrib.admin\',
    \'django.contrib.auth\',
    \'django.contrib.contenttypes\',
    \'django.contrib.sessions\',
    \'django.contrib.messages\',
    \'django.contrib.staticfiles\',
    \'rest_framework\',
    \'corsheaders\',
    \'user.apps.UserConfig\',
    \'workflow.apps.WorkflowConfig\',
    \'workerorder.apps.WorkerorderConfig\',
    # \'jwt\',
    # \'rest_framework_jwt\',
    # \'rest_framework.authentication\'

]

MIDDLEWARE = [
    \'django.middleware.security.SecurityMiddleware\',
    \'django.contrib.sessions.middleware.SessionMiddleware\',
    \'django.middleware.common.CommonMiddleware\',
    \'corsheaders.middleware.CorsMiddleware\',
    # \'django.middleware.csrf.CsrfViewMiddleware\',
    \'django.contrib.auth.middleware.AuthenticationMiddleware\',
    \'django.contrib.messages.middleware.MessageMiddleware\',
    \'django.middleware.clickjacking.XFrameOptionsMiddleware\',
]

ROOT_URLCONF = \'opwf.urls\'
CORS_ORIGIN_ALLOW_ALL = True

CORS_ORIGIN_WHITELIST = (
    \'http://127.0.0.1:8080\',
    \'http://localhost:8080\',
)
CORS_ALLOW_CREDENTIALS = True

TEMPLATES = [
    {
        \'BACKEND\': \'django.template.backends.django.DjangoTemplates\',
        \'DIRS\': [],
        \'APP_DIRS\': True,
        \'OPTIONS\': {
            \'context_processors\': [
                \'django.template.context_processors.debug\',
                \'django.template.context_processors.request\',
                \'django.contrib.auth.context_processors.auth\',
                \'django.contrib.messages.context_processors.messages\',
            ],
        },
    },
]

WSGI_APPLICATION = \'opwf.wsgi.application\'


# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    \'default\': {
        \'ENGINE\': \'django.db.backends.mysql\',
        \'NAME\': \'opwf_db\',
        \'USER\': \'root\',
        \'PASSWORD\': \'1\',
        \'HOST\': \'127.0.0.1\',
        \'PORT\': \'3306\'
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators

REST_FRAMEWORK = {
    # 文档报错: AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’
    # 用下面的设置可以解决
    \'DEFAULT_SCHEMA_CLASS\': \'rest_framework.schemas.AutoSchema\',
    # 默认设置是:
    # \'DEFAULT_SCHEMA_CLASS\': \'rest_framework.schemas.openapi.AutoSchema\',

    # 异常处理器
    # \'EXCEPTION_HANDLER\': \'user.utils.exception_handler\',

    # Base API policies      默认渲染器类
    \'DEFAULT_RENDERER_CLASSES\': [
        \'rest_framework.renderers.JSONRenderer\',
        \'rest_framework.renderers.BrowsableAPIRenderer\',
    ],
    # 默认解析器类
    \'DEFAULT_PARSER_CLASSES\': [
        \'rest_framework.parsers.JSONParser\',
        \'rest_framework.parsers.FormParser\',
        \'rest_framework.parsers.MultiPartParser\'
    ],
    # 1.认证器(全局)
    \'DEFAULT_AUTHENTICATION_CLASSES\': [
        \'rest_framework_jwt.authentication.JSONWebTokenAuthentication\',  # 在 DRF中配置JWT认证
        # \'rest_framework.authentication.SessionAuthentication\',  # 使用session时的认证器
        # \'rest_framework.authentication.BasicAuthentication\'  # 提交表单时的认证器
    ],

    # 2.权限配置(全局): 顺序靠上的严格
    \'DEFAULT_PERMISSION_CLASSES\': [
        # \'rest_framework.permissions.IsAdminUser\',  # 管理员可以访问
        # \'rest_framework.permissions.IsAuthenticated\',  # 认证用户可以访问
        # \'rest_framework.permissions.IsAuthenticatedOrReadOnly\',  # 认证用户可以访问, 否则只能读取
        \'rest_framework.permissions.AllowAny\',  # 所有用户都可以访问
    ],
    # 3.限流(防爬虫)
    \'DEFAULT_THROTTLE_CLASSES\': [
        \'rest_framework.throttling.AnonRateThrottle\',
        \'rest_framework.throttling.UserRateThrottle\',
    ],
    # 3.1限流策略
    # \'DEFAULT_THROTTLE_RATES\': {
    #     \'user\': \'100/hour\',  # 认证用户每小时100次
    #     \'anon\': \'300/day\',  # 未认证用户每天能访问3次
    # },

    \'DEFAULT_CONTENT_NEGOTIATION_CLASS\': \'rest_framework.negotiation.DefaultContentNegotiation\',
    \'DEFAULT_METADATA_CLASS\': \'rest_framework.metadata.SimpleMetadata\',
    \'DEFAULT_VERSIONING_CLASS\': None,

    # 4.分页(全局):全局分页器, 例如 省市区的数据自定义分页器, 不需要分页
    # \'DEFAULT_PAGINATION_CLASS\': \'rest_framework.pagination.PageNumberPagination\',
    # # 每页返回数量
    # \'PAGE_SIZE\': 3,
    # 5.过滤器后端
    \'DEFAULT_FILTER_BACKENDS\': [
        \'django_filters.rest_framework.DjangoFilterBackend\',
        # \'django_filters.rest_framework.backends.DjangoFilterBackend\', 包路径有变化
    ],

    # 5.1过滤排序(全局):Filtering 过滤排序
    \'SEARCH_PARAM\': \'search\',
    \'ORDERING_PARAM\': \'ordering\',

    \'NUM_PROXIES\': None,

    # 6.版本控制:Versioning  接口版本控制
    \'DEFAULT_VERSION\': None,
    \'ALLOWED_VERSIONS\': None,
    \'VERSION_PARAM\': \'version\',

    # Authentication  认证
    # 未认证用户使用的用户类型
    \'UNAUTHENTICATED_USER\': \'django.contrib.auth.models.AnonymousUser\',
    # 未认证用户使用的Token值
    \'UNAUTHENTICATED_TOKEN\': None,

    # View configuration
    \'VIEW_NAME_FUNCTION\': \'rest_framework.views.get_view_name\',
    \'VIEW_DESCRIPTION_FUNCTION\': \'rest_framework.views.get_view_description\',

    \'NON_FIELD_ERRORS_KEY\': \'non_field_errors\',

    # Testing
    \'TEST_REQUEST_RENDERER_CLASSES\': [
        \'rest_framework.renderers.MultiPartRenderer\',
        \'rest_framework.renderers.JSONRenderer\'
    ],
    \'TEST_REQUEST_DEFAULT_FORMAT\': \'multipart\',

    # Hyperlink settings
    \'URL_FORMAT_OVERRIDE\': \'format\',
    \'FORMAT_SUFFIX_KWARG\': \'format\',
    \'URL_FIELD_NAME\': \'url\',

    # Encoding
    \'UNICODE_JSON\': True,
    \'COMPACT_JSON\': True,
    \'STRICT_JSON\': True,
    \'COERCE_DECIMAL_TO_STRING\': True,
    \'UPLOADED_FILES_USE_URL\': True,

    # Browseable API
    \'HTML_SELECT_CUTOFF\': 1000,
    \'HTML_SELECT_CUTOFF_TEXT\': "More than {count} items...",

    # Schemas
    \'SCHEMA_COERCE_PATH_PK\': True,
    \'SCHEMA_COERCE_METHOD_NAMES\': {
        \'retrieve\': \'read\',
        \'destroy\': \'delete\'
    },

    # \'Access-Control-Allow-Origin\':\'http://localhost:8080\',
    # \'Access-Control-Allow-Credentials\': True

}

AUTH_PASSWORD_VALIDATORS = [
    {
        \'NAME\': \'django.contrib.auth.password_validation.UserAttributeSimilarityValidator\',
    },
    {
        \'NAME\': \'django.contrib.auth.password_validation.MinimumLengthValidator\',
    },
    {
        \'NAME\': \'django.contrib.auth.password_validation.CommonPasswordValidator\',
    },
    {
        \'NAME\': \'django.contrib.auth.password_validation.NumericPasswordValidator\',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = \'zh-hans\'

TIME_ZONE = \'Asia/Shanghai\'

USE_I18N = True

USE_L10N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = \'/static/\'
AUTH_USER_MODEL = \'user.User\'

# jwt载荷中的有效期设置
JWT_AUTH = {
    # 1.token前缀:headers中 Authorization 值的前缀
    \'JWT_AUTH_HEADER_PREFIX\': \'JWT\',
    # 2.token有效期:一天有效
    \'JWT_EXPIRATION_DELTA\': datetime.timedelta(days=1),
    # 3.刷新token:允许使用旧的token换新token
    \'JWT_ALLOW_REFRESH\': True,
    # 4.token有效期:token在24小时内过期, 可续期token
    \'JWT_REFRESH_EXPIRATION_DELTA\': datetime.timedelta(hours=24),
    # 5.自定义JWT载荷信息:自定义返回格式,需要手工创建
    \'JWT_RESPONSE_PAYLOAD_HANDLER\': \'user.utils.jwt_response_payload_handler\',
}

1.2 models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

# Create your models here.



class User(AbstractUser):
    \'\'\'
    username:用户名
    password:密码
    mobile:手机号
    email:邮箱
    \'\'\'
    username = models.CharField(max_length=30, unique=True)
    # 写上 unique=True 就可以指定唯一,验证字段的时候自动验证
    password = models.CharField(max_length=256)
    mobile = models.CharField(max_length=11)
    email = models.CharField(max_length=30)
    token = models.CharField(max_length=256, default=\'\')
    weixin = models.CharField(max_length=30, null=True)
    date_joined = models.DateField(auto_now_add=True)

    class Meta:
        db_table = \'user_user\'
        verbose_name = \'用户表\'
        verbose_name_plural = verbose_name


class Role(models.Model):
    \'\'\'
    name:角色名称
    description:描述
    \'\'\'
    zh_name = models.CharField(max_length=30)
    en_name = models.CharField(max_length=30)
    description = models.TextField()

    class Meta:
        db_table = \'user_role\'
        verbose_name = \'角色表\'
        verbose_name_plural = verbose_name



class UserRole(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    role = models.ForeignKey(Role, on_delete=models.CASCADE)

    class Meta:
        db_table = \'user_userrole\'
        verbose_name = \'用户角色表\'
        verbose_name_plural = verbose_name

1.3 serializers.py

# -*- coding: utf-8 -*-
from rest_framework import serializers
from rest_framework_jwt.serializers import jwt_payload_handler
from rest_framework_jwt.settings import api_settings

from user.models import User, UserRole, Role


class UserModelSerializer(serializers.ModelSerializer):
    is_superuser = serializers.CharField(default=0)
    roles = serializers.SerializerMethodField(required=False)
    class Meta:
        model = User
        fields = \'__all__\'
    def create(self, data):
        username = data.get(\'username\', \'\')
        password = data.get(\'password\', \'\')
        mobile = data.get(\'mobile\', \'\')
        email = data.get(\'email\', \'\')
        weixin = data.get(\'weixin\', \'\')
        user = User(username=username, email=email, mobile=mobile, weixin=weixin)
        user.set_password(password)
        user.save()
        return user

    def get_roles(self, row):
        roles_json = UserRole.objects.filter(user=row).values(\'role__id\', "role__zh_name")
        print(\'111\', roles_json)
        print(\'222\', list(roles_json))
        print(type(list(roles_json)))
        return list(roles_json)

    def get_serializer_class(self):
        return UserModelSerializer

class UserSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    username = serializers.CharField()
    password = serializers.CharField()
    mobile = serializers.CharField()
    email = serializers.EmailField()
    weixin = serializers.CharField()
    token = serializers.CharField(read_only=True)

    def create(self, data):
        username = data.get(\'username\', \'\')
        password = data.get(\'password\', \'\')
        mobile = data.get(\'mobile\', \'\')
        email = data.get(\'email\', \'\')
        weixin = data.get(\'weixin\', \'\')

        user = User(username=username, email=email, mobile=mobile, weixin=weixin)
        user.set_password(password)
        user.save()
        # 补充生成记录登录状态的token
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        user.token = token
        return user

class UserRoleSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserRole
        fields = \'__all__\'

class RoleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Role
        fields = "__all__"

1.4 views.py

import datetime
import random

from django.contrib.auth.hashers import make_password
from django.shortcuts import render

# Create your views here.
from rest_framework import viewsets
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.views import APIView

from user.models import User, Role, UserRole
from user.serializers import UserSerializer, UserModelSerializer, RoleSerializer, UserRoleSerializer


# 分页(局部):自定义分页器 局部
class PageNum(PageNumberPagination):
    page_size = 4                           # 每页显示多少条
    page_size_query_param = \'page_size\'     # 查询字符串中代表每页返回数据数量的参数名, 默认值: None
    page_query_param = \'page\'               # 查询字符串中代表页码的参数名, 有默认值: page
    max_page_size = None                    # 最大页码数限制



class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserModelSerializer
    filter_fields = {"username", \'mobile\', \'weixin\', \'email\'}
    pagination_class = PageNum  # 注意不是列表(只能有一个分页模式)

    def perform_update(self, serializer):
        user_obj = serializer.save()
        # 原有的serializer反序列化添加
        roles = self.request.data.get(\'roles\')
        user_obj.userrole_set.all().delete()
        for role_id in roles:
            userrole = UserRole.objects.create(user=user_obj, role_id=role_id)
    def get_serializer_class(self):
        print(\'action------->\', self.action)
        return UserModelSerializer

class UserGetViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

class RoleViewSet(viewsets.ModelViewSet):
    queryset = Role.objects.all()
    serializer_class = RoleSerializer
    filter_fields = {"zh_name"}
    pagination_class = PageNum  # 注意不是列表(只能有一个分页模式)

class RoleGetViewSet(viewsets.ModelViewSet):
    # 用于角色查看接口(没有分页的那种)
    queryset = Role.objects.all()
    serializer_class = RoleSerializer


class RegisterView(APIView):
    def post(self, request):
        username = request.data.get(\'username\')
        password = request.data.get(\'password\')
        password_new = request.data.get(\'password_new\')
        role = request.data.get(\'roles\')
        print(role)
        if not all([username, password, password_new]):
            return Response(
                {\'msg\': \'信息不全\', \'code\': 400}
            )
        if password != password_new:
            return Response(
                {\'code\': 400, \'msg\':\'两次登录密码不一致\'}
            )
        user_serializer = UserSerializer(data=request.data)
        if user_serializer.is_valid():
            user_serializer.save()
            user_obj = User.objects.filter(username=username).first()
            print(user_obj)
            new_list = []
            for i in role:
                role_obj = Role.objects.filter(id=i).first()
                # UserRole.objects.create(user=user_obj, role=role_obj)
                # UserRole.objects.create(user_id=user_obj.id, role_id=role_obj.id)
                new_list.append((UserRole(user=user_obj, role=role_obj)))
            UserRole.objects.bulk_create(new_list)
            return Response(
                {\'msg\': \'注册成功\', \'code\': 200, \'data\':user_serializer.data}
            )

        return Response(
            {\'msg\': \'注册失败\', \'error\': user_serializer.errors }
        )

1.5 urls.py

# -*- coding: utf-8 -*-

from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token

from user import views

router = DefaultRouter()
router.register(r\'user\', views.UserViewSet)
router.register(r\'user_get\', views.UserGetViewSet)
router.register(r\'role\', views.RoleViewSet)
router.register(r\'role_get\', views.RoleGetViewSet)

urlpatterns = [
    path(\'login/\', obtain_jwt_token),
    path(\'register/\', views.RegisterView.as_view()),
]

urlpatterns += router.urls

1.6 utils.py

# -*- coding: utf-8 -*-
def jwt_response_payload_handler(token, user=None, request=None, role=None):
    """
        自定义jwt认证成功返回数据
        :token 返回的jwt
        :user 当前登录的用户信息[对象]
        :request 当前本次客户端提交过来的数据
        :role 角色
    """
    if user.first_name:
        name = user.first_name
    else:
        name = user.username
        return {
            \'authenticated\': \'true\',
             \'id\': user.id,
             "role": role,
             \'name\': name,
             \'username\': user.username,
             \'email\': user.email,
             \'token\': token,
        }

1.7 MyBaseModel.py

# -*- coding: utf-8 -*-
from django.db import models

class BaseModel(models.Model):
    is_delete = models.BooleanField(\'是否刪除\', default=0)
    create_time = models.DateField(\'创建时间\', auto_now_add=True, null=True)
    update_time = models.DateField(\'更新时间\', auto_now=True, null=True)

    class Meta:
        abstract = True

class BaseModelFlow(models.Model):
    name = models.CharField(\'名称\', max_length=60)
    description = models.TextField(\'描述\')

    class Meta:
        abstract = True

1.8 主路由urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'user/\', include(\'user.urls\')),
    path(\'workflow/\', include(\'workflow.urls\')),
    path(\'workerorder/\', include(\'workerorder.urls\')),
]

2 Vue端

2.1 @/components/layout/Header.vue

<template>
        <div>
          <h3 style="margin-top:20px">
           
          欢迎你
           <a-icon type="smile" theme="outlined" style="font-size:20px;color:pink"/>&ensp;
          {{username}}
            <a-icon type="fire" style="font-size:25px;color:pink"/>&ensp;
          </h3>
        </div>
</template>
<script>
export default{
    data(){
        return{
          username:localStorage.getItem(\'username\')
        }
    }
}
</script>

2.2 @/components/layout/Home.vue

<template>
         <div id="components-layout-demo-basic" style="">
            <a-layout>
                <a-layout-sider style="height:250px;width:150px">
                    <LeftMenu style="margin-left:-55px"></LeftMenu>
                    <!-- 以组件发昂视导入左侧菜单 -->
                </a-layout-sider>
                <a-layout>

                    <a-layout-header  style="height:100px;width:500px;background:rgb(253,234,254)">
                        <Header/>
                        <!-- 导入头部 -->
                    </a-layout-header>
                    
                        <div style="margin-left:100px">
                            <!-- 这里的 router-view 是绑定的路由 -->
                            <router-view></router-view>
                        </div>
                        
                </a-layout>

            </a-layout>
        </div>
</template>

<script>
// 导入组件
import LeftMenu from \'@/components/layout/LeftMenu\'
import Header from \'@/components/layout/Header\'
export default {
    // 注册组件
    components:{
        LeftMenu,
        Header
    },
    data() {
        
        return {

        }
    },
    methods: {

    },
    created() {

    }
}
</script>

<style scoped>
#components-layout-demo-basic {
  text-align: center;
}
#components-layout-demo-basic .ant-layout-header {
  background: white;
}
#components-layout-demo-basic .ant-layout-footer {
  background: rgb(253,234,254);
  color: rgb(253,234,254);
}
#components-layout-demo-basic .ant-layout-footer {
  line-height: 1.5;
}
#components-layout-demo-basic .ant-layout-sider {
  background: rgb(253,234,254);
  color: rgb(253,234,254);
  line-height: 120px;
}
#components-layout-demo-basic .ant-layout-content {
  background: white;
  color: white;
  min-height: 120px;
  line-height: 120px;
}
.ant-layout.ant-layout-has-sider{
  width:1700px;
  height: 800px;
  background:rgb(253,234,254)
}
.ant-layout{
  background:rgb(253,234,254);
  width:800px
}
</style>

2.3 @/components/layout/LeftMenu.vue

<template>
  <div>
    <a-switch :default-checked="false" @change="changeMode" style="margin-top:70px"/>&emsp;
    <br>
    <a-menu
      style="width: 300px;background:rgb(253,234,254)"
      :default-selected-keys="[\'1\']"
      :default-open-keys="[\'sub1\']"
      :mode="mode"
      :theme="theme"
      @click="handleClick"
      
    >
    <!-- 必须定义click方法,handleClick是用来跳转路由的,内置毁掉参数e,包括传递上去的key路由地址 -->
      <a-sub-menu >
        <span slot="title">
          <a-icon type="user" />
          <span>用户模块</span>
        </span>
          
          <a-menu-item key="usermanage" title="用户信息管理" style="background:rgb(253,234,254)">
            信息管理
          </a-menu-item>
          <!-- key是路由地址 -->

          <a-menu-item key="rolemanage" title="工单模板" style="background:rgb(253,234,254)">
            角色管理
          </a-menu-item>


       </a-sub-menu>
      

      <a-menu-item key="baidu">
        <a-icon type="calendar" />
        百度翻译
      </a-menu-item>
      <a-sub-menu key="workflow" style="background:rgb(253,234,254)">
        <span slot="title">
          <a-icon type="appstore" />
          <span>模板管理</span>
        </span>
          
          <a-menu-item key="flowtype" title="工单分类" style="background:rgb(253,234,254);">
            模板分类
          </a-menu-item>

          <a-menu-item key="flowconf" title="工单模板" style="background:rgb(253,234,254)">
            新建模板
          </a-menu-item>

       </a-sub-menu>
    
      <a-sub-menu key="workorder">
        <span slot="title"><a-icon type="setting" /><span>工单管理</span></span>
              
          <a-menu-item key="workorder" style="background:rgb(253,234,254)">
          实例化工单
          </a-menu-item>

          <a-menu-item key="suborder"  style="background:rgb(253,234,254)">
          实例化子工单
          </a-menu-item>

      </a-sub-menu>
    </a-menu>
  </div>
</template>
<script>
export default {
  data() {
    return {
      mode: \'inline\',
      theme: \'light\',
      current: \'1\'
    };
  },
  methods: {
    changeMode(checked) {
      this.mode = checked ? \'vertical\' : \'inline\';
    },
    changeTheme(checked) {
      this.theme = checked ? \'dark\' : \'light\';
    },
     handleClick(e) {
      this.current = e.key;
      console.log(e.domEvent)
      this.$router.push({path:this.current});
      // click方法默认回调参数中的 key,所以 e.key就是传递来的路由
    },
  },
};
</script>
<style>
.a-menu{
  background:rgb(253,234,254);
    width: 550px;
}
.ul{
  background:rgb(253,234,254)
}
.ant-menu-submenu > .ant-menu{
  background-color: rgb(253,234,254);

}
</style>


2.4 @/http/apis.js

//将我们http.js中封装好的  get,post.put,delete,patch  导过来
import { axios_get, axios_post, axios_delete, axios_put, axios_patch } from \'./index.js\'




//按照格式确定方法名
export const user_login = P => axios_post("/user/login/", P)  // 用户登录

// 用户模块
// 用户信息管理
export const get_userlist = P => axios_get(\'/user/user/?page=\'+P, P)            // 获取用户列表
export const get_userlist_new = P => axios_get(\'user/user_get/\', P)             // 获取用户列表(不包含分页的接口)
export const add_user = P => axios_post(\'/user/register/\', P)                   // 注册新用户
export const search_for = P => axios_get(\'user/user/\', P)                       // 根据用户名查找指定用户信息并展示
export const delete_user = P => axios_delete(\'/user/user/\' + P + \'/\')           // 根据获取到的用户id删除用户信息
export const update_user = P => axios_put(\'/user/user/\'+ P.id +\'/\', P)          // 根据用户id和提交来的数据修改用户信息

// 角色管理
export const get_rolelist = P => axios_get(\'/user/role/?page=\'+P, P)            // 获取角色列表
export const get_rolelist_new = P => axios_get(\'/user/role_get/\', P)            // 获取角色列表(不包含分页的接口)
export const add_role = P => axios_post(\'/user/role/\', P)                       // 注册新角色
export const search_for_role = P => axios_get(\'user/role/\', P)                  // 根据角色名查找指定角色信息并展示
export const delete_role = P => axios_delete(\'/user/role/\' + P + \'/\')           // 根据获取到的角色id删除角色信息
export const update_role = P => axios_put(\'/user/role/\'+ P.id +\'/\', P)          // 根据角色id和提交来的数据修改角色信息

// 工单模块---分类工单
export const get_flowtypelist = P => axios_get(\'/workflow/flowtype/?page=\'+P, P)            // 获取模板分类列表
export const get_flowtypelist_new = P => axios_get(\'/workflow/flowtype_get/\', P)            // 获取模板分类列表(不含分页)
export const add_flowtype = P => axios_post(\'/workflow/flowtype/\', P)                       // 创建新模板分类
export const search_for_flowtype = P => axios_get(\'/workflow/flowtype/\', P)                  // 根据模板分类名查找指定模板分类信息并展示
export const delete_flowtype = P => axios_delete(\'/workflow/flowtype/\' + P + \'/\')           // 根据获取到的模板分类id删除模板分类信息
export const update_flowtype = P => axios_put(\'/workflow/flowtype/\'+ P.id +\'/\', P)          // 根据模板分类id和提交来的数据修改模板分类信息

// 工单模块---工单模板
export const get_flowconflist = P => axios_get(\'/workflow/flowconf/?page=\'+P, P)            // 获取工单模板列表
export const get_flowconflist_new = P => axios_get(\'/workflow/flowconf_get/\', P)            // 获取工单模板列表(不含分页)
export const add_flowconf = P => axios_post(\'/workflow/flowconf/\', P)                       // 创建新工单模板
export const search_for_flowconf = P => axios_get(\'/workflow/flowconf/\', P)                  // 根据工单模板名查找指定工单模板信息并展示
export const delete_flowconf = P => axios_delete(\'/workflow/flowconf/\' + P + \'/\')           // 根据获取到的工单模板id删除工单模板信息
export const update_flowconf = P => axios_put(\'/workflow/flowconf/\'+ P.id +\'/\', P)          // 根据工单模板id和提交来的数据修改工单模板信息

// 工单模块---配置审批流
export const get_approveconflist = P => axios_get(\'/workflow/approveconf/?page=\'+P, P)            // 获取配置审批流列表
export const add_approveconf = P => axios_post(\'/workflow/approveconf/\', P)                       // 配置新审批流
export const delete_approveconf = P => axios_delete(\'/workflow/approveconf/\' + P + \'/\')           // 根据审批流id删除审批流信息
export const update_approveconf = P => axios_put(\'/workflow/approveconf/\'+ P.id +\'/\', P)          // 根据审批流id和提交来的数据修改审批流信息

2.5 @/http/index.js

import axios from \'axios\'

// 第一步:设置axios
axios.defaults.baseURL = "http://192.168.56.100:1594/"

//全局设置网络超时
axios.defaults.timeout = 10000;

//设置请求头信息
axios.defaults.headers.post[\'Content-Type\'] = \'application/json\';
axios.defaults.headers.put[\'Content-Type\'] = \'application/json\';


// 第二:设置拦截器
/**
 * 请求拦截器(当前端发送请求给后端前进行拦截)
 * 例1:请求拦截器获取token设置到axios请求头中,所有请求接口都具有这个功能
 * 例2:到用户访问某一个页面,但是用户没有登录,前端页面自动跳转 /login/ 页面
 */
axios.interceptors.request.use(
    config => {
        // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
        const token = localStorage.getItem("token")
            // console.log(token)
        if (token) {
            config.headers.Authorization = \'JWT \' + token
        }
        return config;
    },
    error => {
        return Promise.error(error);
    })

axios.interceptors.response.use(
    // 请求成功,因为 API返回的状态码有多个,所以一定要在这里写上,不然会无法访问页面
    res => res.status === 200 || 201 || 204 ? Promise.resolve(res) : Promise.reject(res),
    // 请求失败
    error => {
        if (error.response) {
            // 判断一下返回结果的status == 401?  ==401跳转登录页面。  !=401passs
            // console.log(error.response)
            if (error.response.status === 401) {
                // 跳转不可以使用this.$router.push方法、
                // this.$router.push({path:\'/login\'})
                window.location.href = "http://127.0.0.1:8080/"
            } else {
                // errorHandle(response.status, response.data.message);
                return Promise.reject(error.response);
            }
            // 请求已发出,但是不在2xx的范围
        } else {
            // 处理断网的情况
            // eg:请求超时或断网时,更新state的network状态
            // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
            // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
            // store.commit(\'changeNetwork\', false);
            return Promise.reject(error.response);
        }
    });


// 第三:封装axios请求
// 3.1 封装get请求
export function axios_get(url, params) {
    return new Promise(
        (resolve, reject) => {
            axios.get(url, {params:params})
                .then(res => {
                    // console.log("封装信息的的res", res)
                    resolve(res.data)
                }).catch(err => {
                    reject(err.data)
                })
        }
    )
}

// 3.2 封装post请求
export function axios_post(url, data) {
    return new Promise(
        (resolve, reject) => {
            // console.log(data)
            axios.post(url, JSON.stringify(data))
                .then(res => {
                    // console.log("封装信息的的res", res)
                    resolve(res.data)
                }).catch(err => {
                    reject(err.data)
                })
        }
    )
}

// 3.3 封装put请求
export function axios_put(url, data) {
    return new Promise(
        (resolve, reject) => {
            // console.log(data)
            axios.put(url, JSON.stringify(data))
                .then(res => {
                    // console.log("封装信息的的 res", res)
                    resolve(res.data)
                }).catch(err => {
                    reject(err.data)
                })
        }
    )
}

// 3.4 封装patch请求(可用于局部修改)

export function axios_patch(url, data) {
    return new Promise(
        (resolve, reject) => {
            // console.log(data)
            axios.patch(url, JSON.stringify(data))
                .then(res => {
                    // console.log("封装信息的的res", res)
                    resolve(res.data)
                }).catch(err => {
                    reject(err.data)
                })
        }
    )
}

// 3.5 封装delete请求
export function axios_delete(url, data) {
    return new Promise(
        (resolve, reject) => {
            // console.log(data)
            axios.delete(url, { params: data })
                .then(res => {
                    // console.log("封装信息的的res", res)
                    resolve(res.data)
                }).catch(err => {
                    // reject(err.data)
                })
        }
    )
}

2.6 @/views/user-manage/components/BreadCrumb.vue

<template>
    <div>
        <a-breadcrumb>
            <br>
            <a-breadcrumb-item href="">
            <a-icon type="home" />
            </a-breadcrumb-item>

            <a-breadcrumb-item href="">
            <a-icon type="user" />
            <span>首页</span>

            </a-breadcrumb-item>
            <a-breadcrumb-item>
            用户模块
            </a-breadcrumb-item>
            <a-breadcrumb-item>
            信息管理页面
            </a-breadcrumb-item>
        </a-breadcrumb>
    </div>
</template>

<script>
export default {
    name:"BreadCrumb",
    data() {
        return {

        }
    },
    methods: {

    },
    created() {

    }
}
</script>

<style scoped>

</style>

2.7 @/views/user-manage/components/EditForm.vue

<template>
    <div>
        <a-modal
        title="Please write now."
        :visible="visible"
        @ok="handleOk"
        @cancel="handleCancel"
        >
        <!-- @ok控制按钮ok -->
        <!-- @cancel控制按钮cancel -->
            <p v-if="userList.id">UserName:
                <a-input 
                style="width:380px;float:right" 
                placeholder="username" 
                v-model="userList.username"
                disabled="disabled"
                ></a-input>                
            </p>
            <p v-else>UserName:
                <a-input 
                style="width:380px;float:right" 
                placeholder="username" 
                v-model="userList.username"
                ></a-input>
            </p>
            <br>
            <div v-if="userList.id">

            </div>
            <div v-else>
            <p>PassWord:
                <a-input
                    v-decorator="[
                        \'password\',
                        { rules: [{ required: true, message: \'Please input your Password!\' }] },
                    ]"
                    type="password"
                    placeholder="Password"
                    style="width:380px;float:right"
                    v-model="userList.password"
                    :disabled = \'false\'
                >
                    <a-icon slot="prefix" type="lock" style="color:rgba(0,0,0,.25)" />
                </a-input>
            </p>
            <br>
            <p>PassWord Again:
                <a-input
                    v-decorator="[
                        \'password\',
                        { rules: [{ required: true, message: \'Please input your Password!\' }] },
                    ]"
                    type="password"
                    placeholder="Password"
                    style="width:340px;float:right"
                    v-model="userList.password_new"
                >
                    <a-icon slot="prefix" type="lock" style="color:rgba(0,0,0,.25)" />
                </a-input>
            </p>
            </div>
            <br>
            <p>Email:
                <a-input style="width:410px;float:right" placeholder="Email" v-model="userList.email"></a-input>
            </p>
            <br>
            <p>Mobile:
                <a-input style="width:410px" placeholder="Mobile" v-model="userList.mobile"></a-input>
            </p>
            <br>
            <p>Weixin:
                <a-input style="width:410px" placeholder="WeiXin" v-model="userList.weixin"></a-input>
            </p>
            <br>
            <p>Role:
             <a-select
                mode="multiple"               
                style="width: 410px; float:right"
                placeholder="Please select"
                @change="handleChange"
                v-model="userList.roles"
             >
                <a-select-option v-for="i in roleList" :key="i.id" >
                {{ i.zh_name }}
                </a-select-option>
            </a-select>
            </p>
        </a-modal>

    </div>
</template>

<script>
export default {
    props:[\'visible\', \'userList\', \'roleList\'],
    data() {
        return {
        }
    },
    methods: {
        handleOk(e) {
            this.$emit(\'add\', this.role)
            // add方法的调用一定要在关闭弹窗上面,否则方法不执行完毕没有办法关闭弹窗
            // 调用父组件中 add 方法            
            this.$emit(\'update:visible\', false)
            // 把 visible 的值更新为 false,控制组件不显示

        },
        handleCancel(e) {
            this.$emit(\'update:visible\', false)
            // 把 visible 的值更新为 false,控制组件不显示
        },
        handleChange(value) {
            // console.log(`selected ${value}`);
            console.log(value)
            this.userList.roles = value
        },
    },
    created() {

    }
}
</script>

<style scoped>

</style>

2.8 @/views/user-manage/components/Pagination.vue

<template>
  <div>
    <a-pagination
      show-quick-jumper
      :default-current="2"
      :pageSize = \'4\'
      :total="count"
      show-less-items
      @change="onChange"
      v-model="current"

    />
  </div>
</template>

<script>
export default {
    props:[ \'count\' ],
    data() {
        return {
            current:1
        }
    },
    methods: {
        
        onChange() {
            this.$emit(\'getPage\', this.current)
    },
    },
    created() {

    }
}
</script>

<style scoped>

</style>

2.9 @/views/user-manage/components/Search.vue

<template>
    <div>
        <a-input-search placeholder="Input the username that you want to search for..." enter-button @search="onSearch" style="float:right;width:400px;" v-model="searchList.username"/>
    </div>
</template>

<script>
export default {
    props:[\'searchList\'],
    data() {
        return {

        } 
    },
    methods: {
        onSearch(){
            this.$emit(\'find\')
            // 调用父组件中的find方法
        }
    },
    created() {

    }
}
</script>

<style scoped>

</style>

2.10 @/views/user-manage/components/TableList.vue

<template>
  <a-table 
    :columns="columns" 
    :data-source="userListGet" 
    :rowKey="(record,index)=>{return index}"
    :pagination= \'false\'
    style="height:430px"
  >
  <!-- 带:的都是属性绑定,不可以更换名字,带 : 就是 js 环境 -->
    <!-- 不写:rowKey="(record,index)=>{return index}"浏览器会发出警告 -->
    <p slot="roles" slot-scope="roles">
      <a-tag
        v-for="role in roles"
        :key="role.id"
        color="pink"
      >
        {{ role.role__zh_name }}
      </a-tag>
    </p>
    <p slot="tags" slot-scope="text,tags,i">
      <!-- 加入操作的按钮! -->
      <a-button @click="delUser(text,tags,i)">删除</a-button>
      <a-button @click="updateUser(text,tags,i)">修改</a-button>
    </p>
  </a-table>
</template>

<script>
const columns = [
    {
    title: \'ID\',
    dataIndex: \'id\',
    key: \'id\',
    // ellipsis: true,
    width: 50,
  },
  {
    title: \'UserName\',
    dataIndex: \'username\',
    key: \'username\',
    scopedSlots: { customRender: \'username\' },
    width: 80,
  },
  {
    title: \'Email\',
    dataIndex: \'email\',
    key: \'email\',
    width: 150,
  },
  {
    title: \'Mobile\',
    dataIndex: \'mobile\',
    key: \'mobile\',
    ellipsis: true,
    width: 100,
  },
  {
    title: \'WeiXin\',
    dataIndex: \'weixin\',
    key: \'weixin\',
    ellipsis: true,
    width: 100,
  },
  {
    title: \'Date Joined\',
    dataIndex: \'date_joined\',
    key: \'date_joined\',
    ellipsis: true,
    width: 100,
  },
    {
    title: \'Roles\',
    dataIndex: \'roles\',
    key: \'roles\',
    ellipsis: true,
    width: 150,
    scopedSlots : { customRender: \'roles\'}
    // 不写的话显示不了标签
  },
  {
    title:\'操作\',
    dataIndex: \'tags\',
    key : \'tags\',
    width: 100,
    scopedSlots : { customRender: \'tags\'}
    // scopedSlots: { customRender: \'tags\' },一定不能少不然渲染不了html标签

  }

]

import { delete_user } from \'@/http/apis\'
export default {
  props:[ \'userListGet\', \'userList\'],
  data() {
    return {
      columns,
    }
  },
  methods:{
    get(){
        this.$emit(\'getUser\')
        // 调用父组件中的获取用户列表的方法
    },
    delUser(text,tags,i){
      // 定义变量 isDel来控制 confirm,isDel==true执行的就是对话框的 ok,isDel==false执行的就是对话框的 false
        const isDel = confirm(\'你确定要删除\' + tags.id)
        if(isDel==true){
          delete_user(tags.id).then(
          res=>{
            // 删除回调地址是  http://192.168.56.100:1594/id/
            this.get()
            alert(\'删除成功啦~\')
          })
        }else{
            alert(\'有需要再叫我哈~\')
        }
             
    },
    updateUser(text,tags,i){
      const roleIds = []
      tags.roles.forEach(item => {
        roleIds.push(item.role__id)
      });

      this.userList.id = tags.id
      this.userList.username = tags.username
      this.userList.password = tags.password,
      this.userList.password_new = tags.password
      this.userList.email = tags.email
      this.userList.mobile = tags.mobile
      this.userList.weixin = tags.weixin
      // 传一个角色id的列表给父组件
      this.userList.roles = roleIds     
      this.$emit(\'add\')
    }
  },
  created(){
    this.get()
  }
};
</script>

2.11 @/views/user-manage/index.vue

<template>
<div>
  <div id="components-layout-demo-basic">
     <a-layout>
        <a-layout-header>
          <BreadCrumb style="float:left"></BreadCrumb>

        </a-layout-header>
        <a-layout>
            <a-layout-content>
            <div style="margin-bottom:80px">
                <a-button type="danger" ghost style="float:left;margin-left:20px;margin-top:16px;color:pink;border-color:pink;font-size:16px" @click="addNew">
                AddUser
                </a-button>
                <EditForm
                    :visible.sync="visible"
                    :userList=\'userList\'
                    :roleList=\'roleList\'
                    @add="add"
                >
                <!-- .sync控制组件是否显示 -->
                </EditForm>
                <Search 
                  style="margin-bottom:-20px;margin-top:10px" 
                  :searchList="searchList"
                  @find="find"
                  @getUser="getUser"
                >
                </Search>
                
            </div>
                <TableList
                  :userListGet="userListGet"
                  :userList="userList"
                  @getUser="getUser"
                  @add="add"
                >
                </TableList>
                <Pagination
                  @getPage="getPage" 
                  :count="count" 
                  style="margin-top:20px; "             
                ></Pagination>
                <p></p>
            </a-layout-content>
        </a-layout>
     </a-layout>
  </div>
</div>
</template>

<script>
import BreadCrumb from "./components/BreadCrumb";
import TableList from "./components/TableList";
import Search from "./components/Search";
import EditForm from "./components/EditForm";
import Pagination from "./components/Pagination"

import { add_user, search_for, get_userlist, update_user } from \'@/http/apis\';
import { delete_user } from \'../../http/apis\';
import { get_rolelist_new, add_role_user } from \'../../http/apis\';
export default {
    components:{
        BreadCrumb,
        TableList,
        Search,
        EditForm,
        Pagination
    },
    data() {
        return {
            visible:false,
            userList: {
              \'id\':\'\',
              \'username\': \'\',
              \'passowrd\': \'\',
              \'password_new\': \'\',
              \'email\':\'\',
              \'mobile\':\'\',
              \'weixin\':\'\',
              \'roles\':[]
              // 没定义roles字段的话,没办法绑定
            },
            roleList: [],
            userListGet:[],
            searchList:{
                \'username\':\'\',
                \'page\':1,
                \'page_size\':4

            },
            updateUserList:[],
            // 当前页码
            current:1,
            // 总共的数据多少条
            count:0,
            addRoleList:[]

        }
    },
    methods: {
        addNew(){
          this.visible = true
          this.userList = {
              \'id\':\'\',
              \'username\': \'\',
              \'passowrd\': \'\',
              \'password_new\': \'\',
              \'email\':\'\',
              \'mobile\':\'\',
              \'weixin\':\'\',
              \'roles\':[]
            }
          // 用于控制组件显示
        },
        add(role_list){
          if(this.userList.id){            
            this.visible = true  
            update_user(this.userList).then(res=>{
              // alert(\'修改成功\')
              this.getUser()
            })

          }else{
            
            // 添加用户,子组件中编辑的值实际上是写在父组件上面的
            add_user(this.userList).then(res=>{
              
              console.log(res)
              alert(\'添加新用户成功\')
              this.getUser()
              
          })   
            this.visible=false       
          }
        },
        find(){
          // 根据用户名查找用户信息
          search_for(this.searchList).then(res=>{
            // 阔落的办法可以解决bug,但是不支持查询出多条数据,因为没办法分页
            // if(this.searchList.username){
            //   // 修复如果没有搜索数据,回车就只能显示一个页面的bug
            //   console.log(res)
            //   this.userListGet = res.results
            //   this.count = res.results.length
            // }else{
            //   this.getUser()
            // }
            this.getUser()

          })
        },
        getUser(){
          this.searchList.page = this.current
          // 获取用户信息列表,父组件传递给子组件
          get_userlist(this.searchList).then(res=>{
            this.userListGet = res.results
            this.count = res.count
            console.log(this.count)
            console.log(this.userListGet)

          })
        },
        // 获取页码
        getPage(currentChild){
          // 获取到的currentChild是子组件传递过来是第几页
          this.current = currentChild
          console.log(this.current)
          this.getUser()
        },
        getRole(){
            get_rolelist_new().then(res=>{
                this.roleList = res
                console.log(111111)
                console.log(this.roleList)
            })
        }
    },
    created() {
      this.getRole()
    }
}
</script>

<style scoped>
#components-layout-demo-basic {
  text-align: center;
}
#components-layout-demo-basic .ant-layout-header,
#components-layout-demo-basic .ant-layout-footer {
  background: white;
  color: #fff;
}
#components-layout-demo-basic .ant-layout-footer {
  line-height: 1.5;
}
#components-layout-demo-basic .ant-layout-content {
  background: white;
  color: #fff;
  min-height: 120px;
  line-height: 120px;
}
#components-layout-demo-basic > .ant-layout {
  margin-bottom: 48px;
}
#components-layout-demo-basic > .ant-layout:last-child {
  margin: 0;
}
</style>

2.12 @/router/index.js

import Vue from \'vue\'
import Router from \'vue-router\'

import Home from \'@/components/layout/Home\'
const page = name => () => import(\'@/views/\' + name)
Vue.use(Router)

export default new Router({
  mode: \'history\',
  routes: [
    { path: \'/login\',component: page(\'Login\'),name: \'登录\'},
    // { path: \'/aaa\',component: page(\'approveconf-manage/index\'),name: \'测试approveconf\'},
    { path: \'/\',component: Home,name: \'home\',
      children: [
        { path: \'usermanage\', component: page(\'user-manage/index\'), name: \'信息管理\' },
        { path: \'rolemanage\', component: page(\'role-manage/index\'), name: \'角色管理\' },
        { path: \'flowtype\', component: page(\'flowtype-manage/index\'), name: \'模板分类管理\' },
        { path: \'flowconf\', component: page(\'flowconf-manage/index\'), name: \'工单模板管理\' },
        { path: \'approveconf\', component: page(\'approveconf-manage/index\'), name: \'新建模板管理\' },
        { path: \'workorder\', component: page(\'workorder-manage/index\'), name: \'实例化工单\' },
        { path: \'flowconfform\', component: page(\'flowconfform-manage/index\'), name: \'实例化工单form\' },
        { path: \'baidu\', component: page(\'BaiDu\'), name: \'跳转百度\' },      
      ]
    }
  ]
})

分类:

技术点:

相关文章:

  • 2021-08-24
  • 2021-03-31
  • 2021-06-06
  • 2022-12-23
  • 2022-12-23
  • 2021-07-01
  • 2021-10-10
猜你喜欢
  • 2022-12-23
  • 2021-05-23
  • 2022-12-23
  • 2021-07-29
  • 2021-04-27
  • 2022-01-07
  • 2021-07-25
相关资源
相似解决方案