• 工单系统之用户模块整体实现


    1 用户模块管理分析

    1.1 管理模块部分功能展示

    1.2 前端总体业务逻辑分析

    • Vue组件思想

    vue本身就是一个组件,所以我们在使用的过程中,一定要有组件思想。本项目就是利用父子组件传值,调用方法来进行编写的。

    1.2.1 Home页面
    • 其中嵌套组件Header和LeftMenu

    总页面,主要用于布局。用于显示左侧菜单和头部信息。其中嵌套子路由用于不同模块之间的跳转。

    • Header用于头部信息展示

    • LeftMenu用于左侧菜单展示

    • 模块跳转部分嵌套子路由

    1.2.2 Index页面
    • 嵌套四个组件

    BreadCrumb,EditForm,Search,TableList,用于模块之间的跳转变化。

    • BreadCrumb用于顶部路径展示

    • EditForm用于控制输入框信息,修改和添加时有所不同
      • 修改(在原有数据基础上进行修改,并且不支持密码和用户名的修改)

      • 添加

    • Search控制查找

    • TableList控制表格展示,会随着Search输入的查找数据变化

    1.3 后端总体业务逻辑分析

    1.3.1 ModelViewSet
    • 用于用户信息管理的增删改查
    • 注意事项:
    * 查询接口
    http://192.168.56.100:1594/user/user/?username='cat'
    * 添加接口
    http://192.168.56.100:1594/user/user/
    * 删除接口(指定用户id删除)
    http://192.168.56.100:1594/user/user/id/
    * 修改接口(指定用户id修改)
    http://192.168.56.100:1594/user/user/id/
    
    1.3.2 APIView
    • 用于注册用户的业务逻辑

    2 Django端

    2.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
    
    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': 1
        # 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',
    }
    

    2.2 user/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,
            }
    

    2.3 user/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.response import Response
    from rest_framework.views import APIView
    
    from user.models import User
    from user.serializers import UserSerializer, UserModelSerializer
    
    
    class UserViewSet(viewsets.ModelViewSet):
        # 负责用户信息的增删改查
        queryset = User.objects.all()
        serializer_class = UserModelSerializer
        filter_fields = {"username"}
    
    
    
    
    class RegisterView(APIView):
        # 注册有一定业务逻辑,所以用APIView
        def post(self, request):
            username = request.data.get('username')
            password = request.data.get('password')
            password_new = request.data.get('password_new')
            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_info = User.objects.filter(username=username).first()
                return Response(
                    {'msg': '注册成功', 'code': 200, 'data':user_serializer.data}
                )
            return Response(
                {'msg': '注册失败', 'error': user_serializer.errors }
            )
    
    
    

    2.4 user/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
    
    
    class UserModelSerializer(serializers.ModelSerializer):
        # 用于ModelViewSet的增删改查
        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', '')
            user = User(username=username, email=email, mobile=mobile)
            user.set_password(password)
            user.save()
            return user
    
    # 只用于登录,注册
    class UserSerializer(serializers.Serializer):
        # 用serialzier原方法进行方法重写
        username = serializers.CharField()
        password = serializers.CharField()
        mobile = serializers.CharField()
        email = serializers.EmailField()
        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', '')
            user = User(username=username, email=email, mobile=mobile)
            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
    
    
    # 重写
    # def update(self, instance, validated_data):
    #     instance.mobile = validated_data.get('mobile')
    #     instance.email = validated_data.get('email')
    #     instance.save()
    #     return instance
    

    2.5 user/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)
    
    urlpatterns = [
        path('login/', obtain_jwt_token),
        path('register/', views.RegisterView.as_view()),
    ]
    
    urlpatterns += router.urls
    

    3 Vue端

    • 目录结构

    3.1 main.js

    // The Vue build version to load with the `import` command
    // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    import Vue from 'vue'
    import App from './App'
    import router from './router'
    
    // 使用ant-design-vue
    import Antd from 'ant-design-vue';
    import 'ant-design-vue/dist/antd.css';
    Vue.config.productionTip = false
    Vue.use(Antd);
    
    
    // router.beforeEach((to, from, next) => {
    //   if (to.path =='/login' || localStorage.getItem("token")) {
    //       next()
    //   } else {
    //       alert("尚未登录,请先登录")
    //       return next("/login")
    //   }
    // })
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      components: { App },
      template: '<App/>'
    })
    

    3.2 http/api.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/', P)     // 获取用户列表
    export const add_user = P => axios_post('/user/register/', P)     // 注册新用户
    export const search_for = P => axios_get('user/user/?username=' + P.username)   // 根据用户名查找指定用户信息并展示
    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_dept_list = p => axios_get("/account/deptManage/", p)  //
    
    

    3.3 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)
                    })
            }
        )
    }
    

    3.4 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: '登录'},
        
        // 2.用户管理模块
        { path: '/',component: Home,name: 'home',
          children: [
            { path: 'usermanage', component: page('user-manage/index'), name: '用户管理' },
            { path: 'flowconf', component: page('workflow/WorkFlowConf'), name: '模板管理' },
            { path: 'flowtype', component: page('workflow/WorkFlowType'), name: '模板管理' },
            { path: 'baidu', component: page('BaiDu'), name: '跳转百度' },
          
          ]
        },
    
        // 3.配置工单模板模块
        // { path: '/',component: Home,name: 'home',
        //   children: [
    
        //     { path: '/rolemanage', component: page('role-manage/RoleManage'), name: '角色管理' },
        //     { path: '/flowconf', component: page('flow-conf/FlowConf'), name: '模板管理' },
        //   ]
        // },
    
      ]
    })
    

    3.5 Home.vue

    <template>
             <div id="components-layout-demo-basic">
                <a-layout>
                    <a-layout-sider>
                        <LeftMenu style="margin-left:-55px"></LeftMenu>
                        <!-- 以组件发昂视导入左侧菜单 -->
                    </a-layout-sider>
                    <a-layout>
                        <a-layout-header>
                            <Header/>
                            <!-- 导入头部 -->
                        </a-layout-header>
                            <div>
                                <!-- 这里的 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,
    #components-layout-demo-basic .ant-layout-footer {
      background: white;
      color: white;
    }
    #components-layout-demo-basic .ant-layout-footer {
      line-height: 1.5;
    }
    #components-layout-demo-basic .ant-layout-sider {
      background: white;
      color: white;
      line-height: 120px;
    }
    #components-layout-demo-basic .ant-layout-content {
      background: white;
      color: white;
      min-height: 120px;
      line-height: 120px;
    }
    </style>
    

    3.6 Header.vue

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

    3.7 LeftMenu.vue

    <template>
      <div>
        <a-icon type="smile"/>
        <a-switch :default-checked="false" @change="changeMode"/> 
        <span className="ant-divider"/>
        <a-icon type="bulb"/>
        <a-switch :default-checked="false" @change="changeTheme"/>
        <a-menu
          style=" 256px"
          :default-selected-keys="['1']"
          :default-open-keys="['sub1']"
          :mode="mode"
          :theme="theme"
          @click="handleClick"
        >
        <!-- 必须定义click方法,handleClick是用来跳转路由的,内置毁掉参数e,包括传递上去的key路由地址 -->
        
          <a-menu-item key="usermanage">
            <!-- key是路由地址 -->
            <a-icon type="user" />
            用户管理模块
          </a-menu-item>
    
          <a-menu-item key="baidu">
            <a-icon type="calendar" />
            百度翻译
          </a-menu-item>
          <a-sub-menu key="workflow">
            <span slot="title">
              <a-icon type="appstore" />
              <span>工单管理系统</span>
            </span>
              
              <a-menu-item key="flowtype" title="工单分类">
                工单分类
              </a-menu-item>
    
              <a-menu-item key="flowconf" title="工单模板">
                工单模板
              </a-menu-item>
    
              <a-menu-item key="newflowuserroleactionconf" title="配置审批流">
                  配置审批流
              </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">
              实例化工单
              </a-menu-item>
    
              <a-menu-item key="suborder">
              实例化子工单
              </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;
          this.$router.push({path:this.current});
          // click方法默认回调参数中的 key,所以 e.key就是传递来的路由
        },
      },
    };
    </script>
    

    3.8 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="primary" ghost style="float:left;margin-left:20px" @click="addNew">
                    AddUser
                    </a-button>
                    <EditForm
                        :visible.sync="visible"
                        :userList='userList'
                        @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"              
                    ></Pagination>
                </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';
    export default {
        components:{
            BreadCrumb,
            TableList,
            Search,
            EditForm,
            Pagination
        },
        data() {
            return {
                visible:false,
                userList: {
                  'id':'',
                  'username': '',
                  'passowrd': '',
                  'password_new': '',
                  'email':'',
                  'mobile':'',
                  'weixin':''
                },
                userListGet:[],
                searchList:{
                    'username':'',
                    'page':1,
                    'page_size':4
    
                },
                updateUserList:[],
                // 当前页码
                current:1,
                // 总共的数据多少条
                count:0
    
            }
        },
        methods: {
            addNew(){
              this.visible = true
              this.userList = {
                  'id':'',
                  'username': '',
                  'passowrd': '',
                  'password_new': '',
                  'email':'',
                  'mobile':'',
                  'weixin':''
                }
              // 用于控制组件显示
            },
            add(){
              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()
            }
        },
        created() {
    
        }
    }
    </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>
    
    

    3.9 BreadCrumb.vue

    <template>
        <div>
            <a-breadcrumb>
                <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>
    

    3.10 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="380px;float:right" 
                    placeholder="username" 
                    v-model="userList.username"
                    disabled="disabled"
                    ></a-input>                
                </p>
                <p v-else>UserName:
                    <a-input 
                    style="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="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="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="410px;float:right" placeholder="Email" v-model="userList.email"></a-input>
                </p>
                <br>
                <p>Mobile:
                    <a-input style="410px" placeholder="Mobile" v-model="userList.mobile"></a-input>
                </p>
                <br>
                <p>Weixin:
                    <a-input style="410px" placeholder="WeiXin" v-model="userList.weixin"></a-input>
                </p>
            </a-modal>
    
        </div>
    </template>
    
    <script>
    export default {
        props:['visible', 'userList'],
        data() {
            return {
            }
        },
        methods: {
            // 添加数据显示username的input框,修改的时候input框内数据不能显示
            choose(user_id){
    
            },
            handleOk(e) {
                this.$emit('add')
                // add方法的调用一定要在关闭弹窗上面,否则方法不执行完毕没有办法关闭弹窗
                // 调用父组件中 add 方法            
                this.$emit('update:visible', false)
                // 把 visible 的值更新为 false,控制组件不显示
    
            },
            handleCancel(e) {
                this.$emit('update:visible', false)
                // 把 visible 的值更新为 false,控制组件不显示
            },
        },
        created() {
    
        }
    }
    </script>
    
    <style scoped>
    
    </style>
    
    

    3.11 Search.vue

    <template>
        <div>
            <a-input-search placeholder="Input the username that you want to search for..." enter-button @search="onSearch" style="float:right;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>
    
    

    3.12 TableList.vue

    <template>
      <a-table 
        :columns="columns" 
        :data-source="userListGet" 
        :rowKey="(record,index)=>{return index}"
        :pagination= 'false'
      >
      <!-- 带:的都是属性绑定,不可以更换名字,带 : 就是 js 环境 -->
        <!-- 不写:rowKey="(record,index)=>{return index}"浏览器会发出警告 -->
        <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,
         50,
      },
      {
        title: 'UserName',
        dataIndex: 'username',
        key: 'username',
        scopedSlots: { customRender: 'username' },
         80,
      },
      {
        title: 'Email',
        dataIndex: 'email',
        key: 'email',
         100,
      },
      {
        title: 'Mobile',
        dataIndex: 'mobile',
        key: 'mobile',
        ellipsis: true,
         100,
      },
      {
        title: 'WeiXin',
        dataIndex: 'weixin',
        key: 'weixin',
        ellipsis: true,
         100,
      },
      {
        title: 'Date Joined',
        dataIndex: 'date_joined',
        key: 'date_joined',
        ellipsis: true,
         100,
      },
      {
        title:'操作',
        dataIndex: 'tags',
        key : 'tags',
         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){
          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      
          this.$emit('add')
        }
      },
      created(){
        this.get()
      }
    };
    </script>
    

    3.13 BaiDu.vue

    <template>
        <div>
    
        </div>
    </template>
    
    <script>
    export default {
        data() {
            return {
    
            }
        },
        methods: {
            goBaiDu(){
                window.location.href = 'https://fanyi.baidu.com/?aldtype=16047#auto/zh'
            }
        },
        created() {
            this.goBaiDu()
        }
    }
    </script>
    
    <style scoped>
    
    </style>
    

    3.14 Login.vue

    <template>
      <div width=300>
    
        <center><h1>登录</h1></center>
    
        <a-form-item label="用户名" v-bind="formlayout">
    
          <a-input v-model="username"/>
    
        </a-form-item>
        <a-form-item label="密码" v-bind="formlayout">
          <a-input-password v-model="password" placeholder="input password" />
        </a-form-item>
    
        <a-form-item v-bind="buttonlayout">
    
          <a-button type="primary"  @click="submit">登录</a-button>
    
        </a-form-item>
    
      </div>
    </template>
    
    <script type="text/javascript">
    import { user_login } from '../http/apis';
    
    export default{
    
      data(){
        return{
          username:"",
          password:'',
          //表单样式
          formlayout:{
            //标签
            labelCol:{
              xs:{span:24},
              sm:{span:8}
            },
            //文本框
            wrapperCol:{
              xs:{span:14},
              sm:{span:6}
            }
          },
          //按钮样式
          buttonlayout:{
            //按钮
            wrapperCol:{
              xs:{
                span:24,
                offset:0
              },
              sm:{span:16,offset:8}
            }
          }
        }
    
      },
      //自定义方法
      methods:{
        submit:function(){
          var data={'username':this.username,'password':this.password}
          user_login(data).then(resp => {
            console.log(resp)
            if(resp.token){
              // 如果返还token值,就储存 token username uid
              localStorage.setItem('token',resp.token)
              localStorage.setItem('username',resp.username)
              localStorage.setItem('uid',resp.id)
              this.$router.push('/')
            }
          }).catch(err=>{
            console.log(err)
            alert('登录失败')
          })
    
        }
      }
    };
    </script>
    
    <style type="text/css">
    
    </style>
    

    3.15 Pagitation.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>
    

    4 总结点

    4.1 serializer重写

    class UserSerializer(serializers.Serializer):
        # 用serializer重写,不能用ModelViewSet来添加,写上id会报错username不能设为key,因为User表继承的是AbstractUser,其中的username字段必须有unique=True属性设置唯一约束
        username = serializers.CharField()
        password = serializers.CharField()
        mobile = serializers.CharField()
        email = serializers.EmailField()
        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', '')
            user = User(username=username, email=email, mobile=mobile)
            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
    
    	def update(self, instance, validated_data):
            # instance 是当前对象obj
            # validated_data 是获取的变量
    		instance.mobile = validated_data.get('mobile')
    		instance.email = validated_data.get('email')
    		instance.save()
    		return instance
    

    4.2 要注意ModelViewSet的接口路由

    • 后端接口
    * 查询接口
    http://192.168.56.100:1594/user/user/
    http://192.168.56.100:1594/user/user/?username='cat'
    * 添加接口
    http://192.168.56.100:1594/user/user/
    * 删除接口(指定用户id删除)
    http://192.168.56.100:1594/user/user/id/
    * 修改接口(指定用户id修改)
    http://192.168.56.100:1594/user/user/id/
    
    • 前端访问路径配置
    export const get_userlist = P => axios_get('/user/user/', P)     
    // 查询所有:获取用户列表
    export const search_for = P => axios_get('user/user/?username=' + P.username)   
    // 查找指定信息:根据用户名查找指定用户信息并展示
    export const add_user = P => axios_post('/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和提交来的数据修改用户信息
    

    4.3 使用 ant-design-vue

    // 使用ant-design-vue
    import Antd from 'ant-design-vue';
    import 'ant-design-vue/dist/antd.css';
    Vue.config.productionTip = false
    Vue.use(Antd);
    

    4.4 ModelViewSet修改问题

    • put请求默认是全部修改
    • patch请求可以实现局部修改(后端已经封装好了方法)

    4.5 Vue嵌套路由

    • 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: '/',component: Home,name: 'home',
          children: [
            { path: 'usermanage', component: page('user-manage/Index'), name: '用户管理' },
            { path: 'flowconf', component: page('workflow/WorkFlowConf'), name: '模板管理' },
            { path: 'flowtype', component: page('workflow/WorkFlowType'), name: '模板类型管理' },
            { path: 'baidu', component: page('BaiDu'), name: '跳转百度' },
          
          ]
        },
      ]
    })
    
    • Home页面中写入
    <template>
        <div id="components-layout-demo-basic">
             <a-layout>
                  </a-layout-header>
                      <div>
                         <!-- 这里的 router-view 是绑定的路由 -->
                         <router-view></router-view>
                       </div>
                  </a-layout-header>
              </a-layout>
        </div>
    </template>
    
    <script>
    // 导入组件
    import LeftMenu from '@/components/layout/LeftMenu'
    import Header from '@/components/layout/Header'
    export default {
        // 注册组件
        components:{
            LeftMenu,
            Header
        },
    }
    
    • 绑定左侧菜单LeftMenu
    <template>
      <div>
        <a-menu
          style=" 256px"
          :default-selected-keys="['1']"
          :default-open-keys="['sub1']"
          :mode="mode"
          :theme="theme"
          @click="handleClick"
        >
        <!-- 必须定义click方法,handleClick是用来跳转路由的,内置毁掉参数e,包括传递上去的key路由地址 -->    
          <a-menu-item key="usermanage">
            <!-- key是路由地址 -->
            用户管理模块
          </a-menu-item>
    
          <a-menu-item key="baidu">
            百度翻译
          </a-menu-item>
        </a-menu>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
        };
      },
      methods: {
         handleClick(e) {
          this.current = e.key;
          this.$router.push({path:this.current});
          // click方法默认回调参数中的 key,所以 e.key就是传递来的路由
        },
      },
    };
    </script>
    

    4.6 ant-design-vue 图标大小

    • font-size 可以调节图标大小

    4.7 v-if v-else控制input框disabled属性

    <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="380px;float:right" 
                    placeholder="username" 
                    v-model="userList.username"
                    disabled="disabled"
                    ></a-input>    
                    <!-- disabled可以用来控制input是否能输入文字 -->
                </p>
                <p v-else>UserName:
                    <a-input 
                    style="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="380px;float:right"
                        v-model="userList.password"
                        :disabled = 'false'
                    >
                    </a-input>
                </p>
            </a-modal>
        </div>
    </template>
    
    <script>
    export default {
        props:['visible', 'userList'],
        methods: {
            handleOk(e) {
                this.$emit('add')
                // add方法的调用一定要在关闭弹窗上面,否则方法不执行完毕没有办法关闭弹窗        
                this.$emit('update:visible', false)
            },
            handleCancel(e) {
                this.$emit('update:visible', false)
            },
        },
    }
    </script>
    
    

    4.8 a-table中的分页器&按钮

     <a-table 
        :columns="要展示的列名称(可以自定义列表)" 
        :data-source="要展示的动态数据表名" 
        :pagination="pagination" 
        :rowKey="(record,index)=>{return index}"
      >
      <!-- 带:的都是属性绑定,不可以更换名字,带 : 就是 js 环境 -->
      <!-- 不写:rowKey="(record,index)=>{return index}"浏览器会发出警告 -->
         <p slot="tags" slot-scope="text,tags,i">
          <!-- 加入操作的按钮!其中tags是某列数据,i是索引 -->
          <a-button @click="delUser(text,tags,i)">删除</a-button>
        </p>
      </a-table>
    

    4.9 a-table中获取某一行值的方法

    <template>
      <a-table 
        :columns="columns" 
        :data-source="userListGet" 
        :pagination="pagination" 
        :rowKey="(record,index)=>{return index}"
      >
      <!-- 带:的都是属性绑定,不可以更换名字,带 : 就是 js 环境 -->
        <!-- 不写:rowKey="(record,index)=>{return index}"浏览器会发出警告 -->
        <p slot="tags" slot-scope="text,tags,i">
          <!-- 加入操作的按钮! -->
          <a-button @click="delUser(text,tags,i)">删除</a-button>
        </p>
      </a-table>
    </template>
    
    <script>
    const columns = [
      {
        title:'操作',
        dataIndex: 'tags',
        key : 'tags',
         100,
        scopedSlots : { customRender: 'tags'}
        // scopedSlots: { customRender: 'tags' },一定不能少不然渲染不了html标签
      }
    ]
    
    import { delete_user } from '@/http/apis'
    export default {
      data() {
        return {
          columns,//这里要返回值哦 
          pagination:{
          pageSize: 4,
          }
        };
      },
      methods:{
        delUser(text,tags,i){
           }     
        },
      }
    </script>
    

    4.10 控制对话框

    // 定义变量 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('有需要再叫我哈~') 
    }
    

    4.11 自动跳转到某一网址

    window.location.href = 'https://www.baidu.com/'
    

    4.12 前后端联调分页

    • 为了解决前端+后端能同时进行分页的问题,只定义前端分页,后端如果分页就会影响前端数据,如果后端不定义分页器,就会造成后端admin难以管理。

    • 后端代码

      • 实现局部分页
      from django.shortcuts import render
      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
      from user.serializers import UserSerializer, UserModelSerializer
      
      # 分页(局部):自定义分页器 局部
      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"}
          pagination_class = PageNum  # 注意不是列表(只能有一个分页模式)
      
    • 前端代码

      • Pagination.vue
      <template>
        <div>
          <a-pagination
            show-quick-jumper
            :default-current="2"
            :pageSize = '4'
            :total="count"
            show-less-items
            @change="onChange"
            v-model="current"
          />
            <!-- 一定是change方法,不然不能跳转 -->
        </div>
      </template>
      
      <script>
      export default {
          props:[ 'count' ],
          data() {
              return {
                  current:1
              }
          },
          methods: { 
              onChange() {
                  this.$emit('getPage', this.current)
          },
          },
          created() {
      
          }
      }
      </script>
      
      • Index.vue
      <template>
      <div>
        <div id="components-layout-demo-basic">
              <a-layout-content>
                  <TableList
                        :userListGet="userListGet"
                        :userList="userList"
                        @getUser="getUser"
                        @add="add"
                   >
                          
                   </TableList>
                   <Pagination
                        @getPage="getPage" 
                        :count="count"              
                    >
          		  </Pagination>
              </a-layout-content>
        </div>
      </div>
      </template>
      
      <script>
      import Pagination from "./components/Pagination"
      
      import { get_userlist } from '@/http/apis';
      export default {
          components:{
              BreadCrumb,
              TableList,
              Search,
              EditForm,
              Pagination
          },
          data() {
              return {
                  userListGet:[],
                  updateUserList:[],
                  // 当前页码
                  current:1,
                  // 总共的数据多少条
                  count:0
      
              }
          },
          methods: {
              getUser(){
                // 获取用户信息列表,父组件传递给子组件
                get_userlist(this.current).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()
              }
          },
          created() {
      
          }
      }
      </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>
      
      

    4.13 利用数据解耦性完善查询接口

    export default {
        components:{
            BreadCrumb,
            TableList,
            Search,
            EditForm,
            Pagination
        },
        data() {
            return {
                roleListGet:[],
                searchList:{
                    'zh_name':'',
                    'page':1,
                    'page_size':4,
                    
                },
    
        },
    methods: {
        find(){
                  // 根据用户名查找用户信息
                  search_for_role(this.searchList).then(res=>{
                      this.getRole()
                      // 数据解耦性!!!查询和查询某个其实可以调用同一个接口!
                      // 查询所有:http://192.168.56.100:1594/?page=1&zh_name=
                      // 查询某个:http://192.168.56.100:1594/?page=1&zh_name=many
    
                  })
                },
        getRole(){
            this.searchList.page = this.current
            // 获取用户信息列表,父组件传递给子组件
            get_rolelist(this.searchList).then(res=>{
            this.roleListGet = res.results
            this.count = res.count
            console.log(this.count)
            console.log(this.roleListGet)
        })
        },
    	}
    
  • 相关阅读:
    巧用nginx屏蔽对用户不可见的文件
    关于之前我的主页页面加载很慢的问题
    学习Entity Framework 中的Code First
    理解POCO
    浅谈依赖注入
    从Microsoft.AspNet.Identity看微软推荐的一种MVC的分层架构
    ASP.NET Identity V2
    泛型约束
    C# Serializable
    C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo
  • 原文地址:https://www.cnblogs.com/mapel1594184/p/14038717.html
Copyright © 2020-2023  润新知