zoukankan      html  css  js  c++  java
  • 【默默努力】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
    outerindex.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%;
       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;
         340px;
        background-color: #fff;
        margin: 0 auto;
        border-radius: 5px;
        font-size: 14px;
      }
      .login-btn {
        height: 40px;
      }
      .copy-right {
        position: absolute;
        bottom: -32px;
         100%;
        color: #2b3137;
      }
    }
    </style>
    
    

    接下来进入home页面
    home页面应该指的是这部分

    代码为

    //srccomponentsHome.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 {
       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;
         100%;
        position: absolute;
        left: 0;
        bottom: 20px;
      }
    }
    </style>
    

    Home页面中会用到base组件

    //base.vue
    <template>
      <div class="m-layout">
        <Layout>
          <Header :style="{  '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 {
         198px;
        height: 30px;
        float: left;
        position: relative;
        top: 10px;
        left: 0;
        .logo {
           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代码为

    //srcinterceptorsaxios.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部分为

    //srcconstantsapi.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`
    }
    
    //srcconstantsvariableType.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。希望自己能够珍惜时间,好好写代码,不要再分心,应该全心全意的写代码。

  • 相关阅读:
    Java学习笔记-Lambda表达式
    Java学习笔记-枚举类
    Java学习笔记-枚举类
    Java学习笔记-包装类
    js 递归 汉诺塔的例子
    js 用 hasOwnProperty() 判定属性是来自该对象成员,还是原型链
    正则,js函数math()提取混乱字符串中多个字符串内容
    封装好的cookie的三个常用函数 cookie的添加、删除、提取操作函数
    解决ie6下png背景不能透明bug
    ie6下标签定义的高失效,显示的高不受设定的height值影响
  • 原文地址:https://www.cnblogs.com/smart-girl/p/11430680.html
Copyright © 2011-2022 走看看