【默默努力】vue-pc-app
最近在github上面看到了一个团队的项目,真的非常赞。他们进行vue-cli的二次开发,将项目用自己的方式打包。
今天的这个开源项目地址为:https://github.com/tffe-team/vue-pc-app
项目运行效果为
可以直接登录进去页面效果为
我猜那个团队的意思应该是将一些项目模板化,项目的架子在那里,什么需要添加的,就在里面扩展。
感谢无私分享的程序员,接下来我们看代码
项目入口文件为main.ts
import Vue from 'vue'
import App from '@/App.vue'
import router from './router'
import store from './store'
import iView from 'iview'
import 'iview/dist/styles/iview.css'
const {LoadingBar} = iView
Vue.use(iView)
Vue.config.productionTip = false
router.beforeEach((to, from, next) => {
LoadingBar.start()
next()
})
router.afterEach(route => {
LoadingBar.finish()
//内容滚动的位置
window.scrollTo(0, 0)
})
new Vue({
store,
router,
render: h => h(App),
}).$mount('#app')
后台管理系统用的iview框架
入口组件为App.vue
//用到了装饰器语法修饰组件
<template>
<div id="app">
<Layout style="height: 100%;"><router-view/></Layout>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class App extends Vue {}
</script>
<style>
html{
height: 100%;
}
body{
height: 100%;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
height: 100%;
}
.g-page-box{
text-align: right;
padding: 20px 0px;
}
.g-link {
color: #2d8cf0;
margin: 0 2px;
cursor: pointer;
}
.g-link-disable{
color: #515a6e;
}
.g-right-split{
border-right: 1px solid #999;
padding-right: 5px;
margin-right: 5px;
}
.g-text-red{
color: red;
}
.g-text-green{
color: #05c705;
}
.g-text-orange{
color: orange;
}
.g-pointer{
cursor: pointer;
}
</style>
路由为
//src\router\index.ts
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const Home = () => import(/* webpackChunkName: "home" */ '@/components/Home.vue')
const Login = () => import(/* webpackChunkName: "login" */ '@/components/Login.vue')
const Base = () => import(/* webpackChunkName: "base" */ '@/components/Base.vue')
const List = () => import(/* webpackChunkName: "list" */ '@/components/List.vue')
const AddPage = () => import(/* webpackChunkName: "list" */ '@/components/AddPage.vue')
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'index',
redirect: '/login'
},
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/home',
component: Base,
children: [
{
path: '/home',
name: 'home',
component: Home
}
]
},
{
path: '/main',
component: Base,
children: [
{
path: 'model',
name: 'model',
component: List
}
]
},
{
path: '/add',
component: Base,
children: [
{
path: 'addPage',
name: 'addPage',
component: AddPage
}
]
}
]
})
根据路由我们进行页面的分析
第一个是Login.vue
页面效果为
代码为
//login.vue
<template>
<div class="m-login">
<div class="desc">
<h2 class="title">举个栗子举个</h2>
<div class="sub-title">后台系统</div>
</div>
<Form ref="loginForm" :rules="validationLogin" :model="formInfo">
<FormItem prop="userName" label="用户名">
<Input placeholder="请输入用户名" v-model="formInfo.userName"></Input>
</FormItem>
<FormItem prop="userPassword" label="密码">
<Input type="password" placeholder="请输入密码" v-model="formInfo.userPassword"></Input>
</FormItem>
<FormItem>
<div class="remember">
<Checkbox v-model="remember">记住我</Checkbox>
</div>
</FormItem>
<FormItem>
<Button type="primary" long class="login-btn" @click="submitLogin" :loading="btnLoading">登录</Button>
</FormItem>
</Form>
<p class="copy-right">©️版权信息归时光金科所有</p>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import { login } from '@/api/index'
@Component
export default class Login extends Vue {
btnLoading = false
remember = false
formInfo: any = {
userName: '',
userPassword: ''
}
validationLogin = {}
validateUserName = (rule: string, value: string, callback: Function) => {
if (!value) {
callback(new Error('请输入姓名'))
} else {
callback()
}
}
validateUserPassword = (rule: string, value: string, callback: Function) => {
if (!value) {
callback(new Error('请输入密码'))
} else {
callback()
}
}
created() {
this.validationLogin = {
userName: { validator: this.validateUserName, trigger: 'blur' },
userPassword: { validator: this.validateUserPassword, trigger: 'blur' }
}
this._getLoginInfo()
}
submitLogin(): void {
const myForm: any = this.$refs.loginForm
myForm.validate((valid: any) => {
if (valid) {
this.btnLoading = true
login({
username: this.formInfo.userName,
password: this.formInfo.userPassword
}, (res: any) => {
// 这里比较神奇,任意的username和password都可以使用
if (res.code === 200) {
localStorage.setItem('realName', res.data.chinesename)
//打印出结果为魔方
console.log('realName',res.data.chinesename)
if (this.remember) {
localStorage.setItem('loginInfo', [this.formInfo.userName, this.formInfo.userPassword].join('_'))
} else {
localStorage.removeItem('loginInfo')
}
this.$Message.success({
content: '登录成功',
onClose: (): void => {
window.location.href = '/home'
}
})
} else {
this.btnLoading = false
}
})
}
})
}
_getLoginInfo(): void {
const loginInfo = localStorage.getItem('loginInfo')
if (loginInfo) {
this.remember = true
this.formInfo.userName = loginInfo.split('_')[0]
this.formInfo.userPassword = loginInfo.split('_')[1]
}
}
}
</script>
<style lang="scss" scoped>
.m-login {
height: 80%;
width: 100%;
position: relative;
background: url(../assets/simple-codelines.svg), #2b3137;
background-position: center 10%;
background-size: cover;
color: hsla(0, 0%, 100%, 0.6);
.remember {
color: #515a6e;
text-align: left;
}
.desc {
position: absolute;
top: 30%;
left: 30%;
.title {
color: #fff;
text-align: left;
font-size: 50px;
}
.sub-title {
font-size: 45px;
}
}
.ivu-form {
position: absolute;
top: 30%;
right: 200px;
padding: 20px;
width: 340px;
background-color: #fff;
margin: 0 auto;
border-radius: 5px;
font-size: 14px;
}
.login-btn {
height: 40px;
}
.copy-right {
position: absolute;
bottom: -32px;
width: 100%;
color: #2b3137;
}
}
</style>
接下来进入home页面
home页面应该指的是这部分
代码为
//src\components\Home.vue
<template>
<div id="home" class="m-home">
<div class="content">
<h2 class="title">欢迎</h2>
<p class="subtitle">使用后台系统</p>
</div>
</div>
</template>
<script>
export default {
name: 'home',
methods: {
backHome () {
this.$router.push({path: '/'})
}
}
}
</script>
<style scoped lang="scss">
.m-home {
width: 846px;
padding-top: 100px;
margin: 0 auto;
.content {
padding-top: 85px;
vertical-align: top;
background: url('../assets/home.png') right top no-repeat;
height: 340px;
.title {
font-size: 50px;
color: #545a5a;
}
.subtitle {
font-size: 31px;
color: #999;
margin-top: 5px;
}
.back {
margin-top: 10px;
}
}
.ft {
font-size: 12px;
color: #999;
text-align: center;
width: 100%;
position: absolute;
left: 0;
bottom: 20px;
}
}
</style>
Home页面中会用到base组件
//base.vue
<template>
<div class="m-layout">
<Layout>
<Header :style="{ width: '100%', backgroundColor: '#2b3137'}">
<div class="layout-logo">
<h2 class="title">后台系统</h2>
</div>
<ul class="login-info">
<li>欢迎你,{{userName}}</li>
<li class="login-out" @click="userLoginOut">退出</li>
</ul>
</Header>
<Layout>
<Sider hide-trigger :style="{background: '#fff'}">
<Menu theme="light" width="auto" :active-name="activeName" :open-names="['2']">
<MenuItem name="model">
<router-link :to="{ name: 'model'}">
<Icon type="ios-podium"/>列表页
</router-link>
</MenuItem>
<MenuItem name="addPage">
<router-link :to="{ name: 'addPage'}">
<Icon type="ios-podium"/>添加页面
</router-link>
</MenuItem>
</Menu>
</Sider>
<Layout :style="{padding: '0 24px 24px'}">
<Content
:style="{padding: '24px', flex: '1', background: '#fff', marginTop:'30px'}"
>
<router-view></router-view>
</Content>
</Layout>
</Layout>
</Layout>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { loginOut } from '@/api/index'
@Component
export default class Base extends Vue {
userName: string|null = localStorage.getItem('realName')
activeName: string|undefined = ''
userLoginOut(): void {
loginOut((res: any) => {
if (res.code === 200) {
this.$Message.success({
content: '退出登录',
onClose: (): void => {
window.location.href = '/login'
}
})
}
})
}
created() {
this.activeName = this.$route.name
}
}
</script>
<style lang="scss" scoped>
.m-layout {
background: #f5f7f9;
position: relative;
border-radius: 4px;
height: 100%;
text-align: left;
overflow: auto;
.ivu-layout {
height: 100%;
}
.ivu-layout-header {
padding: 0 26px;
}
.login-info {
float: right;
margin-right: 20px;
color: #fff;
li {
display: inline;
list-style: none;
padding-right: 10px;
}
.login-out {
cursor: pointer;
}
}
.layout-logo {
width: 198px;
height: 30px;
float: left;
position: relative;
top: 10px;
left: 0;
.logo {
width: 82px;
height: 37px;
float: left;
margin: 4px 4px 0 0;
}
.title {
color: #fff;
line-height: 45px;
font-size: 26px;
}
}
.ivu-menu-item {
padding: 0;
a {
color: #515a6e;
padding: 14px 24px;
display: block;
}
.ivu-icon {
margin-right: 6px;
margin-top: -4px;
}
}
.ivu-menu-item-selected a {
color: #2d8cf0;
}
}
</style>
列表页页面效果为
列表页是vuex与后端进行交互渲染的数据
定义了list列表
//state.ts
const state: any = {
list: [],
listAdd: []
}
export default state
定义类型
export const SAVE_LIST = 'SAVE_LIST'
export const SAVE_LIST_ADD = 'SAVE_LIST_ADD'
定义getter
export const list = (state: any) => state.list
export const listAdd = (state:any)=>state.listAdd
action中定义获取的方法
import * as types from './mutation-types'
import { ActionTree, Action } from 'vuex'
import {getListData } from '@/api'
const getList: Action<object, object> = ({ commit }, params) => {
return new Promise((resolve, reject) => {
getListData(params, (res: any) => {
if (+res.code === 200 && res.data.page) {
const data = res.data
commit(types.SAVE_LIST, data.page.data)
resolve(data)
} else {
reject(res.msg)
}
})
})
}
const getListAdd: Action<object, object> = ({ commit }, params) => {
return new Promise((resolve, reject) => {
getListData(params, (res: any) => {
if (+res.code === 200 && res.data.page) {
const data = res.data
commit(types.SAVE_LIST_ADD, data.page.data)
resolve(data)
} else {
reject(res.msg)
}
})
})
}
const actions: ActionTree<object, object> = {
getList,
getListAdd
}
export default actions
在index.ts中引用这些
//index.ts
import Vue from 'vue'
import Vuex from 'vuex'
import actions from './actions'
import * as getters from './getters'
import state from './state'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
actions,
getters,
state,
mutations,
strict: debug,
plugins: debug ? [createLogger({})] : []
})
页面效果为
我按照上面的模式扩展了添加页面的数据,由于接口相同故为一样的数据
项目中封装的axios代码为
//src\interceptors\axios.ts
import axios from 'axios'
import iView from 'iview'
const {Modal} = iView
let axiosInstance: any = {}
axiosInstance = axios.create({
timeout: 20000,
maxContentLength: 2000,
headers: {
'Content-Type': 'application/json'
}
})
axiosInstance.interceptors.request.use((config: any) => {
if (config.method.toLowerCase() === 'get') {
config.params = config.data
}
const requestConfig = Object.assign({
responseType: 'json'
}, config)
return requestConfig
}, (error: any) => {
Modal.error({
title: '错误提示',
content: error.message || '服务器异常'
})
return Promise.reject(error.message)
})
axiosInstance.interceptors.response.use((response: any) => {
const data = response.data
if (+data.code === 10010) {
window.location.href = '/login'
return
}
if (+data.code !== 200) {
Modal.error({
title: '错误提示',
content: data.msg || '服务器异常'
})
}
return data
}, (error: any) => {
Modal.error({
content: error.message
})
return Promise.reject(error)
})
export default axiosInstance
api部分为
//src\constants\api.ts
// const host = process.env.NODE_ENV === 'production' ? '/api' : '/api'
const host = '/api'
export const loginApi = {
login: `${host}/user_login`,
loginOut: `${host}/user_logout`
}
export const listApi = {
getList: `${host}/list`
}
//src\constants\variableType.ts
export interface UserInfo {
username: string
password: string
}
export interface QueryInfo {
page_num?: number
per_page_num?: number
where_str?: object
}
export interface Result {
code: number | string
data: object
msg?: string
}
后记:写这个的过程中,公司关于住房补贴的通告出来了,我的眼皮子真浅,因为我羡慕这个住房补贴,我分心了。
讨论的时间大概为:4点到4点45。希望自己能够珍惜时间,好好写代码,不要再分心,应该全心全意的写代码。
【默默努力】vue-pc-app的更多相关文章
- vue打包app嵌入h5,区分app进入和android,ios显示不同的下载链接
vue打包app嵌入h5,区分app进入和android,ios显示不同的下载链接 需求:自己app打开的登录页面不显示app下载链接.其他地方打开判断android手机的跳转到android下载页链 ...
- vue隐藏APP启动时显示的{{}}
vue隐藏APP启动时显示的{{}} vue组件在编译好之前会显示{{msg}},在官网上找到这个
- ThinkPHP5+Apicloud+vue商城APP实战
ThinkPHP5+Apicoud+vue商城APP实战 目录 章节1:项目概述 课时1apicloud平台介绍.04:38 课时2知识体系架构介绍.16:10 章节2:apicloud50分钟快速入 ...
- Vue+原生App混合开发手记#1
项目的大致需求就是做一个App,里面集成各种功能供用户使用,其中涉及到很多Vue的使用方法,单独总结太麻烦,所以通过这几篇笔记来梳理一下.原型图如下: 路由配置 主界面会用到一些原生App方法,比如验 ...
- Vue PC端框架
Vue PC端框架 1. Element 中文文档:http://element-cn.eleme.io/#/zh-CN github地址:https://github.com/ElemeFE/ele ...
- Vue中app实例对象的几种写法
1.传统方法(练习 小DEMO中用的这种) <script type="text/ecmascript"> var app=new Vue({ el:"#ap ...
- 优秀开源平台,前后端分离快速开发平台,一站式多端开发(PC+APP)
JNPF平台架构介绍 JNPF快速开发平台采用前后端分离技术.采用B/S架构开发,形成一站式开发多端(APP+PC)使用. PC端版本介绍 第一个当然是当下热门的.net core了,运行环境为Vis ...
- pc/app 项目/功能设计
2015-08-11 15:29:59 首先, 提供服务的整个系统包含哪几个设备 用户->[PC浏览器|APP|APP浏览器]->web服务器->[PHP/JAVA组件]->[ ...
- 用vue做app内嵌页遇到的坑
公司要求用vue做一个微信端的网站,其中还包含一些app的内嵌页.开始的时候没想那么多,就直接用vue-cli搭了一个单页的vue项目,可随着项目越做越大,页面越来越多,问题就开始暴露出来了. 众所周 ...
随机推荐
- [JZOJ 5819] 大逃杀
题意:求一个树上背包~~ 先贴代码存一下,好像打挂了. #include <bits/stdc++.h> using namespace std; const int maxn = 400 ...
- word2vec中关于霍夫曼树的
再谈word2vec 标签: word2vec自然语言处理NLP深度学习语言模型 2014-05-28 17:17 16937人阅读 评论(7) 收藏 举报 分类: Felven在职场(86) ...
- Python正则表达式如何进行字符串替换实例
Python正则表达式如何进行字符串替换实例 Python正则表达式在使用中会经常应用到字符串替换的代码.有很多人都不知道如何解决这个问题,下面的代码就告诉你其实这个问题无比的简单,希望你有所收获. ...
- JVM内核-原理、诊断与优化学习笔记(六):类装载器
文章目录 class装载验证流程 class装载验证流程 class装载验证流程 -加载 class装载验证流程 -链接 验证 链接 -> 验证 文件格式的验证 元数据验证(class文件简单语 ...
- scanf 与getchar区别
#include<stdio.h> void main() { int c; c=getchar(); //scanf("%c",&c); if(c!=' ...
- JSON Web Token (JWT)生成Token及解密实战。
昨天讲解了JWT的介绍.应用场景.优点及注意事项等,今天来个JWT具体的使用实践吧. 从JWT官网支持的类库来看,jjwt是Java支持的算法中最全的,推荐使用,网址如下. https://githu ...
- Codeforces 479【A】div3试个水
题目链接:http://codeforces.com/problemset/problem/977/A 题意:这个题,题目就是让你根据他的规律玩嘛.末尾是0就除10,不是就-1. 题解:题解即题意. ...
- Spring注解基础学习总结
1.依赖注入注解: @Component:Bean注入到Spring容器组件(通用) @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE):Bean作用域( ...
- 2018-10-8-3分钟教你搭建-gitea-在-Centos-服务器
title author date CreateTime categories 3分钟教你搭建 gitea 在 Centos 服务器 lindexi 2018-10-08 09:54:39 +0800 ...
- tp5.0x代码执行
1.拿到站首先平复一下心情 看了一下robots.txt结构像dedecms,网站还存在CDN,日了狗看到这里其实都想放弃来着,懒癌晚期,然后接着使用云悉平台走了一波,看了一下得到真实IP,看来只给w ...