zoukankan      html  css  js  c++  java
  • 第三节:整体架构剖析、登录业务编写、零散知识汇总

    一. 整体架构剖析

    1. 目标

     该项目架构的搭建,侧重组件的抽取、封装、调用各个组件的相互通信侧重于一些核心方案的实现侧重于vue3+ts+elementPlus+vuex的使用

     忽略项目的UI、忽略一些基本的验证逻辑、忽略一些无关紧要的位置。

    2. 核心组件封装

    (1). breadcrumb:面包屑组件  →  nav-header:主页顶部栏目组件

    (2). nav-menu:左侧菜单组件

    (3). ypf-form:表单组件 → page-search:搜索框组件 → user.vue:用户管理页面

    (4). ypf-form: 表单组件 → page-modal:弹框组件 → user.vue:用户管理页面

    (5). ypf-table: 表格组件 → page-content:内容区域组件 → user.vue:用户管理页面

    解析:

     →的指向,代表左边的组件 被 右边组件 调用,最终在user.vue页面,只需要引入page-search、page-content、page-modal 三大组件,传入各个组件的相关配置,这三个组件之间可以实现互相调用,即可完成常规用户管理业务。

    3. 数据调用流程

    (1). 定义HYRequest类,封装axios实例

    (2). new一个HYRequest实例,hyRequest

    (3). 在Service文件夹下使用hyRequest调用,封装各个请求的方法(仅仅传入 url 和 参数,不处理结果)

    (4). 在Vuex中的state里声明存储各类数据

    (5). 在Vuex中的mutation里声明方法,用来给state里的属性赋值

    (6). 在Vuex中的action里 调用Service里封装的方法,拿到返回值结果,然后调用mutition里方法,给state里的属性赋值

    (7). 在各个具体的页面里(user.vue, role.vue),可以dispatch调用mutation里的方法,然后,同时也可以直接 store.state.xxx 拿 state 里的数据进行使用

     

    4.核心方案 

    (1). 动态路由权限配置

       详见:xxxxx

    (2). 按钮权限 

       详见:xxxx

    (3). 刷新页面仍然记住当前页面

      详见:xxxx

     

    二. 登录业务剖析

    1. 业务流程

     流程1:

      前端校验form表单中的值,校验通过 → dispatch调用vuex中的方法 → 调用Service封装登录方法 → 返回token存入缓存;同时Commit调用mutations中的方法→将token存入vuex中state属性中

     流程2:

      为了防止vuex中的数据刷新后丢失,这里封装1个方法,然后在main.ts中调用;这里的套路就是vuex中的数据第一次获取的时候也存入缓存一份,然后刷新页面的时候,从缓存中获取,然后给vuex中的数据赋值。【重点!!!vuex中的数据是在内存中,只要页面刷新了,vuex中的数据就消失了】

    loadLocalLogin({ commit }) {
        const token = cacheUtils.getCache('token');
        if (token) {
        commit('changeToken', token);
        }
    },

    2. 组件关系和用法

    (1). 组件关系 

      login-panel父组件中 调用 : login-account(帐户密码登录组件) 和 login-phone(手机号验证码登录组件)

    (2). tab组件用法

      核心是给el-tabs通过v-model绑定一个值,这个值则需要和子tab-panel中的name属性相关联,等于那个name属性,则哪个被选中。

    (3). form表单用法

        <div>
            <el-form label-width="80px" :model="myLoginInfo" :rules="myRules" ref="formRef">
                <el-form-item label="账号" prop="userAccount">
                    <el-input v-model="myLoginInfo.userAccount" @keyup.enter="checkLogin"></el-input>
                </el-form-item>
                <el-form-item label="密码" prop="userPwd">
                    <el-input v-model="myLoginInfo.userPwd" @keyup.enter="checkLogin"></el-input>
                </el-form-item>
            </el-form>
        </div>

    A. 绑定表单数据对象

     ef-form通过model属性绑定一个对象myLoginInfo,然后子元素input通过v-model绑定  myLoginInfo.xxx 属性。

        setup(props) {
                // 表单信息
                const myLoginInfo = reactive({
                    userAccount: '',
                    userPwd: '',
                });
                // form表单对象
                const formRef = ref<InstanceType<typeof ElForm>>();return {
                    myLoginInfo,
                    formRef,
                };
            },

    B. 校验规则绑定

     el-formrules属性绑定校验规则对象,然后每个el-form-item通过prop定义一个名称,注意prop定义的这个名称要和rules定义的属性名相关联,从而实现规则的校验(通常为了方便,就把prop里的名称和model里绑定的对象名称设置成相同即可了)

    // 编写好规则,名称必须和form表单中prop属性定义的名称相同
    export const myRules = {
        userAccount: [
            {
                required: true,
                message: '用户名是必传内容~',
                trigger: 'blur',
            },
            {
                pattern: /^[a-z0-9]{5,10}$/,
                message: '用户名必须是5~10个字母或者数字~',
                trigger: 'blur',
            },
        ],
        userPwd: [
            {
                required: true,
                message: '密码是必传内容~',
                trigger: 'blur',
            },
            {
                pattern: /^[a-z0-9]{3,}$/,
                message: '用户名必须是3位以上的字母或者数字~',
                trigger: 'blur',
            },
        ],
    };
    View Code

    C. 规则校验

     首先要给el-form表单通过 ref 属性绑定一个对象 formRef ,代码详见上面,然后通过这个对象调用validate方法进行校验,true表示校验通过。

        formRef.value?.validate((valid) => {
            if (valid) {
                           
            }
         });

    D. input回车触发

     使用 @keyup.enter 触发即可

        <el-input v-model="myLoginInfo.userAccount" @keyup.enter="checkLogin"></el-input>

    (4). 父组件调用子组件中返回

      在父组件中,首先通过ref属性给子组件绑定一个ref对象,然后通过这个对象 obj.value.xxx,来调用子组件的方法。

    <template>
        <div class="login-panel">
            <el-tabs type="border-card" v-model="currentTabName" stretch>
                <el-tab-pane name="account">            
                    <login-account ref="accountRef" :isKeepPwd="isKeepPassWord" />
                </el-tab-pane>
                <el-tab-pane name="phone">
                    <login-phone ref="phoneRef" />
                </el-tab-pane>
            </el-tabs>
        </div>
    </template>
    
    <script lang="ts">
        import { defineComponent, ref, onMounted } from 'vue';
        export default defineComponent({
            setup() {
                // 1.定义属性
                const accountRef = ref<InstanceType<typeof loginAccount>>(); //子组件对象
                const phoneRef = ref<InstanceType<typeof loginPhone>>(); //子组件对象
    
                // 2.定义方法
                const handleLoginClick = () => {
                    console.log(isKeepPassWord.value, currentTabName.value);
                    // 调用子组件方法
                    if (currentTabName.value === 'account') {
                        accountRef.value?.checkLogin(isKeepPassWord.value);
                    } else {
                        phoneRef.value?.checkLogin();
                    }
                };
                return {
                    accountRef,
                    phoneRef,
                };
            },
        });
    </script>
    View Code

    三. 零散知识汇总

    1. defineComponent作用

      类型推导  和 类型限制 的作用

    2. 如何绑定一个组件对象(ts写法)?

     以PageModel组件为例,使用 InstanceType<typeof PageContent>来获取组件类型,最后用ref来包裹。
    <template>
        <page-modal ref="pageModalRef"></page-modal>
    </template>
    <script lang="ts">
        import { defineComponent, ref } from 'vue';
        import PageModal from '@/components/page-modal/src/page-modal.vue';
        export default defineComponent({
            components: {
                PageModal,
            },
            setup() {
                const pageContentRef = ref<InstanceType<typeof PageContent>>();
              return {
                    pageModalRef,
                };
            },
        });
    </script>

    3.  json转换成ts类或接口

     利用工具网站:http://www.json2ts.com/ 

    (1). json代码:

    { 
    "programmers": [
    { "firstName": "Brett", "lastName":"McLaughlin", "email": "aaaa" },
    { "firstName": "Jason", "lastName":"Hunter", "email": "bbbb" },
    ],
    "authors": [
    { "firstName": "Isaac", "lastName": "Asimov", "genre": "science fiction" },
    { "firstName": "Tad", "lastName": "Williams", "genre": "fantasy" },
    ],
    "musicians": [
    { "firstName": "Eric", "lastName": "Clapton", "instrument": "guitar" },
    { "firstName": "Sergei", "lastName": "Rachmaninoff", "instrument": "piano" }
    ] }
    View Code

    (2). 转换后ts接口、类

    declare module namespace {
        export interface Programmer {
            firstName: string;
            lastName: string;
            email: string;
        }
        export interface Author {
            firstName: string;
            lastName: string;
            genre: string;
        }
        export interface Musician {
            firstName: string;
            lastName: string;
            instrument: string;
        }
        export interface RootObject {
            programmers: Programmer[];
            authors: Author[];
            musicians: Musician[];
        }
    }
    View Code

    4.  代理的配置

     默认开发环境下是不支持跨域请求的,所有要配置代理。 

    (1). vue.config.js

       在devServer配置proxy属性,如下代码,表示凡是以 /api 开头的请求地址,都会改为以 http://47.92.156.224:5000,比如:请求地址的 url= /api/Login/CheckLogin , 则转换为 url=http://47.92.156.224:5000/Login/CheckLogin

        devServer: {
            proxy: {
                '^/api': {
                    target: 'http://47.92.156.224:5000',
                    pathRewrite: {
                        '^/api': '',
                    },
                    changeOrigin: true,
                },
            },
            port: 8080,
            open: false,
        },

    (2). 请求接口的配置

     以axios为例,通常的套路为参数baseUrl设置为 /api (这里的/api也不是直接写死哦,而是写在配置文件中,即开发环境下才是/api),则下面的请求为直接写后面的地址即可。   

    A. 设置BaseUrl

    const service = axios.create({
      baseURL: process.env.VUE_APP_BASE_API,
      timeout: 5000
    })

    B. 配置不同环境下的BaseUrl值

    .env.development

    # base api
    VUE_APP_BASE_API = '/api'

    .env.production

    # base api
    VUE_APP_BASE_API = 'http://xxx生产环境地址'

    C. 实际请求

    export const getArticleList = data => {
      return request({
        url: '/article/list',
        params: data
      })
    }

    5. globalProperties全局属性的使用

    (1). 在main.ts中声明全局属性。下面$filters就是全局属性

    app.config.globalProperties.$filters = {
            // 1.时间转换
            formatTime(val: string) {
                return formatUtcString(val);
            },
    };

    (2). 配置相应的类型(js的不需要配置)

    setup中使用需要在当前页面或者main.ts中配置

    declare module '@vue/runtime-core' {
        interface ComponentCustomProperties {
            $filters: any;
        }
    }

    <template>中使用需要在xx.d.ts中配置

    declare let $filters: any;

    (3). setup中使用

      import { getCurrentInstance } from 'vue';
      const { proxy } = getCurrentInstance();
      console.log(proxy.$filters.formatTime('2021-08-20T04:07:23.000Z'));

    (4). template中使用

    <span style="margin-left: 5px">{{ $filters.formatTime(scope.row.createAt) }}</span>

    6.  { ...item } 的用法

    (1). 用法说明-浅拷贝

     下面表示把pageInfo对象浅拷贝了一份给obj1对象,则: obj1={ pageSize : 10, pageIndex : 1 }
    let pageInfo = {
            pageSize: 10,
             pageIndex: 1,
    };
    let obj1 = { ...pageInfo };

    (2). 补充1个语法糖 -简化写法

      下面的obj2,obj3,obj4的结果都是  { pageSize:20 }

                let pageSize = 20;
                let obj2 = { pageSize: pageSize };
                let obj3 = { pageSize: 20 };
                // 下面是简化写法
                let obj4 = { pageSize };

    (3). 自动合并 

      下面obj5和obj6都是合并后的结果

                let pageInfo = {
                    pageSize: 10,
                    pageIndex: 1,
                };    
           let pageIndex = 2;
                let obj5 = { ...pageInfo, pageSize: 50 }; //{pageSize:50,pageIndex:1}
                let obj6 = { ...pageInfo, pageIndex }; //{pageSize:10,pageIndex:2}

    7. template中使用别名 

      需要加个 ~ 符号 

     <img class="img" src="~@/assets/img/logo.svg" />

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    css表格单元格间距设置
    JavaScript(js)设置输入焦点(focus)
    让div居中的方法
    Window.open()的使用
    getElementsByTagName的用法
    offsetTop获取top值
    js中indexof的使用
    jquery解析json数据
    iframe的使用
    WCF学习笔记Ⅲ
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/15597631.html
Copyright © 2011-2022 走看看