创新实训(13)——vue杂项

vuetify布局

应用组件

在 Vuetify 中,v-app组件和 v-navigation-drawerv-app-barv-footer等组件上的 app 属性,帮助你的应用围绕 <v-main> 组件进行适当的大小调整。 这使你可以创建真正独特的界面,无需因管理布局尺寸而烦恼。 所有应用都需要 v-app 组件。 这是许多 Vuetify 组件和功能的挂载点,并确保它将默认的应用 变体 (dark/light)传递给子组件,并确保在浏览器中对某些点击事件的正确跨浏览器支持,如 Safari。 v-app 只应该在你的应用中渲染一次
下面是app.vue的布局设置

<v-app>
    <v-app-bar
        app
        color="primary"
        dark
    >
      <BreadCrumb></BreadCrumb>

      <v-spacer></v-spacer>

      <v-btn
          href=""
          target="_blank"
          text
      >
        <span class="mr-2">Latest Release</span>
        <v-icon>Learn More</v-icon>
      </v-btn>
    </v-app-bar>

    <v-navigation-drawer
        app
    >
      <Aside></Aside>
    </v-navigation-drawer>
    <v-main app>
      <v-container fluid>
        <router-view></router-view>
      </v-container>
    </v-main>
  </v-app>

这是一个 Vuetify 默认应用标记的例子。 只要设置 app 属性,你可以将布局元素放在任何地方。 所有这一切的关键组件是 v-main。 这将根据你指定的应用组件的结构而动态调整大小。 你可以使用上述任何或所有组件的组合,包括 v-bottom-navigation
其中Aside是我自定义的侧边栏组件,BreadCrumb是我自定义的面包屑导航
要想使用自定义组件,需要在<script>标签中引入

import BreadCrumb from './components/BreadCrumb.vue'
import Aside from "./components/Aside";

然后再export default中声明

export default {
  name: 'app',
  components: {
    Aside,
    BreadCrumb
  },
  data: () => ({
    //
  }),
}

详情参见:https://vuetifyjs.com/zh-Hans/components/application/#section-5e9475287ec44ef6

网格系统

Vuetify 配备了一个使用 flexbox 构建的 12 格网格系统。 网格用于在应用的内容中创建特定的布局。 它包含 5 种类型的媒体断点,用于针对特定的屏幕尺寸或方向,xs、sm、md、lg 和 xl。 这些分辨率在视口断点表中定义如下,可以通过自定义断点进行修改。
这是我最欣赏的vuetify的特性,使用网格系统可以快速规划组件布局,避免了繁琐的CSS格式设置

  • v-container
    v-container提供了将你的网站内容居中和水平填充的功能。 你还可以使用 fluid 属性将容器在所有视口和设备尺寸上完全扩展。 保持了之前 1.x 的功能,其中属性作为 v-container上的类进行传递,以使应用辅助类(如 ma-#/pa-#/fill-height)生效。
    可以把v-container放在 <template>标签下
  • v-row
    v-row是 v-col 的容器组件。 它使用 flex 属性来控制其内栏的布局和流。 它使用的是 24px 的标准间隔。 这可以用 dense 属性来减小,或者用 no-gutters 来完全去除。 这是 2.x 版本对 1.x 版本中 v-layout的替代。
    可以理解为v-row规定了一行,这个”行“默认是填充整个页面,且高度没有限制
  • v-col
    v-col 包裹内容,它必须是 v-row 的直接子代。 这是 2.x 版本对 1.x 版本中v-flex的替代。
    v-col 放在v-row标签内,可以使组件实现水平或垂直布局
    v-col内也可以嵌套v-row
    可以使用:cols定义列宽,取值为1到12
<v-col
	:cols="2"
>
  • v-spacer
    v-spacer 是一个基本而又通用的间隔组件,用于分配父子组件之间的剩余宽度。 当一个v-spacer放置在子组件之前或之后时,组件将推到其容器的左右两侧。 当多个组件之间使用多个 v-spacer时,剩余的宽度将均匀地分布在每个 spacer 之间。

路由

添加路由

路由需要在/router/index.js文件中配置
比如引入SolrArticle.vue

const SolrArticle = () => import('../views/Solr/SolrArticle.vue')
const routes = [
  {
    path:'/Solr/SolrArticle',
    name:'SolrArticle',
    component: SolrArticle
  },
]

这种添加路由的方式使用了懒加载,路由被访问的时候才加载对应组件

路由模式

vue有两种路由模式

  • hash
  • history

vue-router 默认hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。这种路由的特点是url内有#
在这里插入图片描述

可以通过以下方式使用history路由

const router = new VueRouter({
  routes,
  mode:'history'
})

但是,如果使用history模式,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问路由界面就会返回 404,这就不好看了。
在我们这个项目中,其实只有 index.html 这一个页面,所有的其它内容都是在这个页面里动态渲染的。当我们直接在后端访问 /login路径时,服务器会后端并没有这个路径对应的内容,所以返回了 Error Page。
为了获取到我们需要的内容,我们要想办法触发前端路由,即在后端添加处理内容,把 通过这个 URL 渲染出的 index.html 返回到浏览器。
在后端项目中新建一个 package 名为 error,新建实现 ErrorPageRegistrar接口的类 ErrorConfig,把默认的错误页面设置为/index.html,代码如下

import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

@Component
public class ErrorConfig implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
        registry.addErrorPages(error404Page);
    }

}

Aside 侧边栏

将侧边栏作为单个组件,将其引入App.vue

在这里插入图片描述
使用v-navigation-drawer以及嵌套v-list即可实现

html:

<template>
  <v-card
      height=100%
      class="mx-auto"
  >
    <v-navigation-drawer>
      <v-list-item>
        <v-list-item-content>
          <v-list-item-title class="title">
            SDUDOC后台管理
          </v-list-item-title>
          <v-list-item-subtitle>
            Solr and DataBase
          </v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>

      <v-divider></v-divider>

      <v-list
          dense
          nav
      >
        <v-list-item
            v-for="item in items"
            :key="item.title"
            :to="item.href"
        >
          <v-list-item-icon>
            <v-icon>{{item.icon}}</v-icon>
          </v-list-item-icon>
          <v-list-item-title>{{item.title}}</v-list-item-title>
        </v-list-item>
        <v-list-group
            prepend-icon="mdi-database"
        >
          <template v-slot:activator>
            <v-list-item-content>
              <v-list-item-title>Data</v-list-item-title>
            </v-list-item-content>
          </template>

          <v-list-group
              v-for="item in dataItems"
              :key="item.title"
              no-action
              sub-group
          >
            <template v-slot:activator>
              <v-list-item-content>
                <v-list-item-title v-text="item.title"></v-list-item-title>
              </v-list-item-content>
            </template>

            <v-list-item
                v-for="child in item.items"
                :key="child.title"
                :to="child.href"
            >
              <v-list-item-content>
                <v-list-item-title v-text="child.title"></v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </v-list-group>

        </v-list-group>
      </v-list>
    </v-navigation-drawer>
  </v-card>
</template>

js:

data: () => ({
    items: [
      {
        title:' Home',
        icon: 'mdi-home',
        href: '/'
      },
      {
        title: 'User Management',
        icon: 'mdi-account-circle',
        href: '/User'
      },
      {
        title: 'Article Management',
        icon: 'mdi-book',
        href: '/Article'
      }
    ],
    dataItems: [
      {
        title: 'MySQL',
        items: [
          {title: 'sms_article_head', href: '/MySQL/SmsArticleHead'}
        ]
      },
      {
        title: 'mongoDB',
        items: [
          {title: 'dms_article', href: '/MongoDB/DmsArticle'},
          {title: 'dms_character', href: '/MongoDB/DmsCharacter'},
          {title: 'dms_word', href: '/MongoDB/DmsWord'}
        ]
      },
      {
        title: 'solr',
        items: [
          {title: 'solr_article_head', href: '/Solr/SolrArticleHead'},
          {title: 'solr_article', href: '/Solr/SolrArticle'},
          {title: 'solr_character', href:'/Solr/SolrCharacter'},
          {title: 'solr_word', href: '/Solr/SolrWord'}
        ]
      }
    ],
  })

BreadCrumb 面包屑导航

在这里插入图片描述
面包屑导航可以放置在组件中,显示当前页面的层级关系
其实现依赖于路由
html:

    <v-breadcrumbs :items="items">
      <template v-slot:item="{ item }">
        <v-breadcrumbs-item>
          {{ item }}
        </v-breadcrumbs-item>
      </template>
    </v-breadcrumbs>

js:

data:()=> ({
      levelList: [],
      items: [],
    }),
    watch: {
      $route() {
        this.getBreadcrumb()
      }
    },
    created(){
      this.getBreadcrumb()
    },
    methods:{
      getBreadcrumb() {
        let matched = this.$route.matched.filter(item => item.name)
        const first = matched[0];
        if (first && first.name.trim().toLocaleLowerCase() !== 'Dashboard'.toLocaleLowerCase()) {
          matched = [].concat(matched)
        }
        this.levelList = matched
        let router = this.levelList[0].path.split('/').slice(1)
        this.items=router
      }
    }

js函数

首先在main.js中引入$axios

Vue.prototype.axios = axios
axios.defaults.baseURL = 'http://loaclhost:8080'
Vue.prototype.$axios = axios

这样在使用axios发请求时就可以省略地址与端口了
为了组件页面的简洁,我把需要axios请求的函数封装到request.js中,并保存到/src/api/request.js
在这里插入图片描述
以solr的查询方法为例:
request.js:

import qs from "qs";
/**
 * 获取 solr 数据
 * @param coreName
 * @param defaultField
 * @param query
 * @param sort
 * @param start
 * @param rows
 * @param fields
 * @returns {Promise<string>}
 */
async function getSolrData(coreName, defaultField, query, sort, start, rows, fields){
    let result = ''
    await this.$axios.post('/solr/query',
        qs.stringify({
            coreName:coreName,
            defaultField:defaultField,
            query:query,
            sort:sort,
            start:start,
            rows:rows,
            fields:fields
        })
    ).then(resp => {
        if (resp) {
            console.log(resp.data)
            result =  resp.data
        }
    })
        .catch(failResponse => {
            console.log(failResponse)
        })

    return result
}
export {
	getSolrData
}

需要在main.js中声明

import {getSolrData} from "./api/request";
Vue.prototype.getSolrData = getSolrData

这样就可以在组件中使用了

async submit () {
      this.loading = true
      await this.getSolrData('dms_article', this.defaultField, this.query, this.sort, this.start, this.rows).then(result => {
        console.log(result)
        this.numFound = result.numFound
        this.articles = result.results
        this.returnNumber = result.results.length
      })
      this.loading = false
    }

asyncawait配合实现了同步操作,因为axios是异步操作,这导致了如果想在执行函数后立刻获取函数返回值的话,假如不用同步操作,返回值会为默认值(因为请求还没有得到返回值js函数就继续执行了),要想得到正确的返回值,需要同步操作。
获取返回值的方法为

await function().then(result => {
	console.log(result)
})

result即为返回值,可以赋给其他变量
使用同步操作还可以实现其他功能,如连续多个axios请求,要求数据按请求顺序返回,如果不使用同步,返回的数据就是随机的,因为网络请求有延迟,函数返回的顺序不能确定。如果使用同步操作,就可以使返回顺序严格按照函数的执行顺序。

后端参数为list的传参问题

后端接受参数为list,前端不能直接传list,会接收不到,报null
解决方法:
前端把list格式化为JSON字符串

JSON.stringify(list)

后端以String接收参数,再用JSON解析

List<String> list = JSONArray.parseArray(str, String.class)

与springboot整合打包部署

打包

vue项目可以与spring boot整合到一起打包

  1. vue输入命令npm run build

  2. 把dist文件夹下的内容全部复制到springboot项目下的resources/static
    在这里插入图片描述
    在这里插入图片描述

  3. 使用maven对springboot项目打包

注意:为了使后端接口可以正常访问,需要修改axios配置

axios.defaults.baseURL = 'http:/'

部署

打包成war包后,部署到相应服务器的tomcat的webapps下
但是,如果使用8080/项目名访问项目的话,因为css和js路径是从根目录读取的,因此无法加载,显示404错误
为了能够正常加载css和js,需要让8080端口默认加载本项目
修改tomcat/conf/server.xml
<Host>标签下加入新标签:

<Context path="" docBase="项目名" reloadable="true" ></Context>

这样每次新部署后,第一次加载仍然是tomcat默认的欢迎界面,但重启一次tomcat服务器就能正常加载项目了

请求拦截器

token请求头

登陆后,后端会返回给前端一个token,包含了权限信息,后续接口需要token才能访问,如果没有token或token过期、权限不正确等,就无法访问接口

axios设置请求头

登陆后,把token保存为localStorage,在main.js中,设置http request拦截器

axios.interceptors.request.use(function (config) {
    let token = window.localStorage.getItem("token")
    if (token) {
        config.headers.token = token;
        console.log(config.headers)
    }
    return config;
}, function (error) {
    // Do something with request error
    return Promise.reject(error);
});

这样,每次发请求时,都会在headers中加上token,后端就可以进行权限认证了

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来看看小兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值