一、vue前端框架
npm是node.js的一个工具,当装完node.js这个前端平台,即可使用。
VUE的安装和语法请参考:前端框架Vue3_AdaTina的博客-CSDN博客_vue3前端框架
1、创建vue工程
1)vue create 工程名称 ,然后选择手工选择功能(方向键选择,空格键选定,回车键下一步)
2)模块选择
3、配置选项
4)工程创建完毕
5)启动命令
6)以下方式可以用浏览器进行访问
2、目录介绍
3、Ant Design vue 是蚂蚁金服开发的一款vue前端框架,可以直接拿来代码进行使用。
1)安装方式
npm i --save ant-design-vue@3.2.6
2)路径参考:https://www.antdv.com/components/overview
4、echarts百度图表插件Apache ECharts
1)安装
npm install echarts --save --registry=https://registry.npm.taobao.org
2)粘贴代码,并部分修改内容。
5、注册全局变量
#main.js文件(@表示src目录)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import settings from "@/settings"
import Antd from "ant-design-vue"
const app=createApp(App)
app.use(store).use(router).use(Antd).mount('#app')
//注册全局变量
app.config.globalProperties.$settings=settings
#settings.js文件
export default {
host: 'http://api.example.cn'
}
引用方式:
this.$settings.host可以得到host内容
6、案例
1)showView视图页代码
#ShowView视图页代码
<template>
<a-row>
<a-col :span="8">
<a-input-search
v-model:value="city"
placeholder="input search text"
enter-button="Search"
size="large"
@search="get_weather"
/>
</a-col>
</a-row>
<a-row>
<a-col :span="16" >
<a-table :columns="columns" :data-source="weather_list">
<template #bodyCell="{ column, text }">
<template v-if="column.dataIndex === 'name'">
<a>{{ text }}</a>
</template>
</template>
</a-table>
</a-col>
</a-row>
<a-row>
<a-col :span="12">
<div class="chart" ref="chart01"></div>
</a-col>
<a-col :span="12">
<div class="chart" ref="chart02"></div>
</a-col>
</a-row>
</template>
<script>
import axios from "axios"
import * as echarts from 'echarts';
import {ref} from 'vue';
export default {
name: "ShowView",
setup() {
const value = ref();
return {
value,
};
},
data(){
return {
city: "北京",
weather_list: [],
columns : [
{
title: "日期",
dataIndex: 'date',
key: 'date',
},
{
title: '天气',
dataIndex: 'type',
key: 'type',
width: 80,
},
{
title: '最高温度',
dataIndex: 'high',
key: 'high',
ellipsis: true,
},
{
title: '最低气温',
dataIndex: 'low',
key: 'low',
ellipsis: true,
},
],
}
},
methods: {
get_weather() {
axios.get("http://wthrcdn.etouch.cn/weather_mini",
{
params: {
city: this.city
}
}
).then((response) => {
this.weather_list = response.data.data.forecast
})
},
chart01() {
var myChart = echarts.init(this.$refs.chart01);
var option;
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
// Use axis to trigger tooltip
type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
}
},
legend: {},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value'
},
yAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
series: [
{
name: 'Direct',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [320, 302, 301, 334, 390, 330, 320]
},
{
name: 'Mail Ad',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: 'Affiliate Ad',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: 'Video Ad',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [150, 212, 201, 154, 190, 330, 410]
},
{
name: 'Search Engine',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [820, 832, 901, 934, 1290, 1330, 1320]
}
]
};
option && myChart.setOption(option);
},
chart02(){
var myChart = echarts.init(this.$refs.chart02);
var option;
option = {
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '40',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{value: 1048, name: 'Search Engine'},
{value: 735, name: 'Direct'},
{value: 580, name: 'Email'},
{value: 484, name: 'Union Ads'},
{value: 300, name: 'Video Ads'}
]
}
]
};
option && myChart.setOption(option);
},
},
mounted() {
this.get_weather()
this.chart01()
this.chart02()
}
}
</script>
<style scoped>
.chart{
height:500px;
}
</style>
2)BaseView页代码
<template>
<a-layout>
<a-layout-header class="header">
<div class="logo" />
<a-menu
v-model:selectedKeys="selectedKeys1"
theme="dark"
mode="horizontal"
:style="{ lineHeight: '64px' }"
>
<a-menu-item key="1">nav 1</a-menu-item>
<a-menu-item key="2">nav 2</a-menu-item>
<a-menu-item key="3">nav 3</a-menu-item>
</a-menu>
</a-layout-header>
<a-layout>
<a-layout-sider width="200" style="background: #fff">
<a-menu
v-model:selectedKeys="selectedKeys2"
v-model:openKeys="openKeys"
mode="inline"
:style="{ height: '100%', borderRight: 0 }"
>
<a-sub-menu key="sub1">
<template #title>
<span>
<user-outlined />
subnav 1
</span>
</template>
<a-menu-item key="1">option1</a-menu-item>
<a-menu-item key="2">option2</a-menu-item>
<a-menu-item key="3">option3</a-menu-item>
<a-menu-item key="4">option4</a-menu-item>
</a-sub-menu>
<a-sub-menu key="sub2">
<template #title>
<span>
<laptop-outlined />
subnav 2
</span>
</template>
<a-menu-item key="5">option5</a-menu-item>
<a-menu-item key="6">option6</a-menu-item>
<a-menu-item key="7">option7</a-menu-item>
<a-menu-item key="8">option8</a-menu-item>
</a-sub-menu>
<a-sub-menu key="sub3">
<template #title>
<span>
<notification-outlined />
subnav 3
</span>
</template>
<a-menu-item key="9">option9</a-menu-item>
<a-menu-item key="10">option10</a-menu-item>
<a-menu-item key="11">option11</a-menu-item>
<a-menu-item key="12">option12</a-menu-item>
</a-sub-menu>
</a-menu>
</a-layout-sider>
<a-layout style="padding: 0 24px 24px">
<a-breadcrumb style="margin: 16px 0">
<a-breadcrumb-item>Home</a-breadcrumb-item>
<a-breadcrumb-item>List</a-breadcrumb-item>
<a-breadcrumb-item>App</a-breadcrumb-item>
</a-breadcrumb>
<a-layout-content
:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
>
<router-view/>
</a-layout-content>
</a-layout>
</a-layout>
</a-layout>
</template>
<script>
import 'ant-design-vue/dist/antd.css';
import { UserOutlined, LaptopOutlined, NotificationOutlined } from '@ant-design/icons-vue';
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: "BaseView",
components: {
UserOutlined,
LaptopOutlined,
NotificationOutlined,
},
setup() {
return {
selectedKeys1: ref(['2']),
selectedKeys2: ref(['1']),
collapsed: ref(false),
openKeys: ref(['sub1']),
};
},
});
</script>
<style>
#components-layout-demo-top-side-2 .logo {
float: left;
width: 120px;
height: 31px;
margin: 16px 24px 16px 0;
background: rgba(255, 255, 255, 0.3);
}
.ant-row-rtl #components-layout-demo-top-side-2 .logo {
float: right;
margin: 16px 0 16px 24px;
}
.site-layout-background {
background: #fff;
}
</style>
3)路由页代码
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import ShowView from '../views/ShowView.vue'
import BaseView from "../views/BaseView";
const routes = [
{
meta: {
title: 'Urics自动化运维平台',
},
path: '/uric',
alias: '/', // 给当前路径起一个别名
name: 'BaseView',
component: BaseView, // 快捷键:Alt+Enter快速导包
children: [
{
meta: {
title: '展示中心'
},
path: 'show',
alias: '',
name: 'ShowView',
component: ShowView
},
{
meta: {
title: '资产管理'
},
path: 'home',
name: 'HomeView',
component: HomeView
}
]
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
二、Django框架分离
virtualenv是虚拟环境,它的作用是使运行在不同python环境的项目,不受其他项目影响。
1、windows安装virtualenv
把虚拟环境复制到ENV(任意目录)目录
激活/退出虚拟环境
2)mac和linux创建虚拟环境
1、安装虚拟环境的第三方包
pip install virtualenv
使用清华源安装:pip install virtualenv -i https://pypi.python.org/simple/
2、创建虚拟环境
virtualenv ENV 在当前目录下创建名为ENV的虚拟环境(如果第三方包virtualenv安装在python3下面,此时创建的虚拟环境就是基于python3的)
2)指定python版本创建虚拟环境
virtualenv -p /usr/local/bin/python2.7 ENV2
3)继承系统第三方库的创建方法
virtualenv --system-site-packages ENV
3、激活环境
cd ~/ENV 跳转到虚拟环境的文件夹
source bin/activate 激活虚拟环境
pip list 查看当前虚拟环境下所安装的第三方库
deactivate 退出虚拟环境4、删除虚拟环境
直接删除虚拟环境文件
2、pycharm安装虚拟环境的Django工程
以上location目录修改成了E:\uric工程\uric_api,以下是生成的默认结构
调整结构
1)创建app
2)settings文件删除,并新建包settings,并建立如下目录,dev是上线前的配置文件,prod是上线时用的配置文件,test是测试时用的配置文件,先把settings.py的配置文件拷贝到这三个文件中
2)修改入口文件
3)修改配置文件
#添加检索目录,方便引用文件
import sys
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# 添加检索路径
sys.path.insert(0, str(BASE_DIR / 'apps'))
#安装home模块
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
"home"
]
外层urls文件
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path("", include("home.urls")),
]
home/url文件
from django.urls import path
from . import views
urlpatterns = [
path("test", views.TestAPIView.as_view())
]
home/ views文件
from rest_framework.views import APIView
from rest_framework.response import Response
class TestAPIView(APIView):
def get(self,request):
return Response({"message":"hello"},)
4)重构的结构
3、log日志
配置文件settings/dev.py
# 日志配置
LOGGING = {
# 使用的python内置的logging模块,那么python可能会对它进行升级,所以需要写一个版本号,目前就是1版本
'version': 1,
# 是否去掉目前项目中其他地方中以及使用的日志功能,但是将来我们可能会引入第三方的模块,里面可能内置了日志功能,所以尽量不要关闭,肯定False
'disable_existing_loggers': False,
# 日志的处理格式
'formatters': {
# 详细格式,往往用于记录日志到文件/其他第三方存储设备
'verbose': {
# levelname等级,asctime记录时间,module表示日志发生的文件名称,lineno行号,message错误信息
'format': '{levelname} {asctime} {module}:{lineno:d} {message}',
# 日志格式中的,变量分隔符
'style': '{',
},
'simple': { # 简单格式,往往用于终端
'format': '{levelname} {module}:{lineno} {message}',
'style': '{',
},
},
'filters': { # 日志的过滤设置,可以对日志进行输出时的过滤用的
# 在debug=True下产生的一些日志信息,要不要记录日志,需要的话就在handlers中加上这个过滤器,不需要就不加
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': { # 日志的处理方式
'console': { # 终端下显示
'level': 'DEBUG', # 日志的最低等级
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler', # 处理日志的核心类
'formatter': 'simple'
},
'file': { # 文件中记录日志
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
# 日志位置,日志文件名,日志保存目录必须手动创建
'filename': BASE_DIR.parent / "logs/uric.log",
# 单个日志文件的最大值,这里我们设置300M
'maxBytes': 300 * 1024 * 1024,
# 备份日志文件的数量,设置最大日志数量为10
'backupCount': 10,
# 日志格式:详细格式
'formatter': 'verbose',
# 设置默认编码,否则打印出来汉字乱码
'encoding': 'utf-8',
},
},
# 日志实例对象
'loggers': {
'django': { # 固定名称,将来django内部也会有异常的处理,只会调用django下标的日志对象
'handlers': ['console', 'file'],
'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统
},
}
}
settings/dev中间件添加log中间件可以参考最后一行
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'uric_api.middlewares.log.LogMiddleware',
]
构建中间件,记录日志消息
from django.utils.deprecation import MiddlewareMixin
import logging
import time
class LogMiddleware(MiddlewareMixin):
start = 0
def process_request(self, request):
self.start = time.time()
def process_response(self, request, response):
cost_timer = time.time() - self.start
logger = logging.getLogger("django")
if cost_timer > 0.5:
logger.warning(f"请求路径: {request.path} 耗时{cost_timer}秒")
return response
可以把视图time.sleep(5)看一下效果
4、异常处理
utils/exception.py
import logging
from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
from django.db import DatabaseError
logger = logging.getLogger("django")
def custom_exception_handler(exc, context):
"""
自定义异常处理
:param exc: 异常类实例对象
:param context: 抛出异常的执行上下文[context,是一个字典格式的数据,里面记录了异常发生时的环境信息]
:return: Response 响应对象
"""
# 先让drf内置的异常处理函数先解决掉它能识别的异常
response = exception_handler(exc, context)
if response is None:
"""drf无法处理的异常"""
view = context["view"]
if isinstance(exc, DatabaseError):
logger.error('[%s] %s' % (view, exc))
response = Response({"errmsg":"服务器内部存储错误"}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
return response
settings/dev.py
REST_FRAMEWORK = {
# 异常处理
'EXCEPTION_HANDLER': 'uric_api.utils.exceptions.custom_exception_handler',
}
视图更改
# 视图更改
class TestAPIView(APIView):
def get(self, request):
from django.db import DatabaseError
raise DatabaseError("mysql连接失败")
return Response({"message": "hello"})
结果视图
日志视图
5、连接数据库
创建数据库
create database uric default charset=utf8mb4; -- utf8也会导致有些极少的中文出现乱码的问题,mysql5.5之后官方才进行处理,出来了utf8mb4,这个是真正的utf8,能够容纳所有的中文。
为数据库创建用户
# mysql8.0版本以上执行
# 创建用户:create user '用户名'@'主机地址' identified by '密码';
create user 'uricUser01'@'%' identified by 'xxx'; # %表示任意主机都可以通过当前账户登录到mysql
# 分配权限:grant 权限选项 on 数据库名.数据表 to '用户名'@'主机地址' with grant option;
grant all privileges on uric.* to 'uricUser'@'%' with grant option;# mysql8.0版本以下执行,创建数据库用户并设置数据库权限给当前新用户,并刷新内存中的权限记录
# create user uric_user identified by 'xxx';
# grant all privileges on uric.* to 'uric_user'@'%';
# flush privileges;
settings.dev文件配置数据库
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"HOST": "127.0.0.1",
"PORT": 3306,
"USER": "uric_user",
"PASSWORD": "xxx",
"NAME": "uric",
}
}
主模块的初始化文件__init__.py
from pymysql import install_as_MySQLdb
install_as_MySQLdb()
初始化库文件命令
python manage.py makemigrations
python manage.py migrate
6、前后端域名配置
后端settings.dev文件
前端vue.config.js
const {defineConfig} = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
host: "localhost",
/* 本地ip地址 */
//host: "192.168.0.131",
// host: "www.urils.cn", //局域网和本地访问
port: "8000",
// hot: true,
/* 自动打开浏览器 */
// open: false,
/*overlay: {
warning: false,
error: true
},*/
/* 跨域代理 */
/*proxy: {
"/api": {
/!* 目标代理服务器地址 *!/
target: "http://xxxx.top", //
/!* 允许跨域 *!/
changeOrigin: true,
ws: true,
pathRewrite: {
"^/api": ""
}
}
}*/
},
})