从壹开始 [ Nuxt.js ] 之二 || 项目搭建 与 接口API

前言

哈喽大家周一好,今天的内容比较多,主要就是包括:把前端页面的展示页给搭出来,然后调通接口API,可以添加数据,这两天我也一直在开发,本来想一篇一篇的写,发现可能会比较简单,就索性把项目搭建的过程简化,一次写好了,在开发 Nuxt.js 框架的过程中,我发现相比之前还是有些变化的,如果你是读过我第一个前后端分离系列的小伙伴,可能看过我简单说过Nuxt框架的一些知识《Nuxt.js 是什么?》,我发现现在在项目搭建的过程中,已经优化了一些功能,今天就重头搭建一下。

如果你没有看过之前的文章,可以简单看看,主要是看看理论方面,如果是只想看搭建过程,直接看这个即可了,毕竟现在已经是最新的了,老的方案可以舍弃,不过个人建议,前边那几篇可以花几分钟看看,了解下,比如:什么是SSR渲染,为啥要使用它,是如何进行渲染的等等,毕竟很多基础概念我这里不会再说了,因为这是一个只有四篇的小系列,在上个系列都会有说到,这里就不多叙述了,直接进行项目的搭建。

今天是从简到繁的过程,并不会把每个页面的说明都写上,因为页面是千篇一律的,项目设计只写这一篇吧,下一篇重点说如何服务端部署。现在已经基本完成了(因为我还没有来得及发布,如果你想现在查看具体的效果,可以下载下来运行即可,注意要配置咱们的Blog.Core 项目,也可以关注的 Github 地址 https://github.com/anjoy8/Nuxt.tBug,这两天我会发布一版本,在线地址:http://123.206.33.109:7090/):

image

一、Nuxt项目搭建

1、通过脚手架工具初始化项目

这里我们就直接使用了官方脚手架工具创建,当然你也可以一步一步的创建,手动创建比较麻烦,不适合新手,想了解更多,查看官网:新手模板

这里默认你的电脑上已经安装了 npm 环境了,如果不确定是否安装了,可以查使用命令查看 npm -v

image

不仅如此,如果你想使用官方脚手架搭建项目,要确保npm版本在 5.2+ ,目前已经是6.5.0了,我的本地是 5.6.0 ,符合要求,开始动手搭建。

首先你找好一个放置你项目的文件夹作为你的仓库,然后通过CMD或者 PowerShell 来创建项目,很简单 :

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">npx create-nuxt-app 项目名</pre>

在安装的过程中,会出现几个选项,这里先放上我的动图,具体如何选,图下我会解释:

image

具体的选择是这样的:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">Project name TiBug.Nuxt //项目名称
Project description //项目描述
Use a custom server framework //选择通用的服务端渲染模板框架:none,是默认服务 Nuxt default serve
Use a custom UI framework //选择通用UI框架:Element
Choose rendering mode //选择渲染模式:SSR或SPA
Use axios module //是否使用 axios 进行http请求:yes
Use eslint //是否使用代码格式纠错: no,会各种格式警告
Use prettier //是否代码美化:yes
Author name //作者名
Choose a package //选择一个包管理:npm</pre>

你可以根据情况自己去选择,比如你不喜欢用 nuxt 自带的服务器渲染,想使用 Express ,比如你可以直接在这里就选择好你喜欢的样式,比如我就喜欢 ElementUI 样式框架。这相对以前的版本,已经有很大的改观了,我认为很棒的。

这样我们就等待安装成功了,中间可能大概需要2~5分钟,看个人网络而定。

2、项目整体目录结构

安装好后,我们会看到生成的全部文件夹,而且是已经安装好依赖包的:

image

如果你稍微有一些 Vue 的开发经验,应该都能知道大概的文件夹包含的意思,比如Component,Pages,Store等,咱们具体说说每一个文件夹都是干啥的:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">├── assets // 资源文件。用于组织未编译的静态资源入LESS、SASS 或 JavaScript
│ └── logo.jpg // 默认logo图片
├── components // 组件。用于自己编写的Vue组件,比如滚动组件,日历组件,分页组件
│ └── AppLogo.vue // 默认logo组件
├── layouts // 布局。页面都需要有一个布局,默认为 default。它规定了一个页面如何布局页面。所有页面都会加载在布局页面中的 <nuxt /> 标签中。 │ └── default.vue // 默认模板页面,类似mvc中的layout
├── middleware // 中间件。存放中间件。可以在页面中调用: middleware: 'middlewareName' 。
├── pages   // 页面。一个 vue 文件即为一个页面。
│ └── index.vue // 默认首页面
├── plugins // 用于存放JavaScript插件的地方
│ └── element-ui.js // 上边我们安装的UI框架
├── static // 用于存放静态资源文件,比如图片,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下。
├── store   // 用于组织应用的Vuex 状态管理。
├── .editorconfig // 开发工具格式配置
├── .eslintrc.js // ESLint的配置文件,用于检查代码格式
├── .gitignore // 配置git不上传的文件
├── nuxt.config.js // 用于组织Nuxt.js应用的个性化配置,比如网站title,已便覆盖默认配置
├── package.json // npm包管理配置文件
└── README.md // 说明文档</pre>

其实整理看起来,和之前的没多少差别,至少在页面结构上没太大差别,主要更新是把项目搭建前的配置交给了开发者了,不过有一个地方是被去掉了的,你先和 Vue 结构对比下,看看少了一个什么配置

3、路由根据页面定

大家看上边的文档架构层次图,你可能会发现,nuxt 和 vue 还是有些不一样的,这里没有了 **路由Route **的配置,发现了么?

这个就是 nuxt 框架的独到之处,为了能实现更好的SSR渲染,它使用的是根据页面结构,自动路由,所以你的文件名,就是你的路由名称:

在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。

以下目录结构:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">pages/
--| _slug // 以下划线开头的文件夹,代表参数
-----| comments.vue -----| index.vue --| users/
-----| _id.vue //以 下划线 开头的页面,动态路由
--| index.vue</pre>

Nuxt.js 生成对应的路由配置表为:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue' },
{
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue' },
{
name: 'slug',
path: '/:slug',
component: 'pages/_slug/index.vue' },
{
name: 'slug-comments',
path: '/:slug/comments',
component: 'pages/_slug/comments.vue' }
]
}</pre>

具体的请查看官网:路 由

4、项目加载欢迎页面

我在webstorm 中运行上边搭建好的项目,可以看到初始欢迎页:

image

下边我们就正式对这个页面进行完善,进行整体设计。

二、首页结构布局

下边我就简要的写了,具体的代码,请看我的 Github 地址就行。我这里先把 pages/index.vue 的默认内容清空,

1、创建布局模板

这个时候你可以先练习练习,布局页面在哪里配置?先停下来想一想,或者看看刚刚创建的文件夹,没错,就是 layouts 文件夹下的 default.vue 页面,咱们看看这个初始的是什么样子:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"><template>
<div>
<nuxt/> //这个就是页面入口,其他页面就是在这里进行嵌入的,可以对比MVC中 layout.cshtml 中的 @RenderBody()
</div>
</template>

<style>
</style></pre>

我们直接对其进行改造,设计模板结构,

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"><template>
<div id="app">
<el-row :gutter="0" >
<el-col class="nav">
<el-col class="nav-bar" :sx="22" :sm="22" :md="22" :lg="16">
<div class="nav-bar-body">
<div class="nav-bar-inner">
<div id="logo">
<img
src="../assets/logoa.png" />
</div>
</div>
............
</el-row>
<div class="footer">京ICP备xxxxxxxx号</div>
</div>
</template></pre>

因为我已经在安装的时候,选择了ElementUI 框架,所以可以直接使用相关 el 组件,而不用在各种配置和引用了,你一定会问:之前在开发 Vue 的时候,也使用过 ElementUI 呀,不是应该先安装,然后在 Mian.js 中引用么,可是你为啥直接就可以用呢?能提这个问题的,表示真的是已经看懂了,别着急,请往下看。

2、插件是如何运行的

首先,我们应该清楚插件是什么?

插件其实就是一个 .js 文件,将我们需要的组件注入到我们的项目中去,可拔插,很方便替换任何一个我们需要的。

其实,我们应该了解插件是在哪里定义:

(同样,你这里也可以先停一停,自己去文件夹里找找,感觉哪一个像是定义插件的),在脚手架整体结构中,我们知道每一个文件夹都有自己的作用,其中 plugins 文件夹,就是用来存放我们的插件的,插件目录 plugins 用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。说人话就是,我们的vue项目想要运行,必须实例化 vue,这个大家肯定都知道,那实例化之前,当然就必须要加载插件了,就是这个目的。

我们在上边用官方脚手架搭建的时候,已经安装了 elementUI 的插件,所以我们会在 plugins 文件夹里,看到这个 elemengt-ui.js 文件:

image

以后我们如果还需要建立其他组件的话,都在这里处理,比如以后我们会对axios请求访问进行封装插件,如果你不是很明白,这里举个栗子:

如果你是一直看我的文章,一定知道我在第一个系列的 Vue 项目里,为了进行 axios 请求,创建了一个 http.js 的问题,这个其实就是一个插件:

image

我定义好以后,并在 main.js 中注册引入,这样在其他页面内,就可以随心使用了。

最后,我们应该知道如何注册这些插件:

这个时候,就用到我们的配置文件: nuxt.config.js 了,这里边就是我们整个项目的配置文件,比如 head 中的 title 、icon呀,还有css,还有跨域本地代理,最后,当然就是我们的插件注册了:

image

这个时候,我们重新运行下我们的项目(注意:如果修改了nuxt.config.js,要重新编译下),这个时候我们看下效果:

image
image

嗯,很正常的显示了,并且已经支持了SSR渲染,感觉很开心,就是样式太丑,那我们就需要添加进一个样式文件,在根目录下新建 style 文件夹,并添加 style.css,具体的直接从 Github 拉取即可,不过,不仅仅要添加这个文件,还要在配置文件中,进行注册引用,

image

页面 Banner 最终的效果是这样的:

image

我们页面的模板已经加载好了,并且也 mock 了专题的数据,剩下的就是首页结构了。

3、首页正文结构布局

这里因为和 vue 中开发组件或页面是一样的,就不多说了,具体的代码可以参考我的 Github 代码,这里把结构说一下:

image

其中最重要的,就是 asyncData 异步数据加载,这个和 vue 还是不同的:

asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。 在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用 asyncData方法来获取数据,Nuxt.js 会将 asyncData 返回的数据融合组件 data 方法返回的数据一并返回给当前组件。

4、异步数据加载

是在页面加载之前,加载数据,然后融合着 data ,一起返回,最后渲染页面。注意:由于asyncData方法是在组件 初始化 前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。

我这里通过编译后的代码,可以看到是异步请求数据是这样的,下文中,我会配合着 axios 异步请求,一起说说:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> data: function data() { return {
data: 0,
};
},
asyncData: function () { var _asyncData = _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_2___default()( /#PURE/ regeneratorRuntime.mark(function _callee(_ref) { var params, tag, _ref2, data; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: params = _ref.params;
_context.prev = 1;
tag = params.tag;
context.next = 5;//axios http数据请求 return plugins_axios__WEBPACK_IMPORTED_MODULE_3["default"].get("/api/TopicDetail?page=1&tname=".concat(tag)); case 5:
_ref2 = _context.sent;
data = _ref2.data.data;
console.log(data);
debugger; return _context.abrupt("return", {
tagList: data.data,
tagtitle: tag,
fadetitle: true,
notfound: !data.data.length
}); case 12:
_context.prev = 12;
_context.t0 = _context"catch"; case 14: case "end": return _context.stop();
}
}
}, _callee, this, [[1, 12]]);
}));

function asyncData(_x) { return _asyncData.apply(this, arguments);
} return asyncData;

}(),
components: {
ArticleList: components_ArticleList__WEBPACK_IMPORTED_MODULE_4_["default"]
},</pre>

三、其他页面说明

除了上边的首页以外,还有详情页,分类页,以及数据添加页,这里就过多的说明了,其中主要说的是:

1、上拉加载分页

这个其实很简单的,就是在 mounted() 中,注册浏览器滚动事件就好了,如果这里你不知道 mounted 是vue生命周期的什么时候的话,就应该好好的再学学了,因为生命周期钩子是很重要的,至少我个人任务是这样的:

在created的时候,视图中的html并没有渲染出来,所以此时如果直接去操作html的dom节点,一定找不到相关的元素

而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点。

这个时候我们就自定义事件:

<pre class="brush:csharp;gutter:true;" style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> //页面渲染完毕后,监听滚动事件
mounted() {
window.addEventListener('scroll', this.handleScroll)
},
methods: {
//处理方法,在页面具体底部30像素的时候,触发事件
handleScroll() {
const jrscrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollT
let scrollBottom = document.body.clientHeight - window.innerHeight - jrscrollTop
if (scrollBottom < 30) {
if (this.ScrollFirst) {
this.nextpage() //触发这个,下拉加载分页事件
}
}
}
},
//记得在销毁当前页面的时候,也就是切换的时候,把监听去掉
beforeDestroy() {
window.removeEventListener('scroll', this.handleScroll, fa
}</pre>

整体很简单,和我们的平时的 JS 上拉加载很相似,具体的可以查看我的代码。

2、设计 axios 插件对接API(两种方式)

这里如果你有一定的 Vue 开发基础,应该能会八成以上,为什么是八成呢,因为可能还是会有一些小问题需要注意,比如重点的就是异步数据加载了。

因为 Nuxt.js 是基于SSR渲染的,所以必须先获取到数据,然后生成 Html 片,才能去渲染,这个和客户端渲染还是不一样的,客户端两者可以同步,甚至先渲染,再去进行 axios 数据请求,但是这样不能实现 SEO 的作用,所以就在原先 axios 请求的基础上,封装了一个异步请求,这样就能达到目的。但是这个在使用的时候,可能不是很顺手,因为很多人已经习惯了直接用当然组件的 data() 中的数据了,在上边我们也看到了,asyncData 编译后的模板,它其实也是当前组件页面中 data 的一部分,我们甚至就可以当成 data 来使用,只不过这个data里,可以写逻辑,可以进行 axios 异步请求,那下边咱们就看看具体用法:

首先我们应该说说如何定义这个插件,当然,我们在每一个页面,通过引入 axios 也行,但是这样有一些问题,除了不方便以为,还有一个就是无法定义一些公共的东西,比如 api 域名路径,所以,我们需要定义一个插件:

A:首先,我们在 plugins 文件夹中定义 axios.js 插件:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">import axios from 'axios'//引入 axios 服务
let options = {} if (process.server) { // 配置基路由
options.baseURL = 'http://localhost:5000' // http://123.xx.xx.xx:5000(服务端地址)
}
export default axios.create(options) // 注意这里是直接 export 了一个对象实例,而不是 Vue.use,这个插件不需要注册</pre>

B:在页面中使用:

image

这里聪明的你可能看到了,为什么没有像 element-ui 插件那样,没有在 nuxt.config.js 中配置这个插件???
这里说下,因为这里是直接返回的一个对象,我们不需要关心是不是要去安装它,它不像 element和下边的markdown那样,我们只有安装了才能用,因为element和 markdown 不是一个返回值的状态,而是 Vue.use ,是直接集成到了 vue 对象内的,所以需要在项目配置文件中进行安装,这里的 axios.js 插件就不需要安装,如果你还不明白的话,我用 http.js 也封装了一个插件,一个就像 element那样,集成到vue 的插件:

image

这样的话,我们在组件页面中,就可以直接这么用:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">const {data: {response}} = await Vue.http.get('/api/TopicDetail/'+ id);//这个就是使用到了 Vue 对象</pre>

所以这个就需要安装,因为我们必须在 app(也就是vue)被实例化之前,安装这个插件:

image

如果你还看不懂 axios.js插件(返回对象,可直接使用)和 http.js插件(集成到vue对象里,需要注册),这两者之间的区别的话,请留言,或者加群,我详细给你说说。

C:最后为了使用接口,我们需要进行跨域,使用 proxy 代理:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> proxy: [
['/api', { target: 'http://localhost:5000' }],
['/images', { target: 'http://localhost:5000' }],// 将图片也代理到本地,下文的<上传图片>栏目会说到 // ['/api', { target: 'http://123.xx.xx.xx:3080' }],//服务端配置
]

//还有这里
modules: [</pre>

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> // Doc: https://github.com/nuxt-community/axios-module#usage
'@nuxtjs/axios',
'@nuxtjs/proxy'
],</pre>

D:接下来我们就说说如何进行异步获取数据

就拿我们的首页数据加载来说,我们的需求是加载出来全部的 Bug 数据,首先 index.vue 中肯定有一些基本的数据,比如当前页数等常量参数,这些参数不需要我们异步去获取,因为它们在页面渲染之前已经被定义了:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> //当前组件中的数据变量
data() { return {
page: 0,
lastpage: true,
ScrollFirst: true, };
}, </pre>

但是,上边我们也说了,我们为了实现 SSR 渲染,必须在渲染前就要异步获取数据,所以这里我们就使用到了异步数据加载 asyncData:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> // 一共有三种方法,这个最常用的一种方法,asyncData() 方法
async asyncData({ params }) { try { // 路由参数
const { tag } = params
// 获取异步得到的数据,必须使用 {} 大括号包裹
let { data } = await axios.get(/api/TopicDetail/24)
console.log(data) // 如果想要获取返回数据 data 中的某一个属性,可以这么嵌套
const { data: { article } } = await axios.get(/api/TopicDetail?page=1)
console.log(article) // 然后将得到的数据返回过去,其实和本身的 data() 有异曲同工之妙
return {
articleList: article,
tagtitle: tag,
fadetitle: true,
notfound: !article.length
}
} catch (err) {
error({statusCode: 404})
}
},</pre>

提示:如果你不需要使用异步的话,直接用 let data= axios.get(/api/TopicDetail/24) 就可以了。

其实通过这里,你应该也能看的明白,这个异步获取数据,本身和 data() 是很像的,通过操作,把数据返回到页面里,这里只是要注意两点:

1、一定要是异步的 await;

2、一定注意返回的数据格式,你可以用debugger 或者 console 来查看,也可以直接用 mock 数据测试。

最后,就是像 data 数据一样,直接在页面其他地方进行使用了,

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"><el-col :xs="24" :sm="24" :md="24" :lg="12" v-for="item in articleList" :key="item.id" class="artitem" ></pre>

如果你是第一次使用,肯定会遇到找不到数据而报错的情况,用我说的方法,断点调试+mock 数据,相信很快你会游刃有余了。

3、选择Mavon-Editor富文本编辑器

关于富文本编辑器,我也找了几个做对比,因为平时更喜欢用 markdown ,所以,就选用了这个 mavon-editor 编辑器了,使用方法很简单,还是插件三步走:

1、安装依赖:

  npm install mavon-editor --save

2、在plugins中创建vue-markdown.js

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">import Vue from 'vue' import mavonEditor from 'mavon-editor' Vue.use(mavonEditor)</pre>

3、在nuxt.config.js中引入

image

具体如何在详情页展示呢,我用的是 highlight.js 和 marked 来实现的,使用方法很简单,安装过包以后,在页面内引用,然后对我们添加的 content 代码进行 marked() 方法即可,具体使用请查看我的代码。

4、上传图片

这个上传图片还是比较简单的,只不过需要有一些细节需要注意下,我这里有两个版本的上传图片的方法 ,一个是 element-upload 上传用户头像的,一个是 mavon 富文本编辑器中的上传图片的方法,后台方法是同一个,只不过前端控制稍微不同罢了,这里先说下后端API:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> [HttpPost]
[Route("Pic")] public async Task<MessageModel<string>> InsertPicture([FromServices]IHostingEnvironment environment)
{ var data = new MessageModel<string>(); string path = string.Empty; string foldername = "images"; var files = Request.Form.Files; if (files == null || files.Count() <= 0) { data.Msg = "请选择上传的文件。"; return data; } //格式限制
var allowType = new string[] { "image/jpg", "image/png", "image/jpeg" }; string folderpath = Path.Combine(environment.WebRootPath, foldername); if (!System.IO.Directory.Exists(folderpath))
{
System.IO.Directory.CreateDirectory(folderpath);
} if (files.Any(c => allowType.Contains(c.ContentType)))
{ if (files.Sum(c => c.Length) <= 1024 * 1024 * 4)
{ //foreach (var file in files)
var file = files.FirstOrDefault(); string strpath = Path.Combine(foldername, DateTime.Now.ToString("MMddHHmmss") + file.FileName);
path = Path.Combine(environment.WebRootPath, strpath); using (var stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{ await file.CopyToAsync(stream);
}

                data = new MessageModel<string>()
                {
                    Response = strpath,
                    Msg = "上传成功",
                    Success = true,
                }; return data;
            } else {
                data.Msg = "图片过大"; return data;
            }
        } else {
            data.Msg = "图片格式错误"; return data;
        }
    }</pre>

然后看看前端是怎么操作的:

1、element-ui 的 upload 上传图片方法:

这个很简单,官方栗子中,已经说的很明白了,几乎不用做任何的修改就能达到目的,只需要我们修改下后端的api地址即可,注意这里有跨域的问题,我直接用的是本地代理的地址:

image

注意注意下边三个问题就行了:

1、接口地址配置在 action;

2、一定要跨域,本项目使用的是本地代理 proxy,你也可以使用后端的CORS实现跨域;

3、记得要在后端配置使用静态资源文件 app.UseStaticFiles(); ,因为我把图片放到了wwwroot里了;

其他的和官方地址一模一样的。http://element-cn.eleme.io/#/zh-CN/component/upload

2、mavon 富文本编辑器上传图片

这个完全照搬官方文档即可,几乎不用修改什么,只不过注意事项还是上边的那三个,跨域+静态资源使用,不多说:图片上传详情点击这里...,或者看我的Github代码。

四、服务端接口设计

1、设计统一数据结构接口

我这里使用了异步的统一泛型返回数据模型,大家也都能看懂,因为第一个系列已经说的很多了:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> /// <summary>
/// 获取Bug数据列表(带分页) /// </summary>
/// <param name="page">页数</param>
/// <param name="tname">专题类型</param>
/// <returns></returns>
[HttpGet] public async Task<MessageModel<PageModel<TopicDetail>>> Get(int page = 1, string tname = "")
{ var data = new MessageModel<PageModel<TopicDetail>>(); int intTotalCount = 6; int TotalCount = 0; int PageCount = 1;
List<TopicDetail> topicDetails = new List<TopicDetail>(); //总数据,使用AOP切面缓存
topicDetails = await _topicDetailServices.GetTopicDetails(); if (!string.IsNullOrEmpty(tname))
{ var tid = (await _topicServices.Query(ts => ts.tName == tname)).FirstOrDefault()?.Id.ObjToInt();
topicDetails = topicDetails.Where(t => t.TopicId == tid).ToList();
} //数据总数
TotalCount = topicDetails.Count; //总页数
PageCount = (Math.Ceiling(topicDetails.Count.ObjToDecimal() / intTotalCount.ObjToDecimal())).ObjToInt(); //当前页数据
topicDetails = topicDetails.OrderByDescending(d => d.Id).Skip((page - 1) * intTotalCount).Take(intTotalCount).ToList(); return new MessageModel<PageModel<TopicDetail>>()
{
Msg = "获取成功",
Success = topicDetails.Count >= 0,
Response = new PageModel<TopicDetail>()
{
page = page,
pageCount = PageCount,
dataCount = TotalCount,
data = topicDetails,
}
};

    }</pre>

通用返回信息类 和 通用分页类:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> /// <summary>
/// 通用返回信息类 /// </summary>
public class MessageModel<T> { /// <summary>
/// 操作是否成功 /// </summary>
public bool Success { get; set; } = false; /// <summary>
/// 返回信息 /// </summary>
public string Msg { get; set; } = "服务器异常"; /// <summary>
/// 返回数据集合 /// </summary>
public T Response { get; set; }

} /// <summary>
/// 通用分页信息类 /// </summary>
public class PageModel<T> { /// <summary>
/// 当前页数 /// </summary>
public int page { get; set; } = 1; /// <summary>
/// 总页数 /// </summary>
public int pageCount { get; set; } = 6; /// <summary>
/// 数据总数 /// </summary>
public int dataCount { get; set; } = 0; /// <summary>
/// 返回数据 /// </summary>
public List<T> data { get; set; }

}</pre>

五、对接口进行配置权限

因为我们的TiBug 1.0 还没有设计用户登录问题,所以,这一块还没有,不过别慌,过些天我会加上,这几天先把1.0版本发布到服务器。

然后开始开发 TiBug 1.5版本,设计管理后台,初步是基于VueAdmin的,主要是用户管理和Bug管理,权限管理等,大家也看到了,目前的版本是只能添加不能修改,还是基于我们之前设计的 [Authorize("Permission")],数据库配置,我本地简单试了试,还是挺好用的,到时候也会尝试下滑动过期等问题的解决方案。

六、结语

到这里我们已经把项目的结构定义好了,而且也自适应了设备,内容简单,主要的就是讲解了:

1、如何通过 nuxt 官方脚手架搭建项目;2、页面是如何加载的;3、插件的使用;4、异步数据运行;5、使用富文本编辑器以及上传图片等等,作为展示。

这几天把整体页面都设计好了,也上传到Github上了,下篇开始部署,发布到服务器。

七、Github

https://github.com/anjoy8/Nuxt.tBug

https://github.com/anjoy8/Blog.Core

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容