一个vue程序模板

代码结构

vue template 及局部变量 scope_ios

1.main.js

import { createApp } from 'vue'
import App from './App.vue'
import Index from './Index.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// 导入封装好的axios并挂载到Vue全局属性上
import Axios from 'axios'
const app = createApp(Index)
app.use(ElementPlus)
app.use(router)
app.config.globalProperties.$axios = Axios;
app.mount('#app')

2.Index.vue

<template>
    <el-container>
        <el-header>
            <el-row>
                <el-col :span="4" style="border: 5px solid black">
                    <div class="grid-content bg-purple">logo</div>
                </el-col>
                <el-col :span="20" style="border: 5px solid black">
                        <router-link to="/">主页</router-link>
                        <router-link to="/news">新闻</router-link>
                        <router-link to="/picture">图片</router-link>
                        <router-link to="/login">登录</router-link>
                </el-col>
            </el-row>
        </el-header>
        <div class="main">
            <router-view/>
        </div>
    </el-container>

</template>
<script>
    export default {
        name: 'Index',
    }
</script>
<style>
    * {
        box-sizing: border-box;
    }

    body,html{
        height: 100%;

    }

    .el-header{
        margin: 0!important;
        padding: 0!important;
    }
    .el-header .el-row{
        height: 100%;
    }

    .el-header .el-row .el-col{
        display: flex;
        align-items: center;
    }

    .el-header .el-row .el-col *{
        margin: 20px;
    }

    .el-header{
        margin-bottom: 1rem!important;
    }
</style>

3.vue.config.js

module.exports = {
    devServer:{
        port:8090
    }
};

4.index.js

import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import(/* webpackChunkName: "about" */ '../views/Home.vue'),
  },
  {
    path: '/news',
    name: 'news',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/News.vue'),
    //添加meta属性,不可任意起名
    meta:{
      // loginRequest是一个表示符,可以任意起
      loginRequest:true,
    }
  },
  {
    path: '/login',
    name: 'login',
    component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue'),
  },
  {
    path: '/picture',
    name: 'picture',
    component: () => import(/* webpackChunkName: "about" */ '../views/Picture.vue'),
  },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes
});

//添加路由拦截
/*
router.beforeEach((to, from, next) => { } 三个参数:
to:即将要进入的目标 路由对象
from:当前导航正要离开的路由
next:(function函数) 调用next() 进行管道中的下一个钩子
next() 无参 进行 下一个钩子函数
next({ path:'/xxx' , query:{}}) 携带参数跳到xxx页面
 */
router.beforeEach((to,from,next)=>{
  if (to.meta.loginRequest){//如果要去的路由的meta对象的loginRequest属性为true
    if (sessionStorage.getItem('user')) {//如果已经登录,进入目标界面
        next();
    }else {//用户没有登录,直接去登录界面
      next({
        path: '/login',
        query:{
          //把目标界面的页面路径作为参数传到登录界面,方便登录后跳转目标界面
          redirect:to.fullPath
        }
      });
    }
  }else {//其他界面情况
    next();
  }
});

export default router

5.Side.vue

<template>
    <div>
<!--        通过lable改变isCollapse的值-->
<!--        <el-radio-group v-model="isCollapse" style="margin-bottom: 20px">-->
<!--            <el-radio-button :label="false">expand</el-radio-button>-->
<!--            <el-radio-button :label="true">collapse</el-radio-button>-->
<!--        </el-radio-group>-->

<!--
default-active:默认点击
:collapse 是否折叠
el-menu是主目录
el-menu-item是一个目录下的元素
el-sub-menu是子目录
-->
        <el-menu
                default-active="2"
                :collapse="isCollapse"
                @open="handleOpen"
                @close="handleClose"
        >
            <el-menu-item index="0" @click="changeState">
                <el-icon><aim /></el-icon>
                <!--伸展的时候在里面显示,折叠的时候在外面显示 #title是标题-->
                <template #title>{{stateInfo}}</template>
            </el-menu-item>
            <el-menu-item index="{{index}}"  v-for="(data, index) in datas" @click="See(data.url)">
                <el-icon><Document /></el-icon>
                <template #title>
                    <div @click="See(data.url)">{{data.description}}</div>
                </template>
            </el-menu-item>

        </el-menu>
    </div>
</template>

<script>
    import { defineComponent, ref } from 'vue'
    import {
        Location,
        Document,
        Menu as IconMenu,
        Setting,
        Aim
    } from '@element-plus/icons'

    export default {
        name: 'Side',
        props: ['contents'],
        components: {
            Location,
            Document,
            Setting,
            IconMenu,
            Aim
        },
        data(){
            return{
                stateInfo:"展开",
                isCollapse:true,
                datas:[],
            }
        },
        setup() {
            // ref("小黑"):生成变量,这是一个对象,需借助.value来赋值
            // let isCollapse = ref(true);
            const handleOpen = (key, keyPath) => {
                // console.log(key, keyPath)
            };
            const handleClose = (key, keyPath) => {
                // console.log(key, keyPath)
            };
            return {
                handleOpen,
                handleClose,
            }
        },

        methods:{
            changeState() {
                this.isCollapse=!this.isCollapse;
                if(this.isCollapse){
                    this.stateInfo="展开";
                }else {
                    this.stateInfo="收缩";
                }
            },
            See (e) {
                window.location.href = e
            }
        },

        created: function () {
            var self = this;
            this.$axios.get(this.contents)
                .then(function (res) {
                    console.log(res);
                    self.datas = res.data;
                }).catch(function (error) { // 请求失败处理
                console.log(error);
            });
            console.log(this.contents);

        },
    }
</script>

6.Picture.vue

<template>
    <div style="display: flex">
        <Side :contents="src"/>
        <PictureRight/>
    </div>
</template>

<script>
    import Side from './Side'
    import PictureRight from './PictureRight'
    export default {
        name: "Slider",
        data(){
            return{
                src:"http://www.xlcode.top/img/kinds/"
            }
        },
        components: {
            Side,
            PictureRight
        }
    }
</script>

<style scoped>
    .main{
        height: 100%;
        background: #ff6436;
    }
</style>

7.PictureRight.vue

<template>
    aaaaa
</template>

<style scoped>
</style>

8.Login.vue

<template>
    <div class="login">
        <div class="container">
            <el-image
                    :src="url"
                    class="img"
            ></el-image>
            <!--
        ref 属性涉及Dom 元素的获取(el-form表单对象)。
        javaSrcipt 获取Dom 元素是通过:document.querySelector(".input")获取dom元素节点 。
        Vue 为简化DOM获取方法提出了ref 属性和$refs 对象。一般的操作流程是ref 绑定控件,$refs 获取控件。
        这里用ff来标记这个表单,this.$refs['ff']来获得表单
    -->
            <el-form
                    ref="ff"
                    :model="formItems"
                    :rules="rules">
                <!--label-width:标签的宽度-->
                <!--label:标签-->
                <!--
                Vue组件库element-ui中的Form表单组件提供了表单验证功能
                通过rules属性传入验证规则
                Form-Item中的prop属性设置需要校验的字段名,要和rules对象中保持一致,
                也等价于要和表单的属性名称保持一致
                -->
                <!--el-form-item元素的prop属性绑定字段名account,表单验证时,
                就会验证el-input元素绑定的变量formItems.account的值是否符合验证规则-->
                <el-form-item label="账号" prop="account">
                    <el-input v-model="formItems.account"></el-input>
                </el-form-item>

                <el-form-item label="密码" prop="password">
                    <!--show-password:密码框-->
                    <el-input v-model="formItems.password" show-password></el-input>
                </el-form-item>
                <!--提交按钮-->
                <el-form-item>
                    <el-button type="primary" @click="submitForm('ff')">Create</el-button>
                    <el-button @click="resetForm('ff')">Reset</el-button>
                </el-form-item>
            </el-form>
        </div>
    </div>

</template>

<script lang="">
    //导出组件
    export default {

        data() {
            return {
                //图片地址
                url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
                //表单属性对象,和表单进行双向绑定:model
                formItems: {
                    //账号
                    account: '',
                    // 密码
                    password:''
                },
                //验证规则对象,其中的属性名称要和表单对象的属性名称保持一致,否则会失效
                rules: {
                    account: [
                        {
                            required: true,
                            //错误提示
                            message: '请输入账号',
                            //事件,只有两个选项blur / change
                            trigger: 'blur',
                        },
                        {
                            min: 3,
                            max: 5,
                            message: '账号3到5位',
                            trigger: 'blur',
                        },
                    ],
                    password: [
                        {
                            required: true,
                            //输入框类型
                            message: '请示入密码',
                            trigger: 'blur',
                        },
                    ],
                },
            }
        },
        methods: {
            //提交点击事件,传入表单属性对象
            submitForm(formName) {
                //this.$refs[formName]获得这个表单,然后执行validate方法进行校验,
                // 这里的校验是点击按钮之后才会进行的校验
                this.$refs['ff'].validate((valid) => {
                    if (valid) {
                        //获得正确的属性
                        console.log(this.formItems.account+":"+this.formItems.password);
                        //在缓存中存贮用户信息,sessionStorage的过期时间就是关闭浏览器,是个临时会话窗口,
                        // localStorage,这个好处就是存储空间大,长时间保存,同一浏览器,标签页全部共享,
                        // 它是直接存到电脑硬盘上的
                        // localStorage.setItem("user",this.formItems.account);
                        sessionStorage.setItem("user",this.formItems.account);
                        /*
                        | this.$route:当前激活的路由的信息对象。每个对象都是局部的,
                        可以获取当前路由的 path, name, params, query 等属性。
                        | this.$router:全局的 router 实例。通过 vue 根实例中注入 router 实例,
                        然后再注入到每个子组件,从而让整个应用都有路由功能。其中包含了很多属性和对象
                        (比如 history 对象),任何页面也都可以调用其 push(), replace(), go() 等方法。
                         */
                        //获得路由携带的参数,没有的话就指定'/'
                        let redirect = this.$route.query.redirect || '/';
                        //路由调转
                        this.$router.push({path:redirect})
                    } else {
                        console.log('error submit!!');
                        return false
                    }
                })
            },
            //重置点击事件
            resetForm(formName) {
                this.$refs[formName].resetFields()
            },
        },
    }
</script>

<style>

    div.login{
        width: 100%;
        height: 100%;
        position: fixed;
        left:0;
        top: 0;
        background: #ff792f;
    }
    .container{
        position: absolute;
        left: 50%;
        top: 50%;
        /*向左和上偏移半个身位*/
        transform: translate(-50%, -50%);
        background: #70e53f;
        padding: 30px!important;
        /*width: 400px;*/
        /*height: 300px;*/

    }
    div.login div.container .img{
        width: 100px;
        height: 100px;
        margin-bottom: 20px;
        border-radius:50%;
    }

</style>

9.News.vue

<template>
    <el-row >
        <el-col :span="4" style="border: 5px solid black">
            <el-aside width="200px" class="hidden-sm-only">
                <!--监听子组件的up函数,用receive(自定义方法)来接受函数-->
                <Side :contents="src" v-on:up='receive' v-on:up2='receive2' style="position: fixed"/>
            </el-aside>
        </el-col>

        <el-col :span="20" style="border: 5px solid #3dfff7">
            <el-container style="border: 1px solid red">
                <el-main>
                    <NewsRight ref="www"/>
                </el-main>
                <el-footer>Footer</el-footer>
            </el-container>
        </el-col>
    </el-row>
</template>

<script>
    import Side from './NewsLeft'
    import NewsRight from './NewsRight'
    export default {
        name: "About",
        data(){
            return{
                src:"http://www.xlcode.top:8082/searchType",
                content:"",
            }
        },

        methods:{
            // 父组件中响应子组件自定义的方法
            // 此函方法的参数是用来接收从子组件传递来的数据
            // 子组件传递了几个数据,这里就有几个参数
            receive(content){
                this.content = content;
            },
            receive2(){
                // console.log("子调父");
                // ref进行标注子组件,然后实施调用
                this.$refs.www.seachContent(this.content);
            },
        },

        components: {
            Side,
            NewsRight
        }
    }
</script>

<style scoped>
    .main{
        height: 100%;
        background: #ff6436;
    }
</style>

10.NewsLeft.vue

<template>
    <div>
        <!--        通过lable改变isCollapse的值-->
        <!--        <el-radio-group v-model="isCollapse" style="margin-bottom: 20px">-->
        <!--            <el-radio-button :label="false">expand</el-radio-button>-->
        <!--            <el-radio-button :label="true">collapse</el-radio-button>-->
        <!--        </el-radio-group>-->

        <!--
        default-active:默认点击
        :collapse 是否折叠
        el-menu是主目录
        el-menu-item是一个目录下的元素
        el-sub-menu是子目录
        -->
        <el-menu
                default-active="2"
                :collapse="isCollapse"
                @open="handleOpen"
                @close="handleClose"
        >
            <el-menu-item index="0" @click="changeState">
                <el-icon><aim /></el-icon>
                <!--伸展的时候在里面显示,折叠的时候在外面显示 #title是标题-->
                <template #title>{{stateInfo}}</template>
            </el-menu-item>
            <el-menu-item index="{{index}}"  v-for="(data, index) in datas" @click="see(data.url.split('=')[1])">
                <el-icon><Document /></el-icon>
                <template #title>
                    <div @click="see(data.url.split('=')[1])">{{data.name}}</div>
                </template>
            </el-menu-item>

        </el-menu>
    </div>
</template>

<script>
    import { defineComponent, ref } from 'vue'
    import {
        Location,
        Document,
        Menu as IconMenu,
        Setting,
        Aim
    } from '@element-plus/icons'

    export default {
        name: 'Side',
        props: ['contents'],
        components: {
            Location,
            Document,
            Setting,
            IconMenu,
            Aim
        },
        data(){
            return{
                stateInfo:"展开",
                isCollapse:true,
                datas:[],
            }
        },
        setup() {
            // ref("小黑"):生成变量,这是一个对象,需借助.value来赋值
            // let isCollapse = ref(true);
            const handleOpen = (key, keyPath) => {
                // console.log(key, keyPath)
            };
            const handleClose = (key, keyPath) => {
                // console.log(key, keyPath)
            };
            return {
                handleOpen,
                handleClose,
            }
        },
        inject: ["out"],
        methods:{
            changeState() {
                this.isCollapse=!this.isCollapse;
                if(this.isCollapse){
                    this.stateInfo="展开";
                }else {
                    this.stateInfo="收缩";
                }
            },
            see (e) {
                console.log("leftleft")
                //定义函数,向父组件传值
                this.$emit("up",e)
                this.$emit("up2")
                // this.$parent.out();
                // this.out();
            }
        },

        created: function () {
            var self = this;
            console.log(this.contents);
            this.$axios.get(this.contents)
                .then(function (res) {
                    console.log(res);
                    self.datas = res.data.data;
                }).catch(function (error) { // 请求失败处理
                console.log(error);
            });


        },
    }
</script>

11. NewsRight

<template>
  <main class="text-center">
    <hr class="mt-2"/>
    <div class="container" v-for="content in contents">
      <div class="row mt-2 mb-2">

        <!--右面5-->
        <div class="col-md-3 text-center">
          <img :src="content.imgUrl" style="width: 10rem;
                                height: 10rem">
        </div>
        <!--左面7-->
        <div class="col-md-7">
          <h2 class="featurette-heading">
            <span class="text-muted">{{content.title}}</span>
          </h2>
          <p class="lead">
            {{content.content}}
          </p>
        </div>
      </div>
      <hr/>
    </div>

    <!-- 页脚 -->
    <footer class="fixed-bottom">
      <!--右浮动-->
      <p class="float-right"><a href="#app">Back to top</a></p>
      <p>© 2017-2021 Company</p>
    </footer>
  </main>
</template>

<script>
  export default {
    data() {
      return {
        contents: []
      }
    },

    methods: {
      seachContent(condition) {
        let self = this;
        this.$axios.get('http://www.xlcode.top:8082/searchContent/' + condition)
                .then(function (res) {
                  self.contents = res.data.data;
                }).catch(function (error) { // 请求失败处理
          console.log(error);
        });
      }
    },
    mounted:function(){
      this.seachContent("realtime")
    }
  }


</script>