vue项目实战:项目布局和公共文件夹的确定

1.布局文件 

<!--
 * @Description: 布局容器组件    vue-intro + intro.js  新手引导页功能 home文件
 * @Version: 2.0
 * @Autor: lhl
 * @Date: 2020-06-11 17:54:29
 * @LastEditors: lhl
 * @LastEditTime: 2020-08-20 16:54:29
--> 
<!--  -->
<template>
  <div class="layout-box">
    <el-container>
      <el-header>
        <topHeader />
      </el-header>
      <el-container>
        <el-aside width="220px">
          <el-scrollbar class="page-component__scroll">
            <siderBar />
          </el-scrollbar>
        </el-aside>
        <el-main>
          <div class="right-view" ref="rightViewBox">
            <!-- <Breadcrumb ref="bread"/> -->
            <transition name="fade-transform" mode="out-in">
              <keep-alive :include="keepAliveList">
                <router-view></router-view>
              </keep-alive>
            </transition>
          </div>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
// router-view 的 key 或者用路由钩子 再或者用监听路由变化
// 场景:由于 Vue 会复用相同组件, 即 /detail/1 => /detail/2 或者 /detail?id=1 => /detail?id=2 这类链接跳转时, 将不在执行created, mounted之类的钩子
// <router-view :key="$route.fullPath"></router-view>
// 这样组件的 created 和 mounted 就都会执行
import topHeader from "./topHeader";
import siderBar from "./siderBar";
import Breadcrumb from '../Breadcrumb/index'
import { mapActions, mapGetters, mapMutations } from "vuex";

// 引导
import Driver from 'driver.js' // import driver.js
import 'driver.js/dist/driver.min.css' // import driver.js css
import steps from '@/common/steps'
export default {
  components: {
    topHeader,
    siderBar,
    Breadcrumb
  },
  data() {
    //这里存放数据
    return {
      keepAliveList: ['baiduMapTest'] // 缓存的组件
    }
  },
  methods: {
    ...mapMutations(["setContentBoxHeight", "setBtnPermitCode"]),
    // 系统引导
    guide() {
      const  driver  = new Driver()
      driver.defineSteps(steps)
      driver.start()
    },
    // 获取按钮code码
    getBtnCodeList(){
      this.$http.user.btnList().then(res => {
         console.log(res)
        if(res.returnResult === 200){
          this.setBtnPermitCode(res.returnData.buttonList)
        }
      })
    }
  },
  computed:{
    ...mapGetters(['getcontentBoxHeight'])
  },
  created() {
    this.getBtnCodeList()
  },
  mounted() {
    this.guide();
    //第二种方法
      if (window.performance.navigation.type == 1) {
        console.log("页面被刷新")
      } else {
        console.log("首次被加载")
      }
    window.addEventListener("resize", () => {
      if (this.$refs.rightViewBox && this.$refs.rightViewBox.clientHeight) {
        this.setContentBoxHeight(this.$refs.rightViewBox.clientHeight);
        // console.log(this.$refs.rightViewBox.clientHeight, "rightViewBox");
      }
    })
    if (this.$refs.rightViewBox.clientHeight) {
      this.setContentBoxHeight(this.$refs.rightViewBox.clientHeight);
    }
  }
};
</script>
<style lang='scss' scoped>
//@import url(); 引入公共css类
.layout-box {
  height: 100%;
  min-width: 1200px;
  .el-aside {
    position: absolute;
    top: 60px;
    bottom: 0;
    background: #3db4f8;
    .el-menu{
      border-right: none;
    }
  }
  .el-main {
    position: absolute;
    left: 220px;
    right: 0;
    top: 60px;
    bottom: 0;
    padding: 10px;
    background: #eee;
    .right-view{
      padding: 20px;
      background: #fff;
      min-height: 100%;
    }
  }
}
</style>
<!--
 * @Description: 侧边栏组件
 * @Version: 2.0
 * @Autor: lhl
 * @Date: 2020-06-11 17:54:54
 * @LastEditors: lhl
 * @LastEditTime: 2020-07-17 14:22:49
--> 

<!--  -->
<template>
  <div class="sider-content-box">
    <el-menu
      class="el-menu-vertical-demo"
      background-color="#3DB4F8"
      text-color="#fff"
      active-text-color="#fff"
      :collapse-transition="false"
      router
    >
      <!-- 一级菜单 -->
      <template v-for="(item,index) in menuData">
        <el-submenu
          v-if="item.children && item.children.length"
          :index="item.menuUrl"
          :key="item.menuUrl"
        >
          <template slot="title">
            <i :class="item.icon"></i>
            <span>{{item.name}}</span>
          </template>
          <!-- 二级菜单 -->
          <template v-for="(itemChild,vIndex) in item.children">
            <el-submenu
              v-if="itemChild.children && itemChild.children.length"
              :index="itemChild.menuUrl"
              :key="vIndex"
            >
              <template slot="title">
                <i :class="itemChild.icon"></i>
                <span>{{itemChild.name}}</span>
              </template>
              <!-- 三级菜单 -->
              <el-menu-item
                v-for="itemChild_Child in itemChild.children"
                :index="itemChild_Child.menuUrl"
                :key="itemChild_Child.menuUrl"
              >
                <i :class="itemChild_Child.icon"></i>
                <span slot="title">{{itemChild_Child.name}}</span>
              </el-menu-item>
            </el-submenu>
            <el-menu-item v-else :index="itemChild.menuUrl" :key="itemChild.vIndex">
              <i :class="itemChild.icon"></i>
              <span slot="title">{{itemChild.name}}</span>
            </el-menu-item>
          </template>
        </el-submenu>
        <el-menu-item v-else :index="item.menuUrl" :key="index">
          <i :class="item.icon"></i>
          <span slot="title">{{item.name}}</span>
        </el-menu-item>
      </template>
    </el-menu>
  </div>
</template>

<script>
import  { menuData } from '@/common/constant'
export default {
  components: {},
  data() {
    //这里存放数据
    return {
      menuData
    };
  },
  computed: {},
  watch: {},
  methods: {},
  created() {},
  mounted() {}
};
</script>
<style lang='scss' scoped>
//@import url(); 引入公共css类
</style>
<!--
 * @Description: 头部组件
 * @Version: 2.0
 * @Autor: lhl
 * @Date: 2020-06-12 09:12:02
 * @LastEditors: lhl
 * @LastEditTime: 2020-08-04 17:33:36
--> 
<!--  -->
<template>
  <div class="header-content-box">
    <div class="logo" id="logo">
      <img src="@/assets/logo.png" alt />
    </div>
    <div id="title">vue测试管理系统</div>
    <div class="top-info">
      <div class="user-avatar">
        <img src="@/assets/logo.png" alt />
      </div>
      <div class="user-down" id="user">
        <el-dropdown trigger="click" @command="handleCommand">
          <span class="el-dropdown-link">
            {{getUserInfo.userName || '默认用户'}}
            <i class="el-icon-arrow-down el-icon--right"></i>
          </span>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item command="baseInfo">基本资料</el-dropdown-item>
            <el-dropdown-item command="changePassword">修改密码</el-dropdown-item>
            <el-dropdown-item divided command="loginOut">退出登录</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
      </div>
    </div>
    <el-dialog title="用户基本资料" :visible.sync="dialogVisibleInfo" width="50%">
      <div class="user-info">
        <p>id:{{getUserInfo.id}}</p>
        <p>用户名:{{getUserInfo.userName}}</p>
        <p>账号:{{getUserInfo.domainAccount}}</p>
        <p>所在公司:{{getUserInfo.organizationName}}</p>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisibleInfo = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisibleInfo = false">确 定</el-button>
      </span>
    </el-dialog>

    <el-dialog title="修改密码" :visible.sync="dialogVisibleChangePwd" width="50%">
      <el-form
        ref="ruleForm"
        :model="ruleForm"
        :rules="resetRules"
        class="login-form"
        label-width="80px"
        label-position="left"
      >
        <el-form-item label="密码" prop="passwd">
          <el-input
            type="password"
            v-model="ruleForm.passwd"
            placeholder="请输入密码"
            autocomplete="off"
            show-password
          ></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="checkPass">
          <el-input
            type="password"
            v-model="ruleForm.checkPass"
            placeholder="请输入确认密码"
            autocomplete="off"
            show-password
          ></el-input>
        </el-form-item>
      </el-form>

      <span slot="footer" class="dialog-footer">
        <el-button @click="resetForm('ruleForm')">取 消</el-button>
        <el-button type="primary" @click="submitForm('ruleForm')">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { mapMutations, mapGetters } from "vuex";
export default {
  data() {
    var validatePass = (rule, value, callback) => {
      if (value === "") {
        callback(new Error("请输入密码"));
      } else {
        if (this.ruleForm.checkPass !== "") {
          this.$refs.ruleForm.validateField("checkPass");
        }
        callback();
      }
    };
    var validatePass2 = (rule, value, callback) => {
      if (value === "") {
        callback(new Error("请再次输入密码"));
      } else if (value !== this.ruleForm.passwd) {
        callback(new Error("两次输入密码不一致!"));
      } else {
        callback();
      }
    };
    //这里存放数据
    return {
      dialogVisibleInfo: false, // 用户信息
      dialogVisibleChangePwd: false, // 修改用户密码
      ruleForm: {
        passwd: "",
        checkPass: "",
      },
      resetRules: {
        passwd: [{ validator: validatePass, trigger: "blur" }],
        checkPass: [{ validator: validatePass2, trigger: "blur" }],
      },
    };
  },
  computed: {
    ...mapGetters(["getUserInfo"]),
  },
  created() {},
  methods: {
    ...mapMutations(["loginOut"]),
    handleCommand(command) {
      console.log(command);
      if (command === "baseInfo") {
        this.dialogVisibleInfo = true;
      } else if (command === "changePassword") {
        this.dialogVisibleChangePwd = true;
      } else if (command === "loginOut") {
        this.$confirm("您确定要退出吗?", "退出管理平台", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
        })
          .then(() => {
            this.loginOut();
            this.$router.push(`/login?redirect=${this.$route.fullPath}`);
          })
          .catch(() => {});
      }
    },
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          // 调用重置密码的接口
          alert("重置密码成功!");
          this.dialogVisibleChangePwd = false;
        } else {
          // console.log("error submit!!");
          return false;
        }
      });
    },
    resetForm(formName) {
      this.dialogVisibleChangePwd = false;
      this.$refs[formName].resetFields();
    },
  },
};
</script>
<style lang='scss' scoped>
//@import url(); 引入公共css类
.header-content-box {
  height: 60px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: url("~@/assets/img/bg.png");
  .logo {
    img {
      width: 40px;
      height: 40px;
    }
  }
  .top-info {
    display: flex;
    justify-content: center;
    align-items: center;
    .user-avatar {
      border-radius: 50%;
      img {
        width: 40px;
        height: 40px;
        border-radius: 50%;
      }
    }
  }
}
</style>

2.公共样式

// reset.scss
@import './transition.scss';
@import './public.scss';
@import './elementreset.scss';
*{
  margin: 0;
  padding: 0;
  list-style: none;
}
/** 清除内外边距 **/
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */
dl, dt, dd, ul, ol, li,/* list elements 列表元素 */
pre, /* text formatting elements 文本格式元素 */
form, fieldset, legend, button, input, textarea, /* form elements 表单元素 */
th, td /* table elements 表格元素 */ {  
    margin: 0;  
    padding: 0;
}
body,#app{
  height: 100%;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}

html{
  height:100%;
}    
body{
  height:100%;
  margin:0px;
  padding:0px;
}

address, cite, dfn, em, var ,i{ 
    font-style: normal; 
}

/* for ie6 */
fieldset, img { border: 0; } 
/* img :让链接里的 img 无边框 */
button, input, select, textarea { font-size: 100%;} 
/* 使得表单元素在 ie 下能继承字体大小 *//* 注:optgroup 无法扶正 */ /** 重置表格元素 **/
table { border-collapse: collapse; border-spacing: 0; } 

label {
  font-weight: 700;
}

html *{
  font-size: 14px;
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

// a:focus,
// a:active {
//   outline: none;
// }

// a,
// a:focus,
// a:hover {
//   cursor: pointer;
//   text-decoration: none;
// }

div:focus {
  outline: none;
}

.fr {
  float: right;
}

.fl {
  float: left;
}

.clearfix {
  &:after {
    visibility: hidden;
    display: block;
    font-size: 0;
    content: " ";
    clear: both;
    height: 0;
  }
}

.md{
    display: inline-block;
  vertical-align: middle;
}


/*去掉type为number的箭头*/
input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
  -webkit-appearance: none;
}

input[type="number"] {
  -moz-appearance: textfield;
}

// Dialog 对话框居中显示
.dialog-box-center{
  text-align: center;
  &:after {
      content: "";
      display: inline-block;
      height: 100%;
      width: 0;
      vertical-align: middle;
  }
  .el-dialog{
      text-align: left;
      display: inline-block;
      margin: 0 !important;
      vertical-align: middle;
      min-width: 800px;
  }
}
// transition.scss
/*fade*/
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}

.fade-enter,
.fade-leave-active {
  opacity: 0;
}

/*fade-transform*/
.fade-transform-leave-active,
.fade-transform-enter-active {
  transition: all .8s;
}

.fade-transform-enter {
  opacity: 0;
  transform: translateX(-30px);
}

.fade-transform-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
// public.scss
.pd10{
  padding: 10px;
}

.ptb10{
  padding: 10px 0;
}
.mt10{
  margin-top: 10px;
}
.md {
  display: inline-block;
  vertical-align: middle;
}

// 进度条颜色 
#nprogress .bar {
  background: red !important; //自定义颜色
}

.flex-column {
  display: flex;
  justify-content: center;
  flex-direction: column;
}


/*滚动条样式*/
.page-component__scroll {
  height: 100%;
  .el-scrollbar__wrap {
    overflow-x: auto;
  }
}

/*滚动条颜色*/
.scroll-bar-color {
  .el-scrollbar__thumb {
    background-color: rgba(118, 118, 119, 0.8);
  }
}
// elementreset.scss
// 分页样式
.el-pagination {
  text-align: right;
  margin-top: 10px;
}

// element ui表头换行 
.el-table .cell {
  white-space: pre-line;
}

// tooltip样式更改 固定宽高换行显示
.el-tooltip__popper {
  max-width: 400px;
  line-height: 180%;
}

// 重置表单样式 element ui 下拉、输入、级联、日期
.reset-form {
  .el-input,
  .el-select,
  .el-cascader,
  .el-range-editor {
    width: 100% !important;
  }
  .el-pagination {
    .el-input {
      width: 50px !important;
    }
    .el-select {
      .el-input {
        width: 100px !important;
      }
      width: 100px !important;
    }
  }
}

.el-table {
  .el-date-editor.el-input,
  .el-date-editor.el-input__inner {
    width: 100% !important;
  }
}

// to fixed https://github.com/ElemeFE/element/issues/2461
// Dialog内select tree等组件在点击箭头时有虚晃 
.el-dialog {
  transform: none;
  left: 0;
  position: relative;
  margin: 0 auto;
}


// 当前行选中颜色高亮
.el-table--striped .el-table__body tr.el-table__row--striped.current-row td,
.el-table__body tr.current-row>td {
  background-color: #ffec8b;
}

.el-loading-mask {
  background-color: rgba(0, 0, 0, .3);
}

// 菜单样式
/* 非折叠的竖直菜单处理方式和上方的水平菜单一样,只不过需要将horizontal改为vertical
  Element-UI中,在没有折叠的菜单中,无论是水平还是竖直菜单,子菜单是放在菜单栏所在的div中的,
  只是将display属性设置为了none。
  而在折叠菜单中,虽然菜单项也是放在页面上,但不在是放在菜单栏所在的div中,而是放在了页面底部的单独div中,
  类属性值是el-menu--popup,因为是单独的div,所以在处理弹出菜单时就比较方便,直接处理el-submenu__title
  和el-menu-item就可以,而不必区分是几级菜单项(如果不同级别菜单项要分别处理,需要使用“>”等操作分别处理) */
/*处理竖直菜单收齐后的弹出菜单中的子菜单 */
.el-menu--vertical .el-menu--popup .el-submenu__title {
  height: 40px;
  line-height: 40px;
}

.el-menu--vertical .el-menu--popup .el-menu-item {
  height: 40px;
  line-height: 40px;
}

.el-menu--vertical .el-menu--popup .el-icon-arrow-right:before {
  color: #fff;
}

3.面包屑组件

<!--
 * @Description: 全局面包屑组件 Breadcrumb/index.vue
 * @Version: 2.0
 * @Autor: lhl
 * @Date: 2020-08-04 17:51:48
 * @LastEditors: lhl
 * @LastEditTime: 2020-08-20 16:57:07
-->
<template>
  <div class="breadcrumb-box">
    <!-- separator-class="el-icon-arrow-right" 图标分隔符 class -->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item
        v-for="item in breadList"
        :to="{path: item.path }"
        :key="item.name"
      >{{item.meta.title}}</el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>

<script>
export default {
  created() {
    this.getBreadcrumb();
  },
  data() {
    return {
      breadList: [],
    };
  },
  watch: {
    $route() {
      this.getBreadcrumb();
    },
  },
  methods: {
    getBreadcrumb() {
      let matched = this.$route.matched.filter((item) => item.name);
      // console.log(matched,'matched')
      this.breadList = matched;
    },
  },
};
</script>
<style lang='scss' scoped>
//@import url(); 引入公共css类
.breadcrumb-box{
  padding: 10px 0;
  background: #fff;
  border-bottom: 1px solid #333;
}
</style>

  以上代码本人项目实测!!!真实可靠,请勿随意转载~转载请注明出处~~~谢谢合作!

posted @ 2020-08-20 16:58  蓝色帅-橙子哥  阅读(1045)  评论(0编辑  收藏  举报