#前端# 万字总结!前端整个项目超详细方法及思路!

文章目录

第一部分:web/pc端

一、项目开始前的准备工作

1、vue-cli搭建开发环境

跳转直接查看 ===> vue-cli搭建开发环境步骤

2、新建vue项目文件

跳转直接查看 ===>新建vue项目文件方法及步骤

3、vue项目目录解析

(1)vue项目目录全部版块解析

vue整个目录截图:
在这里插入图片描述
以下为目录各个版块的解析:

1、build:构建脚本目录
在这里插入图片描述

1)build.js ==> 生产环境构建脚本;

2)check-versions.js ==> 检查npm,node.js版本;

3)utils.js ==> 构建相关工具方法;

4)vue-loader.conf.js ==> 配置了css加载器以及编译css之后自动添加前缀;

5)webpack.base.conf.js ==> webpack基本配置;

6)webpack.dev.conf.js ==> webpack开发环境配置;

7)webpack.prod.conf.js ==> webpack生产环境配置;

2、config:项目配置

在这里插入图片描述
1)dev.env.js ==> 开发环境变量;
在这里插入图片描述
2)index.js ==> 项目配置文件;
在这里插入图片描述
具体可查看#vue# 本地电脑如何配置host文件?

3)prod.env.js ==> 生产环境变量;

3、node_modules:npm 加载的项目依赖模块

4、src:这里是我们要开发的目录,基本上要做的事情都在这个目录里。
里面包含了几个目录及文件:

1)assets:资源目录,放置一些图片或者公共js、公共css。
这里的资源会被webpack构建;

2)components:组件目录,我们写的组件就放在这个目录里面;

3)router:前端路由,我们需要配置的路由路径写在index.js里面;

4)App.vue:根组件;

5)main.js:入口js文件;

5、static:静态资源目录,如图片、字体等。不会被webpack构建

6、index.html:首页入口文件,可以添加一些 meta 信息等

7、package.json:npm包配置文件,定义了项目的npm脚本,依赖包等信息

8、README.md:项目的说明文档,markdown 格式

9、.xxxx文件:这些是一些配置文件,包括语法配置,git配置等

(2)vue项目常用版块解析及书写

项目目录常用版块截图:在这里插入图片描述
1)src 版块:

  • src ==> assets (内部配置) ==> css**(需要自己自行配置)
    在这里插入图片描述

  • src ==> assets (内部配置) ==> js**(需要自己自行配置)
    在这里插入图片描述

  • src ==> components (组件、可复用) ==> 组件文件夹**(需要自己自行配置)
    在这里插入图片描述

  • src ==> page(页面) ==> 组件文件夹**(需要自己自行配置)
    在这里插入图片描述

  • src ==> router ==> index

  • src ==> store ==> App.vue/main.js
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3、git使用

(1)git安装使用

git的使用具体可查看:
#git#(代码管理工具)通俗易懂的用法
#git# 如何在git里创建一个项目?

(2)git 常用指令

在这里插入图片描述

- 本地仓库:是在开发人员自己电脑上的Git仓库,存放我们的代码
(.git 隐藏文件夹就是我们的本地仓库)

  • 远程仓库:是在远程服务器上的Git仓库,存放代码
    (可以是github.com或者gitee.com 上的仓库,或者自己该公司的服务器)

  • 工作区: 我们自己写代码(文档)的地方

  • 暂存区: 在本地仓库中的一个特殊的文件(index) 叫做暂存区,
    临时存储我们即将要提交的文件

  • Clone:克隆,就是将远程仓库复制到本地仓库

  • Push:推送,就是将本地仓库代码上传到远程仓库

  • Pull:拉取,就是将远程仓库代码下载到本地仓库,并将代码 克隆到本地工作区

(3)使用注意点

3-1)如何通过git clone下载我们需要的代码?

情况:在远程仓库直接复制链接,一般是默认master分支,以及git clone +复制的地址,都是默认master, 但是我们提交的代码,一般是在其他分支,如果没有合并到master的话,master里面是没有东西的 所以我们在下载的时候,切记要进行切换到我们需要的那个分支里面
(通俗理解就是,我们的远程仓库相当于于一个放满零食的仓库,而我们就相当于整理零食的仓库整理员,那每个人都会负责相应的零食区域,而我们在只想要我们这个区域的零食,那当然也是把我们负责的这个区域的零食拿出来即可,如果想要别人的零食区域的东西,可以合并别人的东西,即系合并的别人的分支)
具体步骤如下:
(1) 在远程仓库里面,进行复制地址

(2)打开想要放置的文件夹里面,右键Git Bash Here
然后输入git clone -b +分支名 +远程仓库的地址
在这里插入图片描述
(3)完成上面两步,就可以拿到项目的代码了,
在编辑器打开,记得先npm install一下就可以了

  • 1.1 环境配置
    当安装Git后首先要做的事情是设置用户名称和email地址
    这是非常重要的,因为每次Git提交都会使用该用户信息

#设置用户信息 
   git config --global user.name “itcast”
   git config --global user.email “itcast@itcast.cn”
 
#查看配置信息
   git config --list
   git config user.name
   
#通过上面的命令设置的信息会保存在~/.gitconfig文件中

- 1.2 初始化本地仓库 init


# 初始化仓库带工作区
git init

# 初始化仓库不带工作区
git init --bare

- 1.3 克隆 clone


# 从远程仓库克隆
git clone 远程Git仓库地址 
例如: git clone https://gitee.com/itcast/gittest.git (shift+Ins)

-1.4 查看状态 status


# 查看状态
git status 

#查看状态 使输出信息更加简洁
git status –s 

- 1.5 add


# 将未跟踪的文件加入暂存区
git add  <文件名>  

# 将暂存区的文件取消暂存 (取消 add )
git reset  <文件名>  

- 1.6 commit


# git commit 将暂存区的文件修改提交到本地仓库
git commit -m "日志信息"  <文件名>  

- 1.7 从远程仓库获取代码


# 从远程仓库克隆
git clone <url> 

# 从远程仓库拉取 (拉取到.git 目录,不会合并到工作区,工作区发生变化)
git fetch  <shortname>  <分支名称>

# 手动合并  把某个版本的某个分支合并到当前工作区
git merge <shortname>/<分支名称>

# 从远程仓库拉取 (拉取到.git 目录,合并到工作区,工作区不发生变化) = fetch+merge
git pull  <shortname>  <分支名称>
git pull  <shortname>  <分支名称>  --allow-unrelated-histories  #  强制拉取合并

- 1.8 命令行-- 分支


# 默认 分支名称为 master
# 列出所有本地分支
git branch

# 列出所有远程分支
git branch -r

# 列出所有本地分支和远程分支
git branch -a

# 创建分支
git branch <分支名>

# 切换分支 
git checkout <分支名>

# 删除分支(如果分支已经修改过,则不允许删除)
git branch -d  <分支名>

# 强制删除分支
git branch -D  <分支名>

# 提交分支至远程仓库
git push <仓库简称> <分支名称> 
 
# 合并分支 将其他分支合并至当前工作区
git merge <分支名称>

# 删除远程仓库分支
git push origin –d branchName

二、项目进行时(最开始)

1、底层搭建

(1)配备全局的css/js(src-assets)

(2)配备http(src-assets/js)

2、设置全局文档

(1)App.vue

在这里插入图片描述

(2)router路由的配备

在这里插入图片描述
在这里插入图片描述

(3)组件的使用(父子组件)

%: 子组件向父组件传值

组件传递过程:组件传值模板
也可以说这部分的内容是将组件的传递过程的关键代码给抽离出来了,
帮助大家更好的理解如何使用传值!
在这里插入图片描述

子—>父
①属性emit

子组件:

1.利用插值表达式显示传递过去的数据:

<span>{{子组件数据}}</span>

2.将变量定义到data中

export default {

  data() {

    return {

      子组件数据: "子组件的数据"

    };

  },

}

3.点击事件进行传值,在template中添加组件:

<button @click="ok">OK</button>

4.向父组件传值,在methods下定义事件:

methods: {

    ok() {

      this.$emit("自定义事件", this.子组件数据);

    }

  }

};

父组件:

侦听自定义事件

1.利用插值表达式,将子组件传递过来的数据显示出来

<span>{{插值表达式}}</span>

2.将变量自定义到data中

export default {

data() {

    return {

      插值表达式: ""

    };

  },

};

3.父组件自定义方法侦听子组件传过来的值:

<子组件名称 @自定义事件="绑定的方法"></子组名称>

4.引用子组件:

import 子组件名称 from "子组件路径";

5.注册子组件,注册位置与methods同级:

components: {

    子组件名称

  },

6.在methods中侦听子组件传过来的值:

methods: {

    自定义事件(子组件数据) {

      this.插值表达式 = 子组件数据;

    }

  }

代码如下
父组件:

<template>
    <div class="header">

        <span>{{parseintMessage}}</span>
        <child @slist="plist"></child>


    </div>
</template>

<script>
import Child from './page/child'
export default {
    name: 'header',
    components: {Child},
    data() {
        return {
            parseintMessage:''
        }
    },
    methods: {
        plist(title) {
            this.parseintMessage = title;
        }

    }
}
</script>

<style scoped>

</style>

子组件:

<template>
    <span>{{插值表达式}}</span>
    <button @click="ok">点击</button>
</template>

<script>
export default {
    name: 'child',
    data() {
        return {
            title:'我是子组件里面的span标签',
        }
    },
    methods: {
        ok() {
            this.$emit("slist",this.title)
        }
    }
}
</script>

<style scoped>

</style>

%:父组件向子组件传值

点击查看教程>>>#vue# 【十三】 组件传值之父传子(超级通俗简单的思路!)

属性props
子组件利用props接收父组件传递过来的数据

指的是从外部设置的属性,需子组件设置props属性

注意:

props严格用于父组件与子组件之间的单向通讯,并且你不希望尝试直接在子组件中更改props的值。

否则,将收到类似这样的错误信息“避免直接修改某个prop,因为当父组件重新渲染时,该值将被覆盖” 这样的错误。

父组件:

1.点击事件进行传值,在template中添加组件

<button @click=“OK”>给子组件发送一个消息

2.父组件自定义方法将data里的数据传递过去:

<子组件名称 :自定义事件=“绑定的方法”></子组名称>

data() {
    return {
      自定义事件: ""
    };
  },

3.引用子组件:

import 子组件名称 from “子组件路径”;

4.注册子组件,注册位置与methods同级:

components: {
    子组件名称
  },

5.在methods中写入点击事件

methods: {
    OK() {
     this.自定义事件 = "传递过去的数据";
    }
  }

子组件:

1.定义个插糟,接收渲染传递过来的数据

<div v-html="自定义事件"></div>

1.接收父组件传递过来的值

<script>

export default {

  props: ["自定义事件"],

};

三、项目进行时(开始排版)

1、文件标准化设置

(1)文件存放位置

具体查看第一大点的第三版块

(2)文件命名

具体查看第一大点的第三版块

(3)项目/代码规范

具体可见前端项目/代码规范大全

2、排版常见问题

(1)页面组件

1-1)头部 header

头部是固定不变的,一般由三个部分组成:图标、导航栏、登录栏
一些需要注意的点:
(1)图标最好用一个div进行包裹,然后设置宽度高度为100%(有圆角的,也需要加上圆角 )
(2)导航栏可以自己手写,也可以用element里面的组件
点击跳转到element里面的导航栏
(3)尽量使用flex布局,不要滥用浮动
(4)思路:这3个部分可以都各种用一个div进行包裹,再在里面进行细分排版出来

示例demp

<template>
    <div class="header-box learn-box">
        <div class="header-flex">
            <div class="header-title">
                <span class="header-title-hn">· 大学生</span>
                <span>计算机</span>
                <span>前端项目</span>
            </div>
        </div>
        <div class="header-nav content-wrap-component">
           <router-link to="/">
               <div class="logo">
                   <img src="/static/image/logo1.png" alt="">
               </div>
           </router-link>
            <div class="nav">
                <el-menu  class="menu-links" mode="horizontal" @select="handleSelect">
                    <el-menu-item index="1">
                        <router-link to="/" exact>
                        首页
                        </router-link>
                    </el-menu-item>
                    <el-menu-item index="2" exact>
                        <router-link to="/history">
                        大学学习
                        </router-link>
                    </el-menu-item>
                    <el-submenu index="3" class="Join-party">
                        <template slot="title" >直播学习</template>
                        <el-menu-item index="3-1" 
                        align="center" 
                        style="font-size: .16rem"
                         @click="handleClick(1)" 
                         class="dropdown">百度翻译</el-menu-item>
                        <el-menu-item index="3-2" 
                        align="center" 
                        style="font-size: .16rem"
                         @click="handleClick(2)">文章管理</el-menu-item>
                        <el-menu-item index="3-3" 
                        align="center" 
                        style="font-size: .16rem" 
                        @click="handleClick(3)">百度应用</el-menu-item>
                    </el-submenu>
                    <el-submenu index="2" v-if="getUserInfo" popper-class="person-submenu" >
                        <template slot="title">
                            <span>|</span>
                            <div class="logon-img">
                                <img :src="getUserInfo.avatar" :alt="getUserInfo.real_name">
                            </div>
                            <span class="name">{{getUserInfo.real_name}}</span>
                        </template>
                        <router-link to="/user" >
                        <el-menu-item index="2-1" align="center" style="display: block" >个人信息</el-menu-item>
                        </router-link>
                        <el-menu-item index="2-1"  align="center"  style="display: block">
                            <a :href="loginOut" >退出</a>
                        </el-menu-item>
                    </el-submenu>
                    <el-menu-item index="2" v-else>
                        <div class="person-no-login">
                            <button @click="toLogin">登录 / 注册</button>
                        </div>
                    </el-menu-item>
                </el-menu>
            </div>
        </div>
    </div>
</template>
<script>
import { mapGetters } from 'vuex'
import { BASE_LOGIN } from '@/config'
export default {
    name: 'header-box',
    data () {
        return {
            list: [],
            activeIndex: '1',
            stage : parseInt(this.$route.params.stage),
            BASE_LOGIN,
            loginOut:BASE_LOGIN + '/out'
        }
    },
    computed: {
        ...mapGetters(['getUserInfo'])
    },
    methods: {
        handleSelect(key, keyPath) {
            console.log(key, keyPath);
        },
        handleClick(stage) {
            this.$router.push(`/join/${stage}`)
        },
        toLogin() {
            window.location.href = BASE_LOGIN
        }
    }
 }
</script>

<style scoped lang="scss">
.header-box {
    width: 100%;
    height: 1.12rem;
    background-color: #fff;
}
.header-flex {
    width: 100%;
    height: .36rem;
    background: #F7F7F7;

}
.header-title {
    width: 8rem;
    height: .36rem;
    margin: 0 auto;
}

.header-title span {
    width: 2.56rem;
    font-size: .16rem;
    font-weight: 400;
    color: #797979;
    height: .36rem;
    line-height: .36rem;
    margin-left: .24rem;
    font-family: SourceHanSansSC-Normal, SourceHanSansSC;
}

.header-title .header-title-hn {
    margin-left: .43rem;

}

.header-nav {
    /* width: 1320px; */
    height: .76rem;
    background: #FFFFFF;
    margin: 0 auto;
}

.el-menu--popup{
    min-width: 1.36rem;
}

.header-nav .logo {
    margin: auto;
    width: 3.2rem;
    height:.76rem;
    line-height: .76rem;
    float: left;
}

.header-nav .logo img {
    vertical-align: middle;
    display: inline-block;
    width: 3rem;
}

.header-nav .nav{
    position: relative;
    float: right;
    height: .76rem;
}

.header-nav .nav .el-menu-item {
    position: relative;
    float: left;
    line-height: .76rem;
    padding-right: .3rem;
    padding-left: 0;
    font-size: .16rem;
    font-weight: 400;
    color: #333333;
    a{
        position: relative;
        display: inline-block;
        width: 100%;
        line-height: .76rem;
        margin-top: -.01rem;
        text-decoration: none;
        &::after {
            content: '';
            position: absolute;
            bottom: .2rem;
            left: 0;
            width: 100%;
            height: .02rem;
            background-color: $theme-color;
            transform: scaleX(0);
            transition: .3s transform cubic-bezier(0, 0, 0.2, 1);
        }
        &.router-link-active,
        &:hover {
            font-weight: 400;
            color: $theme-color;
            &::after {
                transform: scaleX(1);
            }
        }
    }
}
.header-nav .nav .nav-ul li:last-child {
   padding-right: 0;
}
.header-nav .nav .person-no-login  button {
    width: 1.1rem;
    height: .4rem;
    line-height: .4rem;
    margin-top: -.03rem;
    color: #FFFFFF;
    border-radius: .05rem;
    font-size: .16rem;
    background-color: #DA1A14;
}
.header-nav .nav .person-no-login button:hover{
    color: #fff;
    background-color:#C72121;
    cursor: pointer;
}

.header-nav .nav .name {
    width: .48rem;
    font-size: .16rem;
    font-weight: 500;
    color: #333333;
}
.header-nav .nav .logon-img {
    vertical-align: middle;
    display: inline-block;
    width: .36rem;
    height: .36rem;
}
.header-nav .nav .logon-img img{
    vertical-align: top;
    width: 100%;
    height: 100%;
    border-radius:50%;
}

.menu-links .el-menu--horizontal .el-menu {
    height: .76rem;

}
.iconlujing1 {
    width: .34rem;
    height: .34rem;
    background: #DA1A14;
}
a {
    text-decoration: none;
    color: #333;
}
</style>


<style>
.learn-box .nav .menu-links {
    height: .76rem;
    border-bottom: none;
}
.learn-box .nav .menu-links .el-menu-item  {
    font-size: .16rem;
    height: .76rem;
    line-height: .76rem;
}

.el-menu--horizontal > .el-menu-item.is-active,.el-menu--horizontal > .el-menu-item {
    font-size: .16rem;
    font-weight: 400;
    color: #333333;
    border: none;

}
.el-menu--horizontal>.el-submenu .el-submenu__title,
.el-menu--horizontal>.el-menu-item,
.el-menu--horizontal>.el-submenu .el-submenu__title ,
.el-menu--horizontal>.el-submenu.is-active .el-submenu__title  {
    border: 0 !important;
}
.learn-box .el-menu--horizontal > .el-submenu .el-submenu__title {
    height: .76rem;
    line-height: .76rem;
    font-size: .16rem;
    padding-left: 0;
    font-weight: 400;
    color: #333333;
}
.learn-box  .content-wrap-component .nav .el-menu--horizontal .el-menu .el-menu-item {
    font-size: .18rem;
    background-color: #DA1A14;
}
.person-submenu .el-menu{
    min-width: 1.36rem;
    font-size: 0.16rem;
}
.person-submenu .el-menu a{
    display: block;
}
.person-no-login {
    margin-left: .12rem;
}

.Join-party {
    padding-right: .11rem;
}
</style>


在这里插入图片描述

1-1)导航栏 nav

(1)作用:通过导航栏可以达到切换页面的效果,只需要在每一个导航里面,加入路由
(2)一般是需要设置宽度为13rem,margin: 0 auto; 达到在网页居中效果
(3)组件:可以自己手写也可以用element点击跳转到element的导航栏

demo示例

    <div class="nav">
       <el-menu  class="menu-links" mode="horizontal" @select="handleSelect">
           <el-menu-item index="1">
               <router-link to="/" exact>
                 首页
               </router-link>
           </el-menu-item>
          <el-menu-item index="2" exact>
              <router-link to="/history"> //通过路由跳转到相应的页面,to="/具体地址"
                网课学习
             </router-link>
          </el-menu-item>
            <el-submenu index="3" class="Join-party">
               <template slot="title" >项目学习</template>
                 <el-menu-item  align="center" @click="handleClick(1)" class="dropdown">css学习</el-menu-item>
                 <el-menu-item  align="center"  @click="handleClick(2)">js学习</el-menu-item>
                <el-menu-item  align="center"  @click="handleClick(3)">vue学习</el-menu-item>
        </div>

1-2)底部栏 footer

(1)底部栏:一般都是一些跳转链接、友情链接、图标二维码等等
(2)一般是需要设置宽度为13rem,margin: 0 auto; 达到在网页居中效果
(3)跟头部栏一样,也是可以先自己划分为几个部分,然后再在里面进行细分版块
(4)注意点:
用得比较多的是无序列表以及自定义列表,并且因为都是统一的样式,使用可以使用同一个类名;
a标签也是常用的,一般都是点击跳转到新页面,所以设置target="_blank",在新窗口打开链接;
注意不要轻易就将高度或者宽度写死,同时尽量使用flex进行布局,少用浮动
示例效果:
在这里插入图片描述

1-3)轮播图 banner

可以直接使用element的Carousel 走马灯

1-4)侧边栏 sidebar

可直接使用element的NavMenu 导航菜单

1-5)tab栏 (当前 态)

#vue#如何从一个页面跳转到另一个页面的指定tab选项卡?

(2)常见小组件(可复用)

2-1)个人登录页面-全部

在这里插入图片描述
思路:
(1)将个人中心页面分为两大部分,左侧的信息导航栏以及右侧的个人信息栏
(2)左侧导航栏以及右侧个人信息栏通过flex进行布局
(3)然后开始做左侧导航栏,分为3大部分,图片,昵称以及红色板块,通过div进行排列就可以了
(4)右侧个人信息栏,包括“个人信息”标题以及下面6个修改信息的位置
个人信息栏用一个大的div进行包裹即可
考虑到下面6个修改信息的内容过多,所以最好是通过组件的方式进行书写(此外因为这6个内容板块有很多共同的样式,所以可以设置一个共同的类名,可以进行共用,再用一个新的类名写各自不相同的那些部分)
(5)里面还有涉及到网络请求接口,以及使用到vue的事件,v-show、v-model、点击事件、三元表达式等一系列常用的方法事件
(6)里面的细分可以看接下来的各个板块的解析

2-2)个人登录页面-左侧导航栏

在这里插入图片描述
demo代码如下:


<template>
    <div class="user-center-content">
        <div class="user-content-nav">
            <div class="user-img">
                <img src="/static/image/use.jpg" alt="">
            </div>
            <p class="user-name">楚君君</p>
            <div class="user-nav">
                <p class="user-meg">个人信息</p>
            </div>
        </div>
        <div class="user-content"></div>
    </div>

</template>

<script>

export default {
    data() {
        return {

        }
    },
    methods: {

        },
}
</script>

<style scoped>
.user-center-content {
    display: flex;
    /*用flex布局*/
    width: 13rem;
    margin: .5rem auto;
    /*这个起到在页面居中的效果,距离底部有50个像素的高度*/
}
.user-center-content .user-content-nav {
    width: 2.04rem;
    height: 2.54rem;
    background: #FFFFFF;
    border-radius: .05rem;
    margin-right: .24rem;
}
    /*以下为个人中心的左侧导航栏板块*/
.user-center-content .user-content {
    width: 10.72rem;
    background: #FFFFFF;
    border-radius: .05rem;
    /*右侧个人信息栏,不要定高,靠后期的内容来填充,动态高度即可*/
}

.user-content-nav .user-img {
    width: .95rem;
    height: .95rem;
    border-radius: 50%;
    /*border-radius为50%,可以实现把一个正方形变成圆形的效果*/
    background-color: #FECC8F;
    margin: .28rem .55rem .16rem .55rem;
}
.user-content-nav .user-img img {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    /*图片设置高度宽度为父盒子的100%,同时不要忘记加入圆角border-radius: 50%*/
}
.user-content-nav .user-name {
    height: .24rem;
    font-size: .16rem;
    font-family: SourceHanSansSC-Regular, SourceHanSansSC;
    font-weight: 400;
    color: #333333;
    line-height: .24rem;
    text-align: center;
    /*设置行间距=高度,text-align: center,可以实现p 在盒子里面,垂直居中*/
}
.user-content-nav .user-nav {
    margin-top: .2rem;
    width: 2rem;
    /*//宽度需要减掉.04rem ,因为增加了.04rem左边框*/
    height: .51rem;
    background: #FDEDEE;
    border-left: .04rem solid #7EA8E3;
}

.user-content-nav .user-nav .user-meg {
    font-size: .16rem;
    font-family: SourceHanSansSC-Medium, SourceHanSansSC;
    font-weight: 500;
    color: #E60113;
    line-height: .51rem;
    padding-left: .22rem;
    /*盒子内部的文字移动,用padding,盒子与盒子的移动,用margin*/
}

</style>

2-3)个人登录页面-右侧信息栏-修改昵称

(也适用于一些其他类型的)
需求:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路:
我们首先要把这个demo分成两个部分:点击前以及点击后
再通过绑定鼠标点击事件,在相应的位置,j加入v-show,进行隐藏或者显示
第一步:我们要先将点击前以及点击后的代码进行书写
第二步:先将点击后的部分进行隐藏
第三步:设置点击事件(取消&保存),控制盒子的显示以及隐藏、及弹出的内容
第四步:设置互相绑定的数据
第五步:接入网络数据接口

HTML页面代码:

<template>
    <div class="content-item"> //注意命名,一定要有一个大的盒子包住
        <h1 class="item-title">账号信息</h1>
        <ul class="user-info-list">
            <li class="info-item" >
                <div class="info-value" >姓名</div>
                 // v-show 控制隐藏或者显示,没有做任何操作之前,以下这个盒子是显示
                <div class="info-disable" v-show="onShow">
                    <p class="disable-text">郑三郑</p>
                    <button class="btn-modify" @click="InputShow()">修改</button> 加入点击事件
                </div>
                // v-show 控制隐藏或者显示,没有做任何操作之前,以下这个盒子是显示
                <div class="info-item-modify" v-show="offShow">
                    <input type="text" value="郑三郑" >
                    <button class="btn-cancel" @click="ShowName()">取 消</button> 加入点击事件
                    <button class="btn-keep" @click="UpdateName()">保 存</button> 加入点击事件
                </div>
            </li>
        </ul>
    </div>
</template>

js页面代码:

<script>
export default {
    name: "nickname",
    data() {
        return {
            onShow:'true',
            offShow:''  //在return这里,首先设置没有任何操作之前,盒子的隐藏以及显示
            //data/return 里面的写法是  变量:''
        }
    },
    methods:{
        InputShow() {
            this.onShow = false;
            this.offShow = true;  //在方法里面写入触发点击事件以后,盒子的显示以及隐藏
            //methods里面的写法是 
            事件名() { 
            this.变量 = xxx
            }
        },
        ShowName() {
            this.onShow =  true;
            this.offShow = false;
        },
        UpdateName() {
            this.$message({
                message: '恭喜你,保存成功!', //设置点击保存以后会有消息提示的弹出框
                type: 'success'
            });
            this.onShow =  true;
            this.offShow = false;
        }

    }

}
</script>

css页面代码:

<style scoped>

.content-item {
    width: 13rem; //使用rem ,可以自适应页面的缩放,不要定高
    margin: .3rem auto;
    border-radius: .15rem; //圆角设置为上下左右都是.15rem
    background-color: #FFFFFF;
}

.item-title {
    width: 10%; //使用百分比,可以让宽度更灵活
    color: #3b3b3b;
    font-size: .2rem;
    font-family: SourceHanSansSC-Regular, SourceHanSansSC;  //这个字体需要加上,避免出现字体错乱
    text-align: right;
}
.user-info-list {
    width: 100%;
    color: #6D6D6D;
}

.user-info-list .info-item {
    width: 100%;
    padding-top: .15rem;
}

.user-info-list .info-item .info-value {
    display: inline-block;
    width: 10%;
    height: .4rem;
    line-height: .4rem;
    font-size: .16rem;
    font-weight: 700; //字体粗细,400-700 ,不用加单位
    text-align: right; // 文字居右
}
.user-info-list .info-item .info-disable {
    display: inline-block;  //切换为行内块元素,可以在同一行显示,同时尽量少用浮动float
    width: 90%;
    height: .4rem;
}
.user-info-list .info-item .info-disable .disable-text {
    display: inline-block;
    line-height: .4rem;  //行高,行高设置为与高度一致,可以让文字居中
    font-size: .16rem;
    font-weight: 500;
    text-align: left;
    padding-left: .5rem;
}
.user-info-list .info-item .info-disable .btn-modify {
    line-height: .4rem;
    font-size: .16rem;
    font-weight: 500;
    text-align: left;
    padding-left: .5rem;
    color: #DA1A14;
    border: 0; 去掉按钮的默认的边框
    background-color: transparent; 去掉按钮button的默认背景颜色
    outline: none; 当元素获得焦点的时候,焦点框为0.
}
.user-info-list .info-item-modify {
    display: inline-block;
    width: 40%;
    border-color: #3BB9FF;
}
.user-info-list .info-item-modify input {
    width: 40%;
    height: .4rem;
    margin-left: .5rem;
    border-radius: .05rem;
    border: .01rem solid #6D6D6D;
    font-size: .16rem;
    font-family: SourceHanSansSC-Regular, SourceHanSansSC;
    color: #6D6D6D;
    background-color: #FAFAFA;
    outline: none;
}
.user-info-list .info-item-modify input:hover {
    border: .01rem solid #DA1A14;
}
.user-info-list .info-item-modify  .btn-cancel {
    width: 10%;
    height: .4rem;
    margin-left: .1rem;
    border: .01rem solid #6D6D6D;
    background-color: transparent;
    border-radius: .05rem;
    font-size: .16rem;

}

.user-info-list .info-item-modify  .btn-keep {
    width: 10%;
    height: .4rem;
    margin-left: .1rem;
    background-color: #DA1A14;
    border-radius: .05rem;
    font-size: .16rem;
    color: #FFFFFF;
}

</style>

2-4)个人登录页面-右侧信息栏-修改头像

思路:
这个需求主要分为3个部分
(1)页面呈现部分
(用一个大的div进行包裹,包裹头像、图片、以及最右边的部分,通过flex进行布局(减少float的使用),3个部分的内容再细分,主要命名等等)
(2)点击更换头像交互
(3)点击更换头像请求网络接口

效果如下(右边):
在这里插入图片描述
demo代码如下:

<template>
<div class="userHead">
    <div class="info-label">
        头像
    </div>
    <div class="info-head">
        <div class="info-img">
            <img src="/static/image/use.jpg" alt="">
        </div>
    </div>
    <div class="info-replace">
        <p class="info-tips">*支持jpg、gif、png或jpeg格式的图片, 建议图片尺寸为 200×200px。建议图片大小不超过2MB。</p>
        <div  class="replaceBtnS">
            <el-upload
                :with-credentials = 'true'
                ref="uploadCropper"
                :show-file-list="false"
                action="/"
                :on-change="changeUpload"
                :auto-upload="false"
                style="margin-right: 10px;">
                <el-button type="primary" size="small" class="replace">更换头像</el-button>
            </el-upload>
        </div>
        <el-dialog
            title="修改头像"
            :visible.sync="dialogVisible"
            width="800"
            :before-close="handleClose">
            <el-row>
                <el-col :span="24" style="height: 300px;">
                    <vue-cropper
                        ref="cropper"
                        :img="option.img"
                        :outputSize="option.size"
                        :outputType="option.outputType"
                        :info="true"
                        :full="option.full"
                        :canMove="option.canMove"
                        :canMoveBox="option.canMoveBox"
                        :original="option.original"
                        :autoCrop="option.autoCrop"
                        :autoCropWidth="option.autoCropWidth"
                        :autoCropHeighh="option.autoCropHeight"
                        :fixed="option.fixed"
                        :fixedNumber="option.fixedNumber"
                        :centerBox="option.centerBox"
                        :infoTrue="option.infoTrue"
                        :fixedBox="option.fixedBox">

                    </vue-cropper>
                </el-col>
            </el-row>
            <span slot="footer" class="dialog-footer">
                        <el-button @click="dialogVisible = false" size="small" class=" cancel">取消</el-button>
                        <el-button type="primary" :loading="loading" @click="finish" size="small" class="hold" >确定</el-button>
                    </span>
        </el-dialog>
    </div>
</div>
</template>

<script>

export default {
    name: "head",
    methods: {
        handleCoverSuccess(e){
            let url = e.data.url;
            userUpdate({
                'avatar':url
            }).then(res => {
                if(res.code === 200){
                    //更换头像成功
                    this.$message({
                        message: res.msg,
                        type: 'success'
                    });
                    this.$emit("updateInfo",true)
                }else{
                    this.$message({
                        message: res.msg,
                        type: 'error'
                    });
                }
            })
        },
        changeUpload(file){
            this.fileinfo = file
            let reader = new FileReader()
            reader.readAsDataURL(file.raw)
            reader.onload = (e) => {
                // 上传成功后将图片地址赋值给裁剪框显示图片
                this.$nextTick(() => {
                    this.$set(this.option,'img',e.target.result)
                    this.dialogVisible = true
                })
            }
        },
        finish(){
            this.$refs.cropper.getCropBlob(data => {
                data = this.blobToFile(data)
                const formData = new FormData()
                formData.append('input_file',data)
                this.dialogVisible = false
                toolsUploadImg(formData).then(res => {
                    if(res.code === 200){
                        //上传图片成功
                        this.handleCoverSuccess(res)
                    }
                }).catch(e => {
                    if(e.code === 500) {
                        this.$message.warning(e.msg)
                    }
                })
            })
        },
        blobToFile(data){
            return new File([data],this.fileinfo.name,{type:this.fileinfo.type})
        },
        handleClose(){
            this.dialogVisible = false
        }
    },
    data () {
        return {
            imgUrl: "",
            dialogVisible: false,
            option: {
                img: '', //裁剪图片的地址
                info: true, //裁剪框的大小信息
                outputSize: 0.8, // 裁剪生成图片的质量
                outputType: '', // 裁剪生成图片的格式
                canScale: false, // 图片是否允许滚轮缩放
                autoCrop: true, //是否默认生成截图框
                autoCropWidth: 300, //默认生成截图框宽度
                autoCropHeight: 300,
                fixedBox: false, // 固定截图框大小 是否允许改变
                fixed: true,  //是否开启截图框宽高固定比例
                fixedNumber: [1, 1], //截图框的宽高比例
                original: false, // 上传图片按照原始比例渲染
                centerBox: false, // 截图框是否被限制在图片里面
                infoTrue: true // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
            },
            previews: {},
            loading: false,
            fileList: [],
            uploadAccept: ['jpeg', 'jpg', 'png']
        }
    }
}
</script>

<style scoped>
.userHead {
    display: flex;
    height: .95rem;
}

.info-label {
    width: .65rem;
    font-size: .16rem;
    font-family: SourceHanSansSC-Regular, SourceHanSansSC;
    font-weight: 400;
    color: #333333;
    line-height: .95rem;
    padding-left: .24rem;
    text-align: right;
}
.info-img {
    display: inline-block;
    width: .95rem;
    height: .95rem;
    border-radius: 50%;
    margin-left: .24rem;
}
.info-img img {
    width: 100%;
    height: 100%;
    border-radius: 50%;
}
.info-replace {
    display: inline-block;
    height: .95rem;
    margin-left: 1rem;
}
.info-replace .info-tips {
    height: .24rem;
    font-size: .14rem;
    font-family: SourceHanSansSC-Normal, SourceHanSansSC;
    font-weight: 400;
    color: #797979;
    padding-top: .2rem;

}

</style>

2-5)个人登录页面-右侧信息栏-修改密码

具体可以点击这篇教程====》》#vue# mousedown、mouseup事件,JS实现密码框小眼睛显示与隐藏

2-6)个人登录页面-右侧信息栏-校验邮箱

具体可以点击这篇教程====》》#vue# js实现正则表达式验证邮箱

2-7)个人登录页面-右侧信息栏-修改手机号

具体可以点击这篇教程====》》#vue# js正则表达式——实现手机号校验效果

2-8)个人登录页面-右侧信息栏-提交表单

2-9)继续补充

(3)排版常见大坑

3-1)css常用标签

点击可以查看>>>

#css# 【二】 常用但容易忘记的样式使用

3-4)来回切换页面,数据错乱解决办法(watch)

在开发里面,我们经常会遇到一个板块,里面有多个内容,类似于下拉框,
然而其实所有的页面,都是在一个页面里面的,只不过是通过路由进行跳转,所以在我们跳转到其他页面的时候
应该要先清空上一个页面的数据,不然的话,就会一直加
在我们切换下拉框的时候,来回查看数据的时候,要通过watch来监听数据的变化

当我们监听到地址的id 发生变化时,就先清空上一个地址id的数据

<script>
import titleBanner from "@/components/child/titleBanner";
import { joinIndex } from '@/model/api/index';
export default {
    name: 'join',
    components: {titleBanner},
    created () {
        this.getIndex(1)
    },
    data () {
        return {
            newList: [],
            stage: parseInt(this.$route.params.stage),
            page:1,
            totalNum:0,
        }
    },
    mounted () {
        document.querySelector('html').addEventListener('scroll', this.scroll);
    },
    watch: {
        '$route'(to) {
            this.stage = Number(to.params.stage)
            this.newList = []
            this.getIndex()
        }
    },
    methods:{
        getIndex(page) {
            joinIndex({
                stage: this.stage,//类型1 入党积极分子 2 发展对象 3 预备党员
                limit_rows:12,
                page:page
            })
            .then(res=>{
                let list = res.data.list;
                this.totalNum = res.data.total_number;//15
                //list.length 计算获取到的数据长度 即总数

                for (var j = 0; j < list.length; j++) {
                    this.newList.push(list[j]);
                }
            })
        },
        toDetail(id) {
            this.$router.push({
                path: `/course/${id}`
            })
        },
        onScroll() {
            let inner = document.querySelector('.join-study');
            if(inner.scrollHeight - inner.scrollTop <= inner.clientHeight){//为true时证明已经到底,可以请求接口
                if(this.newList.length < this.totalNum){
                    this.page++;//每次分页+1
                    this.getIndex(this.page)
                }
            }
        },
    }
}
</script>

3-5)盒子里的数据可以上下滑动,滚动条隐藏

3-6)点击tab,跳转到指定页面

点击查看教程》》》
#vue# 【十六】如何从一个页面跳转到另一个页面的指定tab选项卡?

3-6)CSS页面底部固定

当我们在写页面时经常会遇到页面内容少的时候,footer会顶上去,底部就会有一大片空白(即是没有在最底部展示),这个时候版面会显得很丑
所以我们只需要把它固定在浏览器底部就可以了
所以需要设置页面的最低高度
计算出header加上footer的高度
然后在每一个页面加上:

min-height: calc(100vh - 200px); /* 这个200px是header加上footer的高度 */
<header>Header</header>
<main>Content</main>
<footer>Footer</footer>

main{
min-height: calc(100vh - 200px); /* 这个200px是header加上footer的高度 */
}
header,footer{
height: 100px;
line-height: 100px;
}

3-6)继续补充

(4)动画(悬浮/上移/阴影等)

4-1)鼠标移入时,切换样式/hover的使用(自身、父对子、子对父、兄弟)

点击即可查看>>>#css#如何使用hover,实现父对子的样式改变?>>>

4-2)图片上移且有阴影

点击即可查看>>> #css动画# 【四】如何实现鼠标经过盒子,盒子向上移动且有阴影?

4-3)文本超长时,展示为一行&省略号,并且用气泡展示全部文本

点击即可查看>>>文本超长时,展示为一行&省略号,并且用气泡展示全部文本>>>

4-4)鼠标的点击、移入、移出事件

 
mousedown
当鼠标指针移动到元素上方,并按下鼠标按键(左、右键均可)时,
会发生 mousedown 事件
与 click 事件不同,mousedown 事件仅需要按键被按下,而不需要松开即可发生
 
mouseup
当在元素上松开鼠标按键(左、右键均可)时,会发生 mouseup 事件
与 click 事件不同,mouseup 事件仅需要松开按钮。当鼠标指针位于元素上方时,
放松鼠标按钮就会触发该事件
 
 
click
当鼠标指针停留在元素上方,然后按下并松开鼠标左键时,就会发生一次 click 事件。
注意:触发click事件的条件是按下并松开鼠标左键!
按下并松开鼠标右键并不会触发click事件。
 

4-5)滑动加载

4-6)继续补充

(5)语法常用技巧

5-1)html

原创-HTML基础
原创-HTML5+CSS3(新增标签)

5-2)CSS

5-2-1)flex布局

5-2-1)

5-3)JS

5-3-1)点击tab栏,跳转到指定的页面

5-3-1)

5-4)vue

5-3-1)vue知识大全

5-3-1)

(6)其他

3、element的使用

(1) 使用安装

(2)按需引入

(3)使用注意点

点击查看》》》#vue#【十四】 element同级类名&类名优先级

4、库的使用

(1)Animate动画库

点击查看》》》 #前端开发# 【一】 之Animate动画库的安装及详细用法

(2)Iconfont图标库使用

点击查看》》》#前端开发# 【二】如何将iconfont里面的图标,引用到代码里?

(3)蓝湖、json工具使用

(4)webstorm使用

webstorm vue 向内缩进4个空格

webstorm调整缩进量,ctrl + alt + s打开settings,找到editor下的code style 下的javascript,点击就会出现调整缩进的设置

在这里插入图片描述

四、项目进行时(数据接口)

1、网络接口

(1)#vue# 超级详细步骤!vue项目封装网络请求接口思路及方法

(2)接入数据后可全部刷新

(3)一些数据接口的技巧方法

五、项目结束(bulid)

(1)打包项目

当项目都做完以后,就执行npm run build ,
在目录版块会生成一个dist,
会在dist目录下生成index.html和static文件夹,
将dist下所有文件复制到你需要的目录下或者压缩成压缩包即可

(2)域名切换

正式环境以及测试环境的区别,只是地址域名的不同,其他没有任何区别
假设你在测试环境里改的代码,在正式环境里,也是有更新的
所以在我们打包的时候,只需要切换不同的域名地址即可

在这里插入图片描述

Logo

前往低代码交流专区

更多推荐