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"/> 欢迎你:{{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="width: 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="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>
</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;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>
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,
width: 50,
},
{
title: \'UserName\',
dataIndex: \'username\',
key: \'username\',
scopedSlots: { customRender: \'username\' },
width: 80,
},
{
title: \'Email\',
dataIndex: \'email\',
key: \'email\',
width: 100,
},
{
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:\'操作\',
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){
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="width: 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="width:380px;float:right"
placeholder="username"
v-model="userList.username"
disabled="disabled"
></a-input>
<!-- disabled可以用来控制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-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\',
width: 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)
})
},
}