1 Django端
1.1 settings.py
"""
Django settings for opwf project.
Generated by 'django-admin startproject' using Django 2.0.13.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
import datetime
import os, sys
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'uorj1ni^mnut@wo@c%)iv)%5=8dxlml4-j0!f3b%4#f*8a5)3t'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'user.apps.UserConfig',
'workflow.apps.WorkflowConfig',
'workerorder.apps.WorkerorderConfig',
# 'jwt',
# 'rest_framework_jwt',
# 'rest_framework.authentication'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'corsheaders.middleware.CorsMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'opwf.urls'
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
'http://127.0.0.1:8080',
'http://localhost:8080',
)
CORS_ALLOW_CREDENTIALS = True
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'opwf.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'opwf_db',
'USER': 'root',
'PASSWORD': '1',
'HOST': '127.0.0.1',
'PORT': '3306'
}
}
# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
REST_FRAMEWORK = {
# 文档报错: AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’
# 用下面的设置可以解决
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
# 默认设置是:
# 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema',
# 异常处理器
# 'EXCEPTION_HANDLER': 'user.utils.exception_handler',
# Base API policies 默认渲染器类
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
# 默认解析器类
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
# 1.认证器(全局)
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 在 DRF中配置JWT认证
# 'rest_framework.authentication.SessionAuthentication', # 使用session时的认证器
# 'rest_framework.authentication.BasicAuthentication' # 提交表单时的认证器
],
# 2.权限配置(全局): 顺序靠上的严格
'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAdminUser', # 管理员可以访问
# 'rest_framework.permissions.IsAuthenticated', # 认证用户可以访问
# 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # 认证用户可以访问, 否则只能读取
'rest_framework.permissions.AllowAny', # 所有用户都可以访问
],
# 3.限流(防爬虫)
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
],
# 3.1限流策略
# 'DEFAULT_THROTTLE_RATES': {
# 'user': '100/hour', # 认证用户每小时100次
# 'anon': '300/day', # 未认证用户每天能访问3次
# },
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None,
# 4.分页(全局):全局分页器, 例如 省市区的数据自定义分页器, 不需要分页
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# # 每页返回数量
# 'PAGE_SIZE': 3,
# 5.过滤器后端
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
# 'django_filters.rest_framework.backends.DjangoFilterBackend', 包路径有变化
],
# 5.1过滤排序(全局):Filtering 过滤排序
'SEARCH_PARAM': 'search',
'ORDERING_PARAM': 'ordering',
'NUM_PROXIES': None,
# 6.版本控制:Versioning 接口版本控制
'DEFAULT_VERSION': None,
'ALLOWED_VERSIONS': None,
'VERSION_PARAM': 'version',
# Authentication 认证
# 未认证用户使用的用户类型
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
# 未认证用户使用的Token值
'UNAUTHENTICATED_TOKEN': None,
# View configuration
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
'NON_FIELD_ERRORS_KEY': 'non_field_errors',
# Testing
'TEST_REQUEST_RENDERER_CLASSES': [
'rest_framework.renderers.MultiPartRenderer',
'rest_framework.renderers.JSONRenderer'
],
'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',
# Hyperlink settings
'URL_FORMAT_OVERRIDE': 'format',
'FORMAT_SUFFIX_KWARG': 'format',
'URL_FIELD_NAME': 'url',
# Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True,
# Browseable API
'HTML_SELECT_CUTOFF': 1000,
'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",
# Schemas
'SCHEMA_COERCE_PATH_PK': True,
'SCHEMA_COERCE_METHOD_NAMES': {
'retrieve': 'read',
'destroy': 'delete'
},
# 'Access-Control-Allow-Origin':'http://localhost:8080',
# 'Access-Control-Allow-Credentials': True
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'
AUTH_USER_MODEL = 'user.User'
# jwt载荷中的有效期设置
JWT_AUTH = {
# 1.token前缀:headers中 Authorization 值的前缀
'JWT_AUTH_HEADER_PREFIX': 'JWT',
# 2.token有效期:一天有效
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
# 3.刷新token:允许使用旧的token换新token
'JWT_ALLOW_REFRESH': True,
# 4.token有效期:token在24小时内过期, 可续期token
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24),
# 5.自定义JWT载荷信息:自定义返回格式,需要手工创建
'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',
}
1.2 models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):
'''
username:用户名
password:密码
mobile:手机号
email:邮箱
'''
username = models.CharField(max_length=30, unique=True)
# 写上 unique=True 就可以指定唯一,验证字段的时候自动验证
password = models.CharField(max_length=256)
mobile = models.CharField(max_length=11)
email = models.CharField(max_length=30)
token = models.CharField(max_length=256, default='')
weixin = models.CharField(max_length=30, null=True)
date_joined = models.DateField(auto_now_add=True)
class Meta:
db_table = 'user_user'
verbose_name = '用户表'
verbose_name_plural = verbose_name
class Role(models.Model):
'''
name:角色名称
description:描述
'''
zh_name = models.CharField(max_length=30)
en_name = models.CharField(max_length=30)
description = models.TextField()
class Meta:
db_table = 'user_role'
verbose_name = '角色表'
verbose_name_plural = verbose_name
class UserRole(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
class Meta:
db_table = 'user_userrole'
verbose_name = '用户角色表'
verbose_name_plural = verbose_name
1.3 serializers.py
# -*- coding: utf-8 -*-
from rest_framework import serializers
from rest_framework_jwt.serializers import jwt_payload_handler
from rest_framework_jwt.settings import api_settings
from user.models import User, UserRole, Role
class UserModelSerializer(serializers.ModelSerializer):
is_superuser = serializers.CharField(default=0)
roles = serializers.SerializerMethodField(required=False)
class Meta:
model = User
fields = '__all__'
def create(self, data):
username = data.get('username', '')
password = data.get('password', '')
mobile = data.get('mobile', '')
email = data.get('email', '')
weixin = data.get('weixin', '')
user = User(username=username, email=email, mobile=mobile, weixin=weixin)
user.set_password(password)
user.save()
return user
def get_roles(self, row):
roles_json = UserRole.objects.filter(user=row).values('role__id', "role__zh_name")
print('111', roles_json)
print('222', list(roles_json))
print(type(list(roles_json)))
return list(roles_json)
def get_serializer_class(self):
return UserModelSerializer
class UserSerializer(serializers.Serializer):
id = serializers.IntegerField()
username = serializers.CharField()
password = serializers.CharField()
mobile = serializers.CharField()
email = serializers.EmailField()
weixin = serializers.CharField()
token = serializers.CharField(read_only=True)
def create(self, data):
username = data.get('username', '')
password = data.get('password', '')
mobile = data.get('mobile', '')
email = data.get('email', '')
weixin = data.get('weixin', '')
user = User(username=username, email=email, mobile=mobile, weixin=weixin)
user.set_password(password)
user.save()
# 补充生成记录登录状态的token
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
user.token = token
return user
class UserRoleSerializer(serializers.ModelSerializer):
class Meta:
model = UserRole
fields = '__all__'
class RoleSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = "__all__"
1.4 views.py
import datetime
import random
from django.contrib.auth.hashers import make_password
from django.shortcuts import render
# Create your views here.
from rest_framework import viewsets
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.views import APIView
from user.models import User, Role, UserRole
from user.serializers import UserSerializer, UserModelSerializer, RoleSerializer, UserRoleSerializer
# 分页(局部):自定义分页器 局部
class PageNum(PageNumberPagination):
page_size = 4 # 每页显示多少条
page_size_query_param = 'page_size' # 查询字符串中代表每页返回数据数量的参数名, 默认值: None
page_query_param = 'page' # 查询字符串中代表页码的参数名, 有默认值: page
max_page_size = None # 最大页码数限制
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserModelSerializer
filter_fields = {"username", 'mobile', 'weixin', 'email'}
pagination_class = PageNum # 注意不是列表(只能有一个分页模式)
def perform_update(self, serializer):
user_obj = serializer.save()
# 原有的serializer反序列化添加
roles = self.request.data.get('roles')
user_obj.userrole_set.all().delete()
for role_id in roles:
userrole = UserRole.objects.create(user=user_obj, role_id=role_id)
def get_serializer_class(self):
print('action------->', self.action)
return UserModelSerializer
class UserGetViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class RoleViewSet(viewsets.ModelViewSet):
queryset = Role.objects.all()
serializer_class = RoleSerializer
filter_fields = {"zh_name"}
pagination_class = PageNum # 注意不是列表(只能有一个分页模式)
class RoleGetViewSet(viewsets.ModelViewSet):
# 用于角色查看接口(没有分页的那种)
queryset = Role.objects.all()
serializer_class = RoleSerializer
class RegisterView(APIView):
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
password_new = request.data.get('password_new')
role = request.data.get('roles')
print(role)
if not all([username, password, password_new]):
return Response(
{'msg': '信息不全', 'code': 400}
)
if password != password_new:
return Response(
{'code': 400, 'msg':'两次登录密码不一致'}
)
user_serializer = UserSerializer(data=request.data)
if user_serializer.is_valid():
user_serializer.save()
user_obj = User.objects.filter(username=username).first()
print(user_obj)
new_list = []
for i in role:
role_obj = Role.objects.filter(id=i).first()
# UserRole.objects.create(user=user_obj, role=role_obj)
# UserRole.objects.create(user_id=user_obj.id, role_id=role_obj.id)
new_list.append((UserRole(user=user_obj, role=role_obj)))
UserRole.objects.bulk_create(new_list)
return Response(
{'msg': '注册成功', 'code': 200, 'data':user_serializer.data}
)
return Response(
{'msg': '注册失败', 'error': user_serializer.errors }
)
1.5 urls.py
# -*- coding: utf-8 -*-
from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token
from user import views
router = DefaultRouter()
router.register(r'user', views.UserViewSet)
router.register(r'user_get', views.UserGetViewSet)
router.register(r'role', views.RoleViewSet)
router.register(r'role_get', views.RoleGetViewSet)
urlpatterns = [
path('login/', obtain_jwt_token),
path('register/', views.RegisterView.as_view()),
]
urlpatterns += router.urls
1.6 utils.py
# -*- coding: utf-8 -*-
def jwt_response_payload_handler(token, user=None, request=None, role=None):
"""
自定义jwt认证成功返回数据
:token 返回的jwt
:user 当前登录的用户信息[对象]
:request 当前本次客户端提交过来的数据
:role 角色
"""
if user.first_name:
name = user.first_name
else:
name = user.username
return {
'authenticated': 'true',
'id': user.id,
"role": role,
'name': name,
'username': user.username,
'email': user.email,
'token': token,
}
1.7 MyBaseModel.py
# -*- coding: utf-8 -*-
from django.db import models
class BaseModel(models.Model):
is_delete = models.BooleanField('是否刪除', default=0)
create_time = models.DateField('创建时间', auto_now_add=True, null=True)
update_time = models.DateField('更新时间', auto_now=True, null=True)
class Meta:
abstract = True
class BaseModelFlow(models.Model):
name = models.CharField('名称', max_length=60)
description = models.TextField('描述')
class Meta:
abstract = True
1.8 主路由urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('user/', include('user.urls')),
path('workflow/', include('workflow.urls')),
path('workerorder/', include('workerorder.urls')),
]
2 Vue端
<template>
<div>
<h3 style="margin-top:20px">
欢迎你
<a-icon type="smile" theme="outlined" style="font-size:20px;color:pink"/> 
{{username}}
<a-icon type="fire" style="font-size:25px;color:pink"/> 
</h3>
</div>
</template>
<script>
export default{
data(){
return{
username:localStorage.getItem('username')
}
}
}
</script>
2.2 @/components/layout/Home.vue
<template>
<div id="components-layout-demo-basic" style="">
<a-layout>
<a-layout-sider style="height:250px;150px">
<LeftMenu style="margin-left:-55px"></LeftMenu>
<!-- 以组件发昂视导入左侧菜单 -->
</a-layout-sider>
<a-layout>
<a-layout-header style="height:100px;500px;background:rgb(253,234,254)">
<Header/>
<!-- 导入头部 -->
</a-layout-header>
<div style="margin-left:100px">
<!-- 这里的 router-view 是绑定的路由 -->
<router-view></router-view>
</div>
</a-layout>
</a-layout>
</div>
</template>
<script>
// 导入组件
import LeftMenu from '@/components/layout/LeftMenu'
import Header from '@/components/layout/Header'
export default {
// 注册组件
components:{
LeftMenu,
Header
},
data() {
return {
}
},
methods: {
},
created() {
}
}
</script>
<style scoped>
#components-layout-demo-basic {
text-align: center;
}
#components-layout-demo-basic .ant-layout-header {
background: white;
}
#components-layout-demo-basic .ant-layout-footer {
background: rgb(253,234,254);
color: rgb(253,234,254);
}
#components-layout-demo-basic .ant-layout-footer {
line-height: 1.5;
}
#components-layout-demo-basic .ant-layout-sider {
background: rgb(253,234,254);
color: rgb(253,234,254);
line-height: 120px;
}
#components-layout-demo-basic .ant-layout-content {
background: white;
color: white;
min-height: 120px;
line-height: 120px;
}
.ant-layout.ant-layout-has-sider{
1700px;
height: 800px;
background:rgb(253,234,254)
}
.ant-layout{
background:rgb(253,234,254);
800px
}
</style>
<template>
<div>
<a-switch :default-checked="false" @change="changeMode" style="margin-top:70px"/> 
<br>
<a-menu
style=" 300px;background:rgb(253,234,254)"
:default-selected-keys="['1']"
:default-open-keys="['sub1']"
:mode="mode"
:theme="theme"
@click="handleClick"
>
<!-- 必须定义click方法,handleClick是用来跳转路由的,内置毁掉参数e,包括传递上去的key路由地址 -->
<a-sub-menu >
<span slot="title">
<a-icon type="user" />
<span>用户模块</span>
</span>
<a-menu-item key="usermanage" title="用户信息管理" style="background:rgb(253,234,254)">
信息管理
</a-menu-item>
<!-- key是路由地址 -->
<a-menu-item key="rolemanage" title="工单模板" style="background:rgb(253,234,254)">
角色管理
</a-menu-item>
</a-sub-menu>
<a-menu-item key="baidu">
<a-icon type="calendar" />
百度翻译
</a-menu-item>
<a-sub-menu key="workflow" style="background:rgb(253,234,254)">
<span slot="title">
<a-icon type="appstore" />
<span>模板管理</span>
</span>
<a-menu-item key="flowtype" title="工单分类" style="background:rgb(253,234,254);">
模板分类
</a-menu-item>
<a-menu-item key="flowconf" title="工单模板" style="background:rgb(253,234,254)">
新建模板
</a-menu-item>
</a-sub-menu>
<a-sub-menu key="workorder">
<span slot="title"><a-icon type="setting" /><span>工单管理</span></span>
<a-menu-item key="workorder" style="background:rgb(253,234,254)">
实例化工单
</a-menu-item>
<a-menu-item key="suborder" style="background:rgb(253,234,254)">
实例化子工单
</a-menu-item>
</a-sub-menu>
</a-menu>
</div>
</template>
<script>
export default {
data() {
return {
mode: 'inline',
theme: 'light',
current: '1'
};
},
methods: {
changeMode(checked) {
this.mode = checked ? 'vertical' : 'inline';
},
changeTheme(checked) {
this.theme = checked ? 'dark' : 'light';
},
handleClick(e) {
this.current = e.key;
console.log(e.domEvent)
this.$router.push({path:this.current});
// click方法默认回调参数中的 key,所以 e.key就是传递来的路由
},
},
};
</script>
<style>
.a-menu{
background:rgb(253,234,254);
550px;
}
.ul{
background:rgb(253,234,254)
}
.ant-menu-submenu > .ant-menu{
background-color: rgb(253,234,254);
}
</style>
2.4 @/http/apis.js
//将我们http.js中封装好的 get,post.put,delete,patch 导过来
import { axios_get, axios_post, axios_delete, axios_put, axios_patch } from './index.js'
//按照格式确定方法名
export const user_login = P => axios_post("/user/login/", P) // 用户登录
// 用户模块
// 用户信息管理
export const get_userlist = P => axios_get('/user/user/?page='+P, P) // 获取用户列表
export const get_userlist_new = P => axios_get('user/user_get/', P) // 获取用户列表(不包含分页的接口)
export const add_user = P => axios_post('/user/register/', P) // 注册新用户
export const search_for = P => axios_get('user/user/', P) // 根据用户名查找指定用户信息并展示
export const delete_user = P => axios_delete('/user/user/' + P + '/') // 根据获取到的用户id删除用户信息
export const update_user = P => axios_put('/user/user/'+ P.id +'/', P) // 根据用户id和提交来的数据修改用户信息
// 角色管理
export const get_rolelist = P => axios_get('/user/role/?page='+P, P) // 获取角色列表
export const get_rolelist_new = P => axios_get('/user/role_get/', P) // 获取角色列表(不包含分页的接口)
export const add_role = P => axios_post('/user/role/', P) // 注册新角色
export const search_for_role = P => axios_get('user/role/', P) // 根据角色名查找指定角色信息并展示
export const delete_role = P => axios_delete('/user/role/' + P + '/') // 根据获取到的角色id删除角色信息
export const update_role = P => axios_put('/user/role/'+ P.id +'/', P) // 根据角色id和提交来的数据修改角色信息
// 工单模块---分类工单
export const get_flowtypelist = P => axios_get('/workflow/flowtype/?page='+P, P) // 获取模板分类列表
export const get_flowtypelist_new = P => axios_get('/workflow/flowtype_get/', P) // 获取模板分类列表(不含分页)
export const add_flowtype = P => axios_post('/workflow/flowtype/', P) // 创建新模板分类
export const search_for_flowtype = P => axios_get('/workflow/flowtype/', P) // 根据模板分类名查找指定模板分类信息并展示
export const delete_flowtype = P => axios_delete('/workflow/flowtype/' + P + '/') // 根据获取到的模板分类id删除模板分类信息
export const update_flowtype = P => axios_put('/workflow/flowtype/'+ P.id +'/', P) // 根据模板分类id和提交来的数据修改模板分类信息
// 工单模块---工单模板
export const get_flowconflist = P => axios_get('/workflow/flowconf/?page='+P, P) // 获取工单模板列表
export const get_flowconflist_new = P => axios_get('/workflow/flowconf_get/', P) // 获取工单模板列表(不含分页)
export const add_flowconf = P => axios_post('/workflow/flowconf/', P) // 创建新工单模板
export const search_for_flowconf = P => axios_get('/workflow/flowconf/', P) // 根据工单模板名查找指定工单模板信息并展示
export const delete_flowconf = P => axios_delete('/workflow/flowconf/' + P + '/') // 根据获取到的工单模板id删除工单模板信息
export const update_flowconf = P => axios_put('/workflow/flowconf/'+ P.id +'/', P) // 根据工单模板id和提交来的数据修改工单模板信息
// 工单模块---配置审批流
export const get_approveconflist = P => axios_get('/workflow/approveconf/?page='+P, P) // 获取配置审批流列表
export const add_approveconf = P => axios_post('/workflow/approveconf/', P) // 配置新审批流
export const delete_approveconf = P => axios_delete('/workflow/approveconf/' + P + '/') // 根据审批流id删除审批流信息
export const update_approveconf = P => axios_put('/workflow/approveconf/'+ P.id +'/', P) // 根据审批流id和提交来的数据修改审批流信息
2.5 @/http/index.js
import axios from 'axios'
// 第一步:设置axios
axios.defaults.baseURL = "http://192.168.56.100:1594/"
//全局设置网络超时
axios.defaults.timeout = 10000;
//设置请求头信息
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.defaults.headers.put['Content-Type'] = 'application/json';
// 第二:设置拦截器
/**
* 请求拦截器(当前端发送请求给后端前进行拦截)
* 例1:请求拦截器获取token设置到axios请求头中,所有请求接口都具有这个功能
* 例2:到用户访问某一个页面,但是用户没有登录,前端页面自动跳转 /login/ 页面
*/
axios.interceptors.request.use(
config => {
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
const token = localStorage.getItem("token")
// console.log(token)
if (token) {
config.headers.Authorization = 'JWT ' + token
}
return config;
},
error => {
return Promise.error(error);
})
axios.interceptors.response.use(
// 请求成功,因为 API返回的状态码有多个,所以一定要在这里写上,不然会无法访问页面
res => res.status === 200 || 201 || 204 ? Promise.resolve(res) : Promise.reject(res),
// 请求失败
error => {
if (error.response) {
// 判断一下返回结果的status == 401? ==401跳转登录页面。 !=401passs
// console.log(error.response)
if (error.response.status === 401) {
// 跳转不可以使用this.$router.push方法、
// this.$router.push({path:'/login'})
window.location.href = "http://127.0.0.1:8080/"
} else {
// errorHandle(response.status, response.data.message);
return Promise.reject(error.response);
}
// 请求已发出,但是不在2xx的范围
} else {
// 处理断网的情况
// eg:请求超时或断网时,更新state的network状态
// network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
// 关于断网组件中的刷新重新获取数据,会在断网组件中说明
// store.commit('changeNetwork', false);
return Promise.reject(error.response);
}
});
// 第三:封装axios请求
// 3.1 封装get请求
export function axios_get(url, params) {
return new Promise(
(resolve, reject) => {
axios.get(url, {params:params})
.then(res => {
// console.log("封装信息的的res", res)
resolve(res.data)
}).catch(err => {
reject(err.data)
})
}
)
}
// 3.2 封装post请求
export function axios_post(url, data) {
return new Promise(
(resolve, reject) => {
// console.log(data)
axios.post(url, JSON.stringify(data))
.then(res => {
// console.log("封装信息的的res", res)
resolve(res.data)
}).catch(err => {
reject(err.data)
})
}
)
}
// 3.3 封装put请求
export function axios_put(url, data) {
return new Promise(
(resolve, reject) => {
// console.log(data)
axios.put(url, JSON.stringify(data))
.then(res => {
// console.log("封装信息的的 res", res)
resolve(res.data)
}).catch(err => {
reject(err.data)
})
}
)
}
// 3.4 封装patch请求(可用于局部修改)
export function axios_patch(url, data) {
return new Promise(
(resolve, reject) => {
// console.log(data)
axios.patch(url, JSON.stringify(data))
.then(res => {
// console.log("封装信息的的res", res)
resolve(res.data)
}).catch(err => {
reject(err.data)
})
}
)
}
// 3.5 封装delete请求
export function axios_delete(url, data) {
return new Promise(
(resolve, reject) => {
// console.log(data)
axios.delete(url, { params: data })
.then(res => {
// console.log("封装信息的的res", res)
resolve(res.data)
}).catch(err => {
// reject(err.data)
})
}
)
}
2.6 @/views/user-manage/components/BreadCrumb.vue
<template>
<div>
<a-breadcrumb>
<br>
<a-breadcrumb-item href="">
<a-icon type="home" />
</a-breadcrumb-item>
<a-breadcrumb-item href="">
<a-icon type="user" />
<span>首页</span>
</a-breadcrumb-item>
<a-breadcrumb-item>
用户模块
</a-breadcrumb-item>
<a-breadcrumb-item>
信息管理页面
</a-breadcrumb-item>
</a-breadcrumb>
</div>
</template>
<script>
export default {
name:"BreadCrumb",
data() {
return {
}
},
methods: {
},
created() {
}
}
</script>
<style scoped>
</style>
<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>
<br>
<p>Role:
<a-select
mode="multiple"
style=" 410px; float:right"
placeholder="Please select"
@change="handleChange"
v-model="userList.roles"
>
<a-select-option v-for="i in roleList" :key="i.id" >
{{ i.zh_name }}
</a-select-option>
</a-select>
</p>
</a-modal>
</div>
</template>
<script>
export default {
props:['visible', 'userList', 'roleList'],
data() {
return {
}
},
methods: {
handleOk(e) {
this.$emit('add', this.role)
// add方法的调用一定要在关闭弹窗上面,否则方法不执行完毕没有办法关闭弹窗
// 调用父组件中 add 方法
this.$emit('update:visible', false)
// 把 visible 的值更新为 false,控制组件不显示
},
handleCancel(e) {
this.$emit('update:visible', false)
// 把 visible 的值更新为 false,控制组件不显示
},
handleChange(value) {
// console.log(`selected ${value}`);
console.log(value)
this.userList.roles = value
},
},
created() {
}
}
</script>
<style scoped>
</style>
<template>
<div>
<a-pagination
show-quick-jumper
:default-current="2"
:pageSize = '4'
:total="count"
show-less-items
@change="onChange"
v-model="current"
/>
</div>
</template>
<script>
export default {
props:[ 'count' ],
data() {
return {
current:1
}
},
methods: {
onChange() {
this.$emit('getPage', this.current)
},
},
created() {
}
}
</script>
<style scoped>
</style>
2.9 @/views/user-manage/components/Search.vue
<template>
<div>
<a-input-search placeholder="Input the username that you want to search for..." enter-button @search="onSearch" style="float:right;400px;" v-model="searchList.username"/>
</div>
</template>
<script>
export default {
props:['searchList'],
data() {
return {
}
},
methods: {
onSearch(){
this.$emit('find')
// 调用父组件中的find方法
}
},
created() {
}
}
</script>
<style scoped>
</style>
2.10 @/views/user-manage/components/TableList.vue
<template>
<a-table
:columns="columns"
:data-source="userListGet"
:rowKey="(record,index)=>{return index}"
:pagination= 'false'
style="height:430px"
>
<!-- 带:的都是属性绑定,不可以更换名字,带 : 就是 js 环境 -->
<!-- 不写:rowKey="(record,index)=>{return index}"浏览器会发出警告 -->
<p slot="roles" slot-scope="roles">
<a-tag
v-for="role in roles"
:key="role.id"
color="pink"
>
{{ role.role__zh_name }}
</a-tag>
</p>
<p slot="tags" slot-scope="text,tags,i">
<!-- 加入操作的按钮! -->
<a-button @click="delUser(text,tags,i)">删除</a-button>
<a-button @click="updateUser(text,tags,i)">修改</a-button>
</p>
</a-table>
</template>
<script>
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
// ellipsis: true,
50,
},
{
title: 'UserName',
dataIndex: 'username',
key: 'username',
scopedSlots: { customRender: 'username' },
80,
},
{
title: 'Email',
dataIndex: 'email',
key: 'email',
150,
},
{
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: 'Roles',
dataIndex: 'roles',
key: 'roles',
ellipsis: true,
150,
scopedSlots : { customRender: 'roles'}
// 不写的话显示不了标签
},
{
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){
const roleIds = []
tags.roles.forEach(item => {
roleIds.push(item.role__id)
});
this.userList.id = tags.id
this.userList.username = tags.username
this.userList.password = tags.password,
this.userList.password_new = tags.password
this.userList.email = tags.email
this.userList.mobile = tags.mobile
this.userList.weixin = tags.weixin
// 传一个角色id的列表给父组件
this.userList.roles = roleIds
this.$emit('add')
}
},
created(){
this.get()
}
};
</script>
2.11 @/views/user-manage/index.vue
<template>
<div>
<div id="components-layout-demo-basic">
<a-layout>
<a-layout-header>
<BreadCrumb style="float:left"></BreadCrumb>
</a-layout-header>
<a-layout>
<a-layout-content>
<div style="margin-bottom:80px">
<a-button type="danger" ghost style="float:left;margin-left:20px;margin-top:16px;color:pink;border-color:pink;font-size:16px" @click="addNew">
AddUser
</a-button>
<EditForm
:visible.sync="visible"
:userList='userList'
:roleList='roleList'
@add="add"
>
<!-- .sync控制组件是否显示 -->
</EditForm>
<Search
style="margin-bottom:-20px;margin-top:10px"
:searchList="searchList"
@find="find"
@getUser="getUser"
>
</Search>
</div>
<TableList
:userListGet="userListGet"
:userList="userList"
@getUser="getUser"
@add="add"
>
</TableList>
<Pagination
@getPage="getPage"
:count="count"
style="margin-top:20px; "
></Pagination>
<p></p>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</div>
</template>
<script>
import BreadCrumb from "./components/BreadCrumb";
import TableList from "./components/TableList";
import Search from "./components/Search";
import EditForm from "./components/EditForm";
import Pagination from "./components/Pagination"
import { add_user, search_for, get_userlist, update_user } from '@/http/apis';
import { delete_user } from '../../http/apis';
import { get_rolelist_new, add_role_user } from '../../http/apis';
export default {
components:{
BreadCrumb,
TableList,
Search,
EditForm,
Pagination
},
data() {
return {
visible:false,
userList: {
'id':'',
'username': '',
'passowrd': '',
'password_new': '',
'email':'',
'mobile':'',
'weixin':'',
'roles':[]
// 没定义roles字段的话,没办法绑定
},
roleList: [],
userListGet:[],
searchList:{
'username':'',
'page':1,
'page_size':4
},
updateUserList:[],
// 当前页码
current:1,
// 总共的数据多少条
count:0,
addRoleList:[]
}
},
methods: {
addNew(){
this.visible = true
this.userList = {
'id':'',
'username': '',
'passowrd': '',
'password_new': '',
'email':'',
'mobile':'',
'weixin':'',
'roles':[]
}
// 用于控制组件显示
},
add(role_list){
if(this.userList.id){
this.visible = true
update_user(this.userList).then(res=>{
// alert('修改成功')
this.getUser()
})
}else{
// 添加用户,子组件中编辑的值实际上是写在父组件上面的
add_user(this.userList).then(res=>{
console.log(res)
alert('添加新用户成功')
this.getUser()
})
this.visible=false
}
},
find(){
// 根据用户名查找用户信息
search_for(this.searchList).then(res=>{
// 阔落的办法可以解决bug,但是不支持查询出多条数据,因为没办法分页
// if(this.searchList.username){
// // 修复如果没有搜索数据,回车就只能显示一个页面的bug
// console.log(res)
// this.userListGet = res.results
// this.count = res.results.length
// }else{
// this.getUser()
// }
this.getUser()
})
},
getUser(){
this.searchList.page = this.current
// 获取用户信息列表,父组件传递给子组件
get_userlist(this.searchList).then(res=>{
this.userListGet = res.results
this.count = res.count
console.log(this.count)
console.log(this.userListGet)
})
},
// 获取页码
getPage(currentChild){
// 获取到的currentChild是子组件传递过来是第几页
this.current = currentChild
console.log(this.current)
this.getUser()
},
getRole(){
get_rolelist_new().then(res=>{
this.roleList = res
console.log(111111)
console.log(this.roleList)
})
}
},
created() {
this.getRole()
}
}
</script>
<style scoped>
#components-layout-demo-basic {
text-align: center;
}
#components-layout-demo-basic .ant-layout-header,
#components-layout-demo-basic .ant-layout-footer {
background: white;
color: #fff;
}
#components-layout-demo-basic .ant-layout-footer {
line-height: 1.5;
}
#components-layout-demo-basic .ant-layout-content {
background: white;
color: #fff;
min-height: 120px;
line-height: 120px;
}
#components-layout-demo-basic > .ant-layout {
margin-bottom: 48px;
}
#components-layout-demo-basic > .ant-layout:last-child {
margin: 0;
}
</style>
2.12 @/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/layout/Home'
const page = name => () => import('@/views/' + name)
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{ path: '/login',component: page('Login'),name: '登录'},
// { path: '/aaa',component: page('approveconf-manage/index'),name: '测试approveconf'},
{ path: '/',component: Home,name: 'home',
children: [
{ path: 'usermanage', component: page('user-manage/index'), name: '信息管理' },
{ path: 'rolemanage', component: page('role-manage/index'), name: '角色管理' },
{ path: 'flowtype', component: page('flowtype-manage/index'), name: '模板分类管理' },
{ path: 'flowconf', component: page('flowconf-manage/index'), name: '工单模板管理' },
{ path: 'approveconf', component: page('approveconf-manage/index'), name: '新建模板管理' },
{ path: 'workorder', component: page('workorder-manage/index'), name: '实例化工单' },
{ path: 'flowconfform', component: page('flowconfform-manage/index'), name: '实例化工单form' },
{ path: 'baidu', component: page('BaiDu'), name: '跳转百度' },
]
}
]
})