.net core + vue开发单页应用(二)
上一篇中已将完成了基础结构的搭建,这一篇将开始正式开发。页面直接使用了一位小伙伴的源码,可以在https://github.com/taylorchen709/vue-admin下载修改入口main.js在入口函数中添加对element-ui,vue-router,vuex,vue-source等组件的引用import Vue from 'vue'import App from
上一篇中已将完成了基础结构的搭建,这一篇将开始正式开发。页面直接使用了一位小伙伴的源码,可以在https://github.com/taylorchen709/vue-admin下载
修改入口main.js
在入口函数中添加对element-ui,vue-router,vuex,vue-source等组件的引用
import Vue from 'vue'
import App from './App'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
import VueRouter from 'vue-router'
import store from './vuex/store'
import Vuex from 'vuex'
import VueSource from 'vue-resource'
import routes from './routes'
import 'font-awesome/css/font-awesome.min.css'
Vue.use(ElementUI)
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.use(VueSource)
//创建路由实例
const router = new VueRouter({
routes
})
router.beforeEach((to, from, next) => {
//这里进行了简单的登录验证
//访问login时直接清除user
if (to.path == '/login') {
sessionStorage.removeItem('user');
}
let user = JSON.parse(sessionStorage.getItem('user'));
if (!user && to.path != '/login') {
next({ path: '/login' })
} else {
next()
}
})
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
修改App.vue
<template>
<div id="app">
<transition name="fade"
mode="out-in">
<router-view></router-view>
</transition>
</div>
</template>
<script>
export default {
name: 'app',
components: {
}
}
</script>
增加viewes文件夹,并增加模块视图
login.vue
<template>
<el-form :model="ruleForm2" :rules="rules2" ref="ruleForm2" label-position="left" label-width="0px" class="demo-ruleForm login-container">
<h3 class="title">系统登录</h3>
<el-form-item prop="account">
<el-input type="text" v-model="ruleForm2.account" auto-complete="off" placeholder="账号"></el-input>
</el-form-item>
<el-form-item prop="checkPass">
<el-input type="password" v-model="ruleForm2.checkPass" auto-complete="off" placeholder="密码"></el-input>
</el-form-item>
<el-checkbox v-model="checked" checked class="remember">记住密码</el-checkbox>
<el-form-item style="width:100%;">
<el-button type="primary" style="width:100%;" @click.native.prevent="handleSubmit2" :loading="logining">登录</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
logining: false,
ruleForm2: {
account: 'admin',
checkPass: '123456'
},
rules2: {
account: [
{ required: true, message: '请输入账号', trigger: 'blur' },
//{ validator: validaePass }
],
checkPass: [
{ required: true, message: '请输入密码', trigger: 'blur' },
//{ validator: validaePass2 }
]
},
checked: true
};
},
methods: {
handleReset2() {
this.$refs.ruleForm2.resetFields();
},
handleSubmit2(ev) {
var _this = this;
this.$refs.ruleForm2.validate((valid) => {
if (valid) {
this.logining = true;
//NProgress.start();
var loginParams = { username: this.ruleForm2.account, password: this.ruleForm2.checkPass };
if (loginParams.username != 'admin') {
this.$message({
message: '用户不存在',
type: 'error'
});
this.logining = false;
return false;
}
if (loginParams.password != '123456') {
this.$message({
message: '密码不正确',
type: 'error'
});
this.logining = false;
return;
}
let user = {
id: 1,
username: 'admin',
password: '123456',
avatar: 'resources/img/user.png',
name: '管理员'
}
sessionStorage.setItem('user', JSON.stringify(user));
this.$router.push({ path: '/' });
} else {
return false;
}
});
}
}
}
</script>
<style scoped>
.login-container {
/*box-shadow: 0 0px 8px 0 rgba(0, 0, 0, 0.06), 0 1px 0px 0 rgba(0, 0, 0, 0.02);*/
-webkit-border-radius: 5px;
border-radius: 5px;
-moz-border-radius: 5px;
background-clip: padding-box;
margin: 180px auto;
width: 350px;
padding: 35px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
.remember {
margin: 0px 0px 35px 0px;
}
</style>
home.vue
<template>
<el-row class="container">
<el-col :span="24" class="header">
<el-col :span="10" :class="collapsed?'logo-collapse-width logo-collapsed':'logo-width logo'">
{{collapsed?'':sysName}}
<!--<img :src="logo" v-show="collapsed" />-->
</el-col>
<el-col :span="10">
<div class="tools" @click.prevent="collapse">
<i class="fa fa-align-justify"></i>
</div>
</el-col>
<el-col :span="4" class="userinfo">
<el-dropdown trigger="hover">
<span class="el-dropdown-link userinfo-inner"><img :src="this.sysUserAvatar" /> {{sysUserName}}</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>我的消息</el-dropdown-item>
<el-dropdown-item>设置</el-dropdown-item>
<el-dropdown-item divided @click.native="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-col>
<el-col :span="24" class="main">
<aside :class="collapsed?'menu-collapsed':'menu-expanded'">
<!--导航菜单-->
<el-menu :default-active="$route.path" class="el-menu-vertical-demo" @open="handleopen" @close="handleclose" @select="handleselect"
unique-opened router v-show="!collapsed">
<template v-for="(item,index) in $router.options.routes" v-if="!item.hidden">
<el-submenu :index="index+''" v-if="!item.leaf">
<template slot="title">
<i :class="item.iconCls"></i>{{item.name}}
</template>
<el-menu-item v-for="child in item.children" :index="child.path" :key="child.path" v-if="!child.hidden">{{child.name}}</el-menu-item>
</el-submenu>
<el-menu-item v-if="item.leaf&&item.children.length>0" :index="item.children[0].path"><i :class="item.iconCls"></i>{{item.children[0].name}}</el-menu-item>
</template>
</el-menu>
<!--导航菜单-折叠后-->
<ul class="el-menu el-menu-vertical-demo collapsed" v-show="collapsed" ref="menuCollapsed">
<li v-for="(item,index) in $router.options.routes" v-if="!item.hidden" class="el-submenu item">
<template v-if="!item.leaf">
<div class="el-submenu__title" style="padding-left: 20px;" @mouseover="showMenu(index,true)" @mouseout="showMenu(index,false)"><i :class="item.iconCls"></i></div>
<ul class="el-menu submenu" :class="'submenu-hook-'+index" @mouseover="showMenu(index,true)" @mouseout="showMenu(index,false)">
<li v-for="child in item.children" v-if="!child.hidden" :key="child.path" class="el-menu-item" style="padding-left: 40px;" :class="$route.path==child.path?'is-active':''" @click="$router.push(child.path)">{{child.name}}</li>
</ul>
</template>
<template v-else>
<li class="el-submenu">
<div class="el-submenu__title el-menu-item" style="padding-left: 20px;height: 56px;line-height: 56px;padding: 0 20px;" :class="$route.path==item.children[0].path?'is-active':''" @click="$router.push(item.children[0].path)"><i :class="item.iconCls"></i></div>
</li>
</template>
</li>
</ul>
</aside>
<section class="content-container">
<div class="grid-content bg-purple-light">
<el-col :span="24" class="breadcrumb-container">
<!--<strong class="title">{{$route.name}}</strong>-->
<el-breadcrumb separator="/" class="breadcrumb-inner">
<el-breadcrumb-item v-for="item in $route.matched" :key="item.path">
{{ item.name }}
</el-breadcrumb-item>
</el-breadcrumb>
</el-col>
<el-col :span="24" class="content-wrapper">
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</el-col>
</div>
</section>
</el-col>
</el-row>
</template>
<script>
export default {
data() {
return {
sysName:'Simple',
logo:'dist/resources/img/logo.png',
collapsed:false,
sysUserName: '',
sysUserAvatar: '',
form: {
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
}
}
},
methods: {
onSubmit() {
console.log('submit!');
},
handleopen() {
//console.log('handleopen');
},
handleclose() {
//console.log('handleclose');
},
handleselect: function (a, b) {
},
//退出登录
logout: function () {
var _this = this;
this.$confirm('确认退出吗?', '提示', {
//type: 'warning'
}).then(() => {
sessionStorage.removeItem('user');
_this.$router.push('/login');
}).catch(() => {
});
},
//折叠导航栏
collapse:function(){
this.collapsed=!this.collapsed;
},
showMenu(i,status){
this.$refs.menuCollapsed.getElementsByClassName('submenu-hook-'+i)[0].style.display=status?'block':'none';
}
},
mounted() {
var user = sessionStorage.getItem('user');
if (user) {
user = JSON.parse(user);
this.sysUserName = user.name || '';
this.sysUserAvatar = user.avatar || '';
}
}
}
</script>
<style scoped >
.container {
position: absolute;
top: 0px;
bottom: 0px;
width: 100%;
}
.container .header {
height: 60px;
line-height: 60px;
background: #20a0ff;
color: #fff;
}
.container .header .userinfo {
text-align: right;
padding-right: 35px;
float: right;
}
.container .header .userinfo .userinfo-inner {
cursor: pointer;
color: #fff;
}
.container .header .userinfo .userinfo-inner img {
width: 40px;
height: 40px;
border-radius: 20px;
margin: 10px 0px 10px 10px;
float: right;
}
.container .header .logo {
height: 60px;
font-size: 22px;
padding-left: 20px;
padding-right: 20px;
border-color: rgba(238, 241, 146, 0.3);
border-right-width: 1px;
border-right-style: solid;
}
.container .header .logo img {
width: 40px;
float: left;
margin: 10px 10px 10px 18px;
}
.container .header .logo .txt {
color: #fff;
}
.container .header .logo-collapsed {
padding: 0;
font-size: 22px;
padding-left: 0px;
padding-top: 10px;
border-color: rgba(238, 241, 146, 0.3);
border-right-width: 1px;
border-right-style: solid;
}
.container .header .logo-collapsed img {
width: 60px;
float: left;
margin: 0px;
}
.container .header .logo-collapsed .txt {
color: #fff;
}
.container .header .logo-width {
width: 230px;
}
.container .header .logo-collapse-width {
width: 60px;
}
.container .header .tools {
padding: 0px 23px;
width: 14px;
height: 60px;
line-height: 60px;
cursor: pointer;
}
.container .main {
display: flex;
position: absolute;
top: 60px;
bottom: 0px;
overflow: hidden;
}
.container .main aside {
flex: 0 0 230px;
width: 230px;
}
.container .main aside .el-menu {
height: 100%;
}
.container .main aside .collapsed {
width: 60px;
}
.container .main aside .collapsed .item {
position: relative;
}
.container .main aside .collapsed .submenu {
position: absolute;
top: 0px;
left: 60px;
z-index: 99999;
height: auto;
display: none;
}
.container .main .menu-collapsed {
flex: 0 0 60px;
width: 60px;
}
.container .main .menu-expanded {
flex: 0 0 230px;
width: 230px;
}
.container .main .content-container {
flex: 1;
overflow-y: scroll;
padding: 20px;
}
.container .main .content-container .breadcrumb-container .title {
width: 200px;
float: left;
color: #475669;
}
.container .main .content-container .breadcrumb-container .breadcrumb-inner {
float: left;
padding-bottom: 10px;
}
.container .main .content-container .content-wrapper {
background-color: #fff;
box-sizing: border-box;
}
</style>
此时编译运行可以看到下面的页面,由于还没有开发子页面,所以只显示主页和菜单:
添加模拟的后端API
这里模拟用户管理的功能。为.net开发,具体的开发流程不在赘述。
UserInfo.cs
public class UserInfo
{
public string uuid { get; set; }
public string login_name { get; set; }
public string real_name { get; set; }
public string email { get; set; }
public string birthday { get; set; }
public int age { get; set; }
}
UserInfoRepository.cs
public class UserInfoRepository
{
static List<UserInfo> db_users = new List<UserInfo>() {
};
public static IList<UserInfo> LoadAll() {
return db_users;
}
public static string Insert(UserInfo u)
{
u.uuid = Guid.NewGuid().ToString();
db_users.Add(u);
return u.uuid;
}
public static string Update(UserInfo u)
{
db_users.Remove(db_users.Where(m => m.uuid.Equals(u.uuid)).FirstOrDefault());
db_users.Add(u);
return u.uuid;
}
public static UserInfo Get(string uuid) {
return db_users.Where(m=>m.uuid.Equals(uuid)).FirstOrDefault();
}
public static void Delete(string uuid)
{
var u = db_users.Where(m => m.uuid.Equals(uuid)).FirstOrDefault();
db_users.Remove(u);
}
}
UserController.cs
[Route("api/[controller]")]
public class UsersController : Controller
{
[HttpGet]
public string Get()
{
return JsonConvert.SerializeObject(UserInfoRepository.LoadAll());
}
[HttpGet("{uuid}")]
public string Get(string uuid)
{
return JsonConvert.SerializeObject(UserInfoRepository.Get(uuid));
}
[HttpGet("p/")]
public string Get(string name, int page = 1, int pagesize = 20)
{
long count = 0;
var list = UserInfoRepository.LoadAll();
count = list.Count;
var obj = new
{
total = count,
list = list.Where(m => m.real_name.Equals(name)).Skip((page - 1) * pagesize).Take(pagesize)
};
return JsonConvert.SerializeObject(obj);
}
[HttpPost]
public void Post([FromBody]UserInfo user)
{
UserInfoRepository.Insert(user);
}
[HttpPut]
public void Put([FromBody]UserInfo user)
{
UserInfoRepository.Update(user);
}
[HttpDelete("{uuid}")]
public void Delete(string uuid)
{
UserInfoRepository.Delete(uuid);
}
}
添加列表页面
添加内容包括,users.vue页面、路由以及将登录后的默认页面修改为users
users.vue
<template>
<section>
<!--工具条-->
<el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
<el-form :inline="true" :model="filters">
<el-form-item>
<el-input v-model="filters.name" placeholder="姓名"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="search">查询</el-button>
</el-form-item>
<el-form-item>
<el-button type="success" v-on:click="add">添加</el-button>
</el-form-item>
</el-form>
</el-col>
<!--列表-->
<el-table :data="infos" highlight-current-row v-loading="listLoading" style="width: 100%;">
<el-table-column type="selection" width="55">
</el-table-column>
<el-table-column type="index" width="60">
</el-table-column>
<el-table-column prop="login_name" label="登录名" sortable>
</el-table-column>
<el-table-column prop="real_name" label="真实姓名" sortable>
</el-table-column>
<el-table-column prop="email" label="邮箱" sortable>
</el-table-column>
<el-table-column prop="birthday" label="生日" sortable>
</el-table-column>
<el-table-column prop="age" label="年龄" sortable>
</el-table-column>
<el-table-column label="操作" width="150">
<template scope="scope">
<el-button type="warning" size="small" @click="edit(scope.$index, scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="del(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-col :span="24" class="toolbar">
<el-pagination layout="prev, pager, next" @current-change="handleCurrentChange" :page-size="10" :total="total" style="float:left;"></el-pagination>
</el-col>
</section>
</template>
<script>
import ElCol from "element-ui/packages/col/src/col";
export default {
components: {ElCol}, data(){
return {
filters:{
name:'',
},
infos:[],
listLoading: false,
page:1,
total:0
};
},
methods:{
search:function(){
var _self =this;
_self.listLoading=true;
this.$http.get('/api/users/s',{
params:{
name:_self.filters.name,
page:_self.page,
pagesize:10
}
}).then((response)=>{
_self.infos=response.data.list;
_self.total=response.data.total;
_self.listLoading=false;
},(response)=>{
}).catch((response)=>{});
},
add:function(){
this.$router.push('/useradd')
},
edit:function(index,row){
this.$router.push(
{ path: '/useradd', query: { uuid: row.uuid }}
)
},
del:function(index,row){
var _self =this;
this.$confirm('确定删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
_self.$http.delete('/api/users/'+row.Uuid).then((res)=>{
_self.search();
_self.$message({
type: 'success',
message: '删除成功!'
});
},(res)=>{
_self.$message({
type: 'srror',
message: '删除失败!'
});
}).catch();
});
},
handleCurrentChange:function(p){
this.page = p;
this.search();
}
},
mounted() {
this.search();
}
}
</script>
添加路由,在系统管理的children下添加users
{
path: '/',
component: Home,
name: '系统管理',
iconCls: 'el-icon-setting',//图标样式class
children: [
{path: '/users', component: Users, name: '用户管理' }
]
}
同样的方式增加用户添加和编辑页面,完成用户管理小模块。
总结
我们完成了在.net core下基于vue开发单页应用,这种方法实际上是利用webpack的打包功能,编译时将应用打包好,.net应用的cshtml页面再引用已打包的js文件。方案缺陷之一为:每次修改页面都需要重新build,不能同使用Node开发时实时预览页面,在实际的项目中可能并不会使用这种方式,仅供折腾。下面附上源码地址:
https://github.com/wenjq0911/simple.git
源博客地址使用的leanote,地址在此 http://blog.leanote.com/post/wenjq0911@gmail.com
更多推荐
所有评论(0)