前后端分离

3 篇文章 0 订阅
2 篇文章 0 订阅

一、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": ""
                }
            }
        }*/
    },
})

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值