zoukankan      html  css  js  c++  java
  • Hippo登录展示功能

    1. 后台实现登录

    1.1 Xadmin

    1.11安装

    https://github.com/sshwsfc/xadmin.git   #django2
    https://github.com/zgljl2012/xadmin-x      #django3
    pip install  xadmin-x 
    

    在配置文件中注册如下应用

    INSTALLED_APPS = [
        'xadmin',
        'crispy_forms',
        'reversion',
    ]
    
    # 修改使用中文界面
    LANGUAGE_CODE = 'zh-Hans'
    
    # 修改时区
    TIME_ZONE = 'Asia/Shanghai'
    USE_TZ = False
    

    xadmin有建立自己的数据库模型类,需要进行数据库迁移

    python manage.py makemigrations
    python manage.py migrate
    

    在总路由中添加xadmin的路由信息

    import xadmin
    xadmin.autodiscover()
    
    # version模块自动注册需要版本控制的 Model
    from xadmin.plugins import xversion
    xversion.register_models()
    
    urlpatterns = [
        path(r'xadmin/', xadmin.site.urls),
    ]
    

    如果之前没有创建超级用户,需要创建,如果有了,则可以直接使用之前的。

    python manage.py createsuperuser
    

    1.11 给admin配置基本站点

    在当前子应用中创建adminx.py,添加如下代码

    import xadmin
    from xadmin import views
    
    class BaseSetting(object):
        """xadmin的基本配置"""
        enable_themes = True  # 开启主题切换功能
        use_bootswatch = True
    
    xadmin.site.register(views.BaseAdminView, BaseSetting)
    
    class GlobalSettings(object):
        """xadmin的全局配置"""
        site_title = "hippo"  # 设置站点标题
        site_footer = "@追梦nan-2020"  # 设置站点的页脚
        menu_style = "accordion"  # 设置菜单折叠
    
    xadmin.site.register(views.CommAdminView, GlobalSettings)
    

    1.12 注册模型到xadmin中

    在xadmin中配置信息

    # 轮播图
    from .models import BannerInfo
    class BannerInfoModelAdmin(object):
        list_display=["name","orders","is_show"]
    xadmin.site.register(BannerInfo, BannerInfoModelAdmin)
    
    list_display     #控制列表展示的字段
    search_fields    # 控制可以通过搜索框搜索的字段名称,xadmin使用的是模糊查询
    list_filter      #可以进行过滤操作的列,对于分类、性别、状态
    ordering         #默认排序的字段 
    show_detail_fields  #在列表页提供快速显示详情信息
    list_editable    #在列表页可以快速直接编辑的字段
    refresh_times    #指定列表页的定时刷新
    list_export      #控制列表页导出数据的可选格式
    show_bookmarks   #控制是否显示书签功能
    data_charts      #控制显示图表的样式
    data_charts = {  #控制显示图表的样式
            "order_amount": {          #随便写的名称order_amount
              'title': '图书发布日期表',  #控制图标名称
              "x-field": "bpub_date",  #控制x轴字段
              "y-field": ('btitle',),  #控制y轴字段,可以是多个值
              "order": ('id',),        # 控制默认排序
              
            },
    model_icon      #控制菜单的图标
    readonly_fields #在编辑页面的只读字段
    exclude        # 在编辑页面隐藏的字段
    

    1.13 修改xadmin中子应用名

    apps.py

    class HomeConfig(AppConfig):
        name = 'home'
        verbose_name = '我的首页'
    

    __init__.py

    default_app_config = "home.apps.HomeConfig"
    

    1.2 Django_Auth

    Django默认已经提供了认证系统。认证系统包含:

    - 用户管理
    - 权限
    - 用户组
    - 密码哈希系统
    - 用户登录或内容显示的表单和视图
    - 一个可插拔的后台系统
    

    Django默认用户的认证机制依赖Session机制,我们在项目中将引入JWT认证机制,将用户的身份凭据存放在Token中,然后对接Django的认证系统,帮助我们来实现:

    • 用户的数据模型
    • 用户密码的加密与验证
    • 用户的权限系统

    Django用户模型类

    Django认证系统中提供了用户模型类User保存用户的数据,默认的User包含以下常见的基本字段:

    字段名 字段描述
    username 必选。150个字符以内。 用户名可能包含字母数字,_@+ .-个字符。
    first_name 可选(blank=True)。 少于等于30个字符。
    last_name 可选(blank=True)。 少于等于30个字符。
    email 可选(blank=True)。 邮箱地址。
    password 必选。 密码的哈希加密串。 (Django 不保存原始密码)。 原始密码可以无限长而且可以包含任意字符。
    groups Group 之间的多对多关系。
    user_permissions Permission 之间的多对多关系。
    is_staff 布尔值。 设置用户是否可以访问Admin 站点。
    is_active 布尔值。 指示用户的账号是否激活。 它不是用来控制用户是否能够登录,而是描述一种帐号的使用状态。
    is_superuser 是否是超级用户。超级用户具有所有权限。
    last_login 用户最后一次登录的时间。
    date_joined 账户创建的时间。 当账号创建时,默认设置为当前的date/time。

    常用方法:

    • set_password(raw_password)

      设置用户的密码为给定的原始字符串,并负责密码的。 不会保存User 对象。当Noneraw_password 时,密码将设置为一个不可用的密码。

    • check_password(raw_password)

      如果给定的raw_password是用户的真实密码,则返回True,可以在校验用户密码时使用。

    管理器方法:

    管理器方法即可以通过User.objects. 进行调用的方法。

    • create_user(username, email=None, password=None, ***extra_fields*)

      创建、保存并返回一个User对象。

    • create_superuser(username, email, password, ***extra_fields*)

      create_user() 相同,但是设置is_staffis_superuserTrue

    1.3 创建User应用

    python ../../manage.py startapp users
    

    settings.py文件中注册子应用。

    # 新增一个系统导包路径
    import sys
    sys.path.insert(0,os.path.join(BASE_DIR,"apps"))
    
    INSTALLED_APPS = [
    		...
      	'users',
    ]
    

    新增导包路径pycahrm会变黄 将pycahrm设置为源根

    1.4 JWT

    安装

    pip install djangorestframework-jwt
    

    配置

    REST_FRAMEWORK = {
        # 异常处理
        'EXCEPTION_HANDLER': 'pippo_api.utils.exceptions.custom_exception_handler',
        #jwt
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.BasicAuthentication',
        ),
    }
    import datetime
    #jwt有效时间
    JWT_AUTH = {
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    }
    

    在用户注册或登录成功后,在序列化器中返回用户信息以后同时返回token即可。

    1.5 后端登陆认证

    Django REST framework JWT提供了登录获取token的视图,可以直接使用

    在子应用路由urls.py中

    from rest_framework_jwt.views import obtain_jwt_token
    from django.urls import path
    
    urlpatterns = [
        path('login/', obtain_jwt_token),
    ]
    

    在主路由中,引入当前子应用的路由文件

    urlpatterns = [
    		...
        path('users/', include("users.urls")),
        # include 的值必须是 模块名.urls 格式,字符串中间只能出现一个圆点
    ]
    

    接下来,我们可以通过postman来测试下功能

    1.6 实时刷新token

    user中引入refresh_jwt_token

    from rest_framework_jwt.views import obtain_jwt_token,refresh_jwt_token
    from django.urls import path
    
    urlpatterns = [
        path('login/', obtain_jwt_token),
        path('verify/', refresh_jwt_token), #校监并刷新token
    ]
    

    修改settings.py配置文件

    # JWT
    JWT_AUTH = {
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
        'JWT_ALLOW_REFRESH': True,  #这个参数要改True,才能刷新token
    }
    

    2. 前端显示页面

    1. 添加拦截器

    实现每次请求到后端都携带token

    新建axios.js文件

    import axios from 'axios'
    
    // 配置默认的host,假如你的API host是:http://api.htmlx.club
    axios.defaults.baseURL = 'http://127.0.0.1:8000'
    
    // 添加请求拦截器
    axios.interceptors.request.use(config=> {
      // 在发送请求之前做些什么
        if(localStorage.token){
            config.headers.Authorization =localStorage.token
        }
      return config
    }, error =>  {
      // 对请求错误做些什么
    return Promise.reject(error)
    });
    
    

    在main.js中引入

    import './axios'
    

    2. 导航守卫

    实现每个页面是否携带token,如果存在,那刷新token反之跳转到登录页面

    在路由index.js中追加

    router.beforeEach((to, from, next) => {
        let token = localStorage.token;
        if (to.path === '/') {
            next()
    
        } else {
    
            if (token) {
                axios.post('http://127.0.0.1:8000/users/verify/', {
                    token: token,
                }).then((res) => {
                    //更新本地的token
                    localStorage.token = res.data.token;
                }).catch((error) => {
                    //刷新token失败或者token失效
                    next('/')
                })
                next()
            } else {
                next('/')
            }
        }
    })
    

    3. 登录页组件

    Login.vue 样式

    <template>
      <div id="box">
        <img src="../assets/login/login.png" alt="">
        <div class="login_box">
          <div class="login-title">
            <p>儒风若梦!</p>
          </div>
          <div class="login-container">
            <div class="title">
              <span>登录</span>
            </div>
            <div class="inp">
              <a-form id="components-form-demo-normal-login" :form="form" @submit="handleSubmit">
                <a-form-item>
                  <a-input
                      v-decorator="['username',{ rules: [{ required: true, message: '请输入用户名!' }] },]"
                      placeholder="用户名">
                    <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)"/>
                  </a-input>
                </a-form-item>
                <a-form-item>
                  <a-input v-decorator="['password', { rules: [{ required: true, message: '请输入密码!' }] },]"
                           type="password"
                           placeholder="密码">
                    <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)"/>
                  </a-input>
                </a-form-item>
                <a-form-item>
                  <a-button type="primary" html-type="submit" class="login-form-button">登录</a-button>
                </a-form-item>
              </a-form>
            </div>
          </div>
    
    
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "Login",
      beforeCreate() {
        this.form = this.$form.createForm(this, {name: 'normal_login'});
      },
      methods: {
        handleSubmit(e) {
          e.preventDefault();
          this.form.validateFields((err, values) => {
            if (!err) {
              // console.log('Received values of form: ', values);
              //发送ajax请求
              this.axios.post('/users/login/', {
                username: values.username,
                password: values.password
                //获取数据并保存token
              }).then(res => {
                localStorage.token = res.data.token;
                //拿到数据跳转到指定页面
                this.$router.push('/hippo')
              }).catch(error=>{
                this.$message.error('用户名或者密码不对')
              })
    
                // 首页加载时验证token有效性
    
            }
          });
        },
      },
    }
    </script>
    
    <style scoped>
    body {
      margin: 0;
    }
    
    #box {
       100%;
      height: 100%;
      position: relative;
      overflow: hidden;
    }
    
    #box img {
       100%;
      height: 100%;
    }
    
    .login_box {
      position: absolute;
       600px;
      height: 400px;
      left: 0;
      margin: auto;
      right: 0;
      bottom: 0;
      top: -338px;
    }
    
    
    .login-title {
       100%;
      text-align: center;
    }
    
    .login-title p {
      font-family: PingFangSC-Regular;
      font-size: 20px;
      color: #fff;
      letter-spacing: .29px;
      padding-top: 10px;
      padding-bottom: 50px;
    }
    
    .login-container {
       550px;
      height: auto;
      background: rgba(255, 255, 255, 0.3);
      box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .5);
      border-radius: 4px;
      margin: 0 auto;
      padding-bottom: 40px;
    }
    
    .title {
      font-size: 22px;
      color: #9b9b9b;
      border-bottom: 1px solid #e6e6e6;
      display: flex;
      justify-content: space-around;
      padding: 20px 60px 10px 60px;
      margin-bottom: 20px;
    }
    
    .title span {
      color: #4a4a4a;
    }
    
    .inp {
       450px;
      margin: auto;
    }
    
    
    #components-form-demo-normal-login .login-form-button {
       100%;
    }
    
    
    </style>
    

    绑定路由

    import Login from '@/views/Login'
    {
            path: '/',
            component: Login
        },
    

    4. 导航栏

    使用了路由嵌套。

    官方文档 https://router.vuejs.org/zh/guide/essentials/nested-routes.html

    hippo.vue

    <template>
      <a-layout id="components-layout-demo-custom-trigger" style="min-height: 100vh">
        <a-layout-sider v-model="collapsed" :trigger="null" collapsible>
          <!--      <div class="logo" />-->
          <div class="logo" style="color:#fff;font-size: 18px;text-align: center;">
            儒风
          </div>
          <a-menu theme="dark" :default-selected-keys="['1']" mode="inline">
            <template v-for="v in menu_list">
    
              <a-menu-item :key='v.id' v-if="v.children.length==0">
                <!--            路由指向 点击标签进行跳转到指定的url-->
                <router-link :to="v.menu_url">
                  <a-icon :type="v.icon"/>
                  <span>{{ v.title }}</span>
                </router-link>
              </a-menu-item>
              <a-sub-menu :key="v.id" v-else>
                <span slot="title"><a-icon :type="v.icon"/><span>{{ v.title }}</span></span>
                <a-menu-item :key="c.id" v-for="c in v.children">
                  {{ c.title }}
                </a-menu-item>
              </a-sub-menu>
            </template>
    
          </a-menu>
    
        </a-layout-sider>
        <a-layout>
          <a-layout-header style="background: #fff; padding: 0">
            <a-icon
                class="trigger"
                :type="collapsed ? 'menu-unfold' : 'menu-fold'"
                @click="() => (collapsed = !collapsed)"
            />
          </a-layout-header>
          <a-layout-content :style="{ margin: '24px 16px', padding: '5px', minHeight: '280px' }">
            <router-view></router-view>
          </a-layout-content>
          <a-layout-footer style="text-align: center">
            Ant Design ©2018 Created by Ant UED
          </a-layout-footer>
        </a-layout>
      </a-layout>
    </template>
    <script>
    export default {
      name:"Hippo",
      data() {
        return {
          collapsed: false,
          menu_list: [
            {id: 1, icon: 'desktop', title: '控制中心', tube: '', 'menu_url': '/hippo/', children: []},
            {
              id: 2, icon: 'container', title: '主机管理', 'menu_url': '/hippo/host/', children: []
            },
            {
              id: 3, icon: 'bold', title: '批量执行', tube: '', 'menu_url': '/hippo/workbench', children: [
                {id: 10, title: '执行任务', 'menu_url': '/hippo/host/'},
                {id: 11, title: '模版管理', 'menu_url': '/hippo/host/'},
              ]
            },
            {
    
              id: 4, icon: 'flag', title: '代码发布', tube: '', 'menu_url': '/hippo/workbench', children: [
                {id: 12, title: '应用管理', 'menu_url': '/hippo/release'},
                {id: 13, title: '发布申请', 'menu_url': '/hippo/release'},
              ]
            },
            {id: 5, icon: 'schedule', title: '定时计划', tube: '', 'menu_url': '/hippo/workbench', children: []},
            {
              id: 6, icon: 'deployment-unit', title: '配置中心', tube: '', 'menu_url': '/hippo/workbench', children: [
                {id: 14, title: '环境管理', 'menu_url': '/hippo/environment'},
                {id: 15, title: '服务配置', 'menu_url': '/hippo/workbench'},
                {id: 16, title: '应用配置', 'menu_url': '/hippo/workbench'},
              ]
            },
            {id: 7, icon: 'radar-chart', title: '监控中心', tube: '', 'menu_url': '/hippo/workbench', children: []},
            {
              id: 8, icon: 'alert', title: '报警中心', tube: '', 'menu_url': '/hippo/workbench', children: [
                {id: 17, title: '报警历史', 'menu_url': '/hippo/workbench'},
                {id: 18, title: '报警联系人', 'menu_url': '/hippo/workbench'},
                {id: 19, title: '报警联系组', 'menu_url': '/hippo/workbench'},
              ]
            },
            {
              id: 9, icon: 'setting', title: '系统管理', tube: '', 'menu_url': '/hippo/workbench', children: [
                {id: 20, title: '账户管理', tube: '', 'menu_url': '/hippo/workbench'},
                {id: 21, title: '角色管理', tube: '', 'menu_url': '/hippo/workbench'},
                {id: 22, title: '系统设置', tube: '', 'menu_url': '/hippo/workbench'},
              ]
            },
          ],
        };
      },
    };
    </script>
    <style>
    #components-layout-demo-custom-trigger .trigger {
      font-size: 18px;
      line-height: 64px;
      padding: 0 24px;
      cursor: pointer;
      transition: color 0.3s;
    }
    
    #components-layout-demo-custom-trigger .trigger:hover {
      color: #1890ff;
    }
    
    #components-layout-demo-custom-trigger .logo {
      height: 32px;
      background: rgba(255, 255, 255, 0.2);
      margin: 16px;
    }
    </style>
    
    

    5. 仪表盘

    home.vue

    <template>
      <div class="out">
        <div class="top">
          <!--      栅格-->
          <a-row :gutter="[16,16]">
            <a-col :span="6">
              <!--          卡片-->
              <a-card>
                <!--            js小手-->
                <a href="javascript:void(0);">
                  <!--              统计-->
                  <a-statistic title="应用" valueStyle="color:green;" style="margin-right: 50px" :value="4" suffix="个">
    
                  </a-statistic>
                </a>
    
              </a-card>
            </a-col>
            <a-col :span="6">
              <a-card>
                <a href="javascript:void(0);">
                  <a-statistic title="主机" valueStyle="color:green;" style="margin-right: 50px" :value="1" suffix="个">
                  </a-statistic>
                </a>
    
              </a-card>
            </a-col>
            <a-col :span="6">
              <a-card>
                <a href="javascript:void(0);">
                  <a-statistic title="任务" valueStyle="color:green;" style="margin-right: 50px" :value="2" suffix="个">
    
                  </a-statistic>
                </a>
    
              </a-card>
            </a-col>
            <a-col :span="6">
              <a-card>
                <a href="javascript:void(0);">
                  <a-statistic title="监控" valueStyle="color:green;" style="margin-right: 50px" :value="2" suffix="个">
    
                  </a-statistic>
                </a>
    
              </a-card>
            </a-col>
          </a-row>
        </div>
        <div class="center" style="margin-top: 30px;">
          <!-- 别忘了给这个标签设置高度,不然看不到效果 -->
          <a-card title="报警趋势">
            <!--        级联标签-->
            <a-cascader placeholder="过滤监控项,默认所有" slot="extra" :options="options" change-on-select @change="onChange"
                        style="260px;"/>
            <div>
    
              <div id="main" style="height: 400px;92%;" ref="chart">
                <!--            报警趋势-->
    
              </div>
            </div>
    
    
          </a-card>
    
    
        </div>
        <div class="down" style="margin-top: 30px">
          <a-row>
            <a-col :span="14">
              <a-card title="发布申请Top10">
                <a-button type="link" slot="extra">
                  今日
                </a-button>
                <a-button type="link" slot="extra">
                  本周
                </a-button>
                <a-button type="link" slot="extra">
                  本月
                </a-button>
                <a-range-picker @change="onDateChange" slot="extra" style=" 200px;"/>
    
                <div id="pub" style="height: 300px;94%;" ref="pub">
                  <!--                 柱状图-->
                </div>
              </a-card>
            </a-col>
            <a-col :span="9" :offset="1">
              <a-card title="最近30天登录">
    
                <a-list :data-source="login_list" style="overflow: auto;height: 308px">
                  <a-list-item slot="renderItem" slot-scope="item, index">
                    {{ item }}
                  </a-list-item>
    
                </a-list>
              </a-card>
            </a-col>
          </a-row>
        </div>
    
    
      </div>
    </template>
    <script>
    import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN';
    
    export default {
      name: 'Home',
      data() {
        return {
          locale: zhCN,
          login_list: [
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
            '11-18 13:12:02 管理员 通过 1.1.1.1 登录',
          ],
          options: [
            {
              value: 'test',
              label: '站点检测',
              children: [
                {
                  value: '1',
                  label: 'elatic',
    
                },
                {
                  value: '2',
                  label: 'www.baidu.com',
    
                },
                {
                  value: '3',
                  label: 'ssh端口',
    
                },
                {
                  value: '4',
                  label: 'spug官网',
    
                },
              ],
            },
            {
              value: 'jiangsu',
              label: '端口检测',
              children: [
                {
                  value: 'nanjing',
                  label: 'ssh端口',
    
                },
              ],
            },
            {
              value: 'jiangsu2',
              label: '进程检测',
              children: [
                {
                  value: 'nanjing',
                  label: 'aaa',
    
                },
              ],
            },
            {
              value: 'jiangsu3',
              label: '自定义脚本',
              children: [
                {
                  value: 'nanjing',
                  label: 'top',
    
                },
              ],
            },
          ],
        };
      },
      methods: {
        onChange(value) {
          console.log(value);
        },
        onDateChange(date, dateString) {
          console.log(date, dateString);
        },
    
    
      },
      created() {
    
        this.$nextTick(() => {
              //console.log(this.$refs);
              // data: ['2019-10-10', '2019-10-11', '2019-10-12', '2019-10-13', '2019-10-14', '2019-10-15', '2019-10-16']
              // 报警图表
              var myChart = this.$echarts.init(this.$refs.chart);
              var alert_option = {
                //
                tooltip: {
                  trigger: 'axis'
                },
                color: 'blue',
                grid: {
                  left: '3%',
                  right: '8%',  // 控制距离左右上下的边距
                  bottom: '3%',
                  containLabel: true // 完整显示,自动缩放
                },
    
                xAxis: {
                  type: 'category',
                  boundaryGap: false,
                  data: ['2019-10-10', '2019-10-11', '2019-10-12', '2019-10-13', '2019-10-14', '2019-10-15', '2019-10-16',]
                },
                yAxis: {
                  type: 'value'
                },
                series: [
                  {
                    name: '报警次数',
                    type: 'line',
                    stack: '总量',
                    data: [0, 1, 0, 2, 1, 0, 1],
                    smooth: true,
                  },
    
                ]
              };
    
              // myChart.setOption(alert_option);
              // window.onresize = myChart.resize
              //
              // 发布申请柱状图
              var myChart2 = this.$echarts.init(this.$refs.pub);
              var pub_option = {
                color: ['#3398DB'],
                tooltip: {
                  trigger: 'axis',
                  axisPointer: {            // 坐标轴指示器,坐标轴触发有效
                    type: 'shadow'        // 默认为直线,可选为:'line' | 'shadow'
                  }
                },
                grid: {
                  left: '3%',
                  right: '4%',
                  bottom: '3%',
                  containLabel: true
                },
                xAxis: [
                  {
                    type: 'category',
                    data: ['订单服务', 'test11', '系统管理', 'test'],
                    axisTick: {
                      alignWithLabel: true
                    }
                  }
                ],
                yAxis: [
                  {
                    type: 'value'
                  }
                ],
                series: [
                  {
                    name: '直接访问',
                    type: 'bar',
                    barWidth: '60%',
                    data: [10, 52, 200, 334]
                  }
                ]
              };
              myChart.setOption(alert_option);
              myChart2.setOption(pub_option);
              window.addEventListener('resize', function () {
                myChart.resize();
                myChart2.resize();
              })
    
    
            }
        )
        // 注意:直接通过this.$refs.chart获取该标签不生效,因为vue的标签加载是异步的,可能标签加载还没完成就过去获取这个标签是拿不到的,所以我们需要延迟回调方法来等vue的dom更新完成之后,再获取标签
        // console.log('>>>>', this.$refs, typeof this.$refs.chart)
        // var echarts = require('echarts');
    
    
      }
    
    };
    </script>
    
    

    配置路由

        {
            path: '/hippo',
            component: Hippo,
            children:[
                {
                    path: '',
                    component: Home,
                }
            ]
    
        },
    

    访问http://localhost:8080/hippo

    热爱技术,享受生活,感谢推荐!
  • 相关阅读:
    flutter-布局(水平和垂直)
    flutter-GridView(网格列表)
    flutter-ListView(列表组件)
    flutter-图片组件(Image)的使用
    flutter-container-容器
    flutter-hello flutter 并且修改字体
    nginx
    初步学习next.js-7-打包
    初步学习next.js-6-使用antd
    初步学习next.js-5-编写css样式和lazyloading
  • 原文地址:https://www.cnblogs.com/zdqc/p/14302784.html
Copyright © 2011-2022 走看看