当前位置:   article > 正文

vue3 + element-plus + typescript 创建系统_vue3 typescript elementui 点击跳转到新的container

vue3 typescript elementui 点击跳转到新的container

vue3 构建项目是在没阅读官网文档和其他资料的情况下,一边试验一边摸索学习的,如果有不正确的地方,欢迎评论指正~

项目搭建

vue3 安装

1、安装 nodeJS
2、安装 vue-cli

npm install -g @vue/cli

# 检测是否安装成功(显示版本号即为安装成功)
vue --version
  • 1
  • 2
  • 3
  • 4

创建项目

vue create [项目名]
  • 1

直接选第一个:
在这里插入图片描述
或者选择第三个进行自定义配置:
自定义配置
vue3 选择 3.x
相关设置

运行项目

npm run serve
  • 1

默认端口是8080
在这里插入图片描述

路由设置

安装router

没有自定义安装 router,需要进行安装:

npm install vue-router@4
  • 1

main.ts文件需要引入router模块

// main.ts
import App from './App.vue';
import router from './router';

createApp(App).use(router).mount("#app");
  • 1
  • 2
  • 3
  • 4
  • 5

配置路由

路由配置写在srt/router目录下的index.ts文件中,如果没有这个文件,可以自行创建
在这里插入图片描述

// index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import HomeView from '../views/HomeView.vue'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',		// 路径
    name: 'home',	// 路由名称
    component: HomeView,	// 对应的路由页面
  },
  {
    path: '/about',
    name: 'about',
    // 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/AboutView.vue')
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

设置统一模板 layout

新增 layout 文件

src目录下新增文件夹layout,新增默认布局文件DefaultLayout.vue
在这里插入图片描述

<template>
    <div class="layout">
        <el-container style="height:100%">
            <el-aside class="layout_aside">
                <el-menu
                    :router="true"
                    :default-active="$route.meta.id"
                >
                    <template v-for="(item, index) in menuList[1].children" :key="index">
                        <el-menu-item :index="item.meta.id" :route="item" v-if="item.meta.isMenu">
                            <el-icon><component :is="item.meta.icon"></component></el-icon>
                            <span>{{item.meta.title}}</span>
                        </el-menu-item>
                    </template>
                </el-menu>
            </el-aside>
            <el-container>
				<router-view />		<!-- 点击菜单,不同组件变化显示在这里 -->
            </el-container>
        </el-container>
    </div>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import {
    Document,
    Menu as IconMenu,
    Location,
    Setting,
    Expand,
    ArrowDown,
    SwitchButton,
    Fold,
    Guide,
    Coin,
    Connection,
    Calendar
} from "@element-plus/icons-vue";

@Options({
    components: {
        Document,
        IconMenu,
        Location,
        Setting,
        Expand,
        Fold,
        SwitchButton,
        ArrowDown,
        Guide,
        Coin,
        Connection,
        Calendar
    },
})
export default class DefaultLayout extends Vue {
    isCollapse: Boolean = true; // 菜单栏是否折叠:false--展开 true--折叠

    handleOpen(key: string, keyPath: string[]) {}
    handleClose(key: string, keyPath: string[]) {
        console.log(key, keyPath);
    }

    // 折叠/展开菜单栏
    collapseHandle(): void {
        this.isCollapse = !this.isCollapse;
    }
    // 菜单栏
    get menuList() {return this.$router.options.routes}

    // 退出登录
    logout():void {
        console.log("退出登录");
    }

    mounted() {
        // console.log("路由====", this.menuList, this.$route);
    }

}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

配置路由信息

将布局界面DefaultLayout.vue作为父组件,其他界面作为该界面的子组件写在children数组里面

// index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import DefaultLayout from "../layout/DefaultLayout.vue";

const routes: Array<RouteRecordRaw> = [
  {
    path: "/",
    name: "DefaultLayout",
    component: DefaultLayout,
    children: [
      {
        path: "path",
        component: ()=>import("../views/name.vue"),
        name: "name",
        meta: {			// 路由元信息
          title: "router-title",
          id:"1",
          icon: "guide",
        }
      },
    ]
  },
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

export default router;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

关于菜单的一些问题

动态加载菜单栏 icon

很多时候,系统的菜单都是根据后台配置的,还有权限配置等,菜单前面添加 icon 区分就需要动态引入图标。而 element-plus 是将名字作为标签显示,如在这里插入图片描述的写法是

<!-- element-plus -->
<el-icon :size="size" :color="color">
  <Edit />
</el-icon>

<!-- elementui -->
<i class="el-icon-edit"></i>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

动态引入的写法是在el-icon标签中添加<component>标签,并将 icon 名字写在is属性中:

<!-- <el-icon><icon-menu /></el-icon> -->
<el-icon><component :is="item.meta.icon"></component></el-icon>
  • 1
  • 2

关于菜单高亮与地址栏地址相匹配

在这里插入图片描述

数据响应式

reactive

要为 JavaScript 对象创建响应式状态,可以使用reactive方法:

<script setup lang="ts">
import { reactive } from "vue";

let form = reactive({
  name: "张三",
  age: 11,
  gender: "male",
  phone: "133******"
})
form.name = "李四"
console.log("form 没变:", form);

let obj = {
  name: "东子",
  age: 22,
  gender: "female",
  phone: "122******"
}
form = obj;
console.log("form 后变:", form);
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

reactive在控制台打印出来的是proxy
控制台输出

方法更新数据

如果是在script标签的全局下修改form的值,界面是可以响应的,但是如果用按钮控制信息的改变,界面是没有变化的:添加方法
在这里插入图片描述
在方法内form = obj这种写法是不生效的,需要用到Object.assign(form, obj),界面才会更新数据源代码
在这里插入图片描述

解构

如果是reactive需要用到解构,需要用toRefs

let form = reactive({
  name: "张三",
  age: 11,
  gender: "male",
  phone: "133******"
})

let { name, age, gender, phone } = form;
name = "李四";

console.log("name:", name);
console.log("age:", age);
console.log("gender:", gender);
console.log("phone:", phone);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

import {reactive, toRefs} from 'vue';

let form = reactive({
  name: "张三",
  age: 11,
  gender: "male",
  phone: "133******"
})
let { name, age, gender, phone } = toRefs(form);
name.value = "李四";

console.log("name:", name.value);
console.log("age:", age.value);
console.log("gender:", gender.value);
console.log("phone:", phone);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

ref

接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。获取和修改需要通过.value属性进行:

import {ref} from 'vue';

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

computed 和 watch 使用

computed

computed获取的值需要加.value

import { ref, computed } from "vue";

const count = ref(1)

// 只有 get 获取数据
const num = computed(()=>{
	return count.value + 1;
})
console.log("num:", num.value);		// 2


// get 和 set 
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})
console.log("plusOne", plusOne.value);

plusOne.value = 1
console.log(count.value) // 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述

watch

reactive

<template>
  <div class="about">
    <el-input v-model="state.count"></el-input>
  </div>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5
import {reactive, watch, ref} from 'vue';

// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (newVal, oldVal) => {
    console.log("newVal:", newVal)
    console.log("oldVal:", oldVal)
  },
  {
    deep: true,
    immediate: true
  }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

ref

<template>
  <div class="about">
    <el-input v-model="count"></el-input>
  </div>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5
const count = ref(3)
watch(count, (newVal, oldVal) => {
  console.log("newVal:", newVal)
  console.log("oldVal:", oldVal)
})
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

同时侦听多个源

源代码
在这里插入图片描述

父子组件通信

defineProps

defineProps不需要导入就可以直接使用
在这里插入图片描述
在这里插入图片描述

子组件向父组件传值

getCurrentInstance支持访问内部组件实例。getCurrentInstance 只能在 setup生命周期钩子中调用。

ctx.emit("update:paramName", newValue);	// 可以向父组件传值
ctx.emit("funcName", paramValue);	// 通过调用方法并且向父组件传递参数
  • 1
  • 2
第一种

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

第二种

在这里插入图片描述
在这里插入图片描述
但是界面并没有更新,因为vue3中父组件绑定的值需要用到v-model:xxx
在这里插入图片描述
在这里插入图片描述
vscode编译器会显示报错:
在这里插入图片描述

defineExpose

使用 <script setup> 的组件是默认关闭的,也即通过模板 ref或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。
为了在 <script setup> 组件中明确要暴露出去的属性,使用 defineExpose 编译器宏:

<!-- FatherView.vue -->
<template>
    <div>
        <p>FatherView</p>
        <br>
        ChildView:
        <child-view
            ref="childRef" 
        />
        <el-button @click="clickHandle">获取子组件暴露的值</el-button>
        <el-button @click="changeHandle">修改子组件暴露的值</el-button>
    </div>
</template>
<script lang="ts" setup>
import ChildView from './ChildView.vue';
import { ref } from 'vue';
const childRef = ref('childRef');
// 获取子组件暴露的值
const clickHandle = ()=>{
    console.log("childView a:", childRef.value.a);
    console.log("childView b:", childRef.value.b);
}
// 修改子组件暴露的值
const changeHandle = ()=>{
    childRef.value.a = 33;
    childRef.value.b = 3344;
    console.log("childView a:", childRef.value.a);
    console.log("childView b:", childRef.value.b);
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
<!-- ChildView.vue -->
<template>
    <div>
        ChildView
        <br>
        a:{{a}}
        <br>
        b:{{b}}
    </div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'

const a = 1
const b = ref(2)

defineExpose({
  a,
  b
})
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在这里插入图片描述

生命周期

在这里插入图片描述

<script lang="ts" setup>
import { onMounted, onUpdated, onUnmounted } from 'vue'

onMounted(() => {
  console.log('mounted!')
})
onUpdated(() => {
  console.log('updated!')
})
onUnmounted(() => {
  console.log('unmounted!')
})
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

axios 封装

安装 axios

npm install axios --save
  • 1

配置axios,添加拦截器

// 文件目录:utils/request.ts
import axios from "axios";

// 创建axios实例
let service: any = {};
const config = {
    timeout: 50000, // 请求超时时间
}
service = axios.create(config);

// request拦截器 axios的一些配置
service.interceptors.request.use(
  (config: any) => {
    Object.assign(config.data, {
      header: {
        timeStamp: new Date().getTime().toString()
      }
    })
    return config;
  },
  (error: any) => {
    // Do something with request error
    console.error("error:", error); // for debug
    Promise.reject(error);
  }
);

// respone拦截器 axios的一些配置
service.interceptors.response.use(
  (response: any) => {
    switch(response.statusText) {
      case "OK":
        return response.data;
      default: 
        return response;
    }
    // return response;
  },
  (error: any) => {
    return Promise.reject(error);
  }
);

export default service;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

代理

// vue.config.ts
const { defineConfig } = require("@vue/cli-service");
const path = require("path");

function resolve(dir) {
  return path.join(__dirname, dir);
}

module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false, // 关闭 eslint 检查
  devServer: {
  	// 多个服务代理
    // proxy: {
    //   "/api": {
    //     target:"http://xxx.xxx.xxx.xxx",	// 代理的后台地址
    //     pathRewrite: {
    //       "^/api": "",
    //     },
    //     changeOrigin: true,
    //     ws: true
    //   }
    // },
    proxy: "http://xxxxxxx",		// 单个服务代理
    port: 8011,	// 系统前端端口
  },

  // 配置 @ 路径
  chainWebpack: config => {
    config.resolve.alias
      .set("@", resolve("src"))
      .set("assets", resolve("src/assets"))
      .set("components", resolve("src/components"))
      .set("views", resolve("src/views"))
  },
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

vuex

安装

npm install vuex@next --save
  • 1

分模块管理

分模块管理可以将各模块分别写在store/modules文件夹下,index.ts进行模块管理
在这里插入图片描述

// index.ts
import { createStore } from "vuex";
import module1 from './modules/module1';
import module2 from "./modules/module2";

export default createStore({
  state: {
    
  },
  getters: {},
  mutations: {},
  actions: {},
  modules: {
    module1,	// 模块1
    module2, 	// 模块2
  },
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

各模块配置

// module1.ts
import api from "@/config/api";		// 接口地址
import service from "@/utils/request";	// axios 封装

const module1 = {
    namespaced: true,
    state:()=>({
        list: []
    }),
    mutations: {
        updateList(state:any, list:string[]) {
			state.list = list;
		}
    }, 
    actions: {
        // 调用接口获取数据
        async someFunc(content:any,params:Object) {
            const res = await service.post(api.xxx, params, {
                headers: {
                    'Content-Type': 'application/json; charset=UTF-8'
                }
            });
            return res;
        },
    }
}

export default module1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

使用

<script lang="ts" setup>
import { useStore } from "vuex";

const store = useStore();

// 获取 state 的值
let list = store.state.module1.list;
// 更新 state 的 list 
store.commit("module1/updateList", [1,2,3]);
// 调用 action 的方法
store.dispatch("module1/someFunc", params)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

element-plus

element-plus 目前只懂得怎么全局引用,局部引用还在摸索中

安装

npm install element-plus --save
  • 1

报错

1、在main.ts引入了element-plus后会出现下面的报错问题
原本以为是ts没定义类型,结果是element-plus 版本不对。后面将版本改成2.2.0就不会报错了

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

createApp(App).use(ElementPlus)mount('#app')
  • 1
  • 2
  • 3
  • 4

报错问题

部署

打包文件

npm run build
  • 1

IIS出现的问题(听说 IIS 不能设置反向代理)

1、刷新的时候会显示404:
①在dist文件夹下新增web.config文件,然后重启 IIS 即可

// web.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Handle History Mode and custom 404/500" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

②或者在 IIS 设置 URL 重定向,效果是一样的

部署到 nginx

下载

下载 nginx,然后把部署包放到nginx-1.23.0\html(nginx 下载文件的 html 目录)。
例如新建的目录名是project
在这里插入图片描述

nginx 配置

找到nginx/config/目录下的nginx.config文件
在这里插入图片描述

server {
        listen 6688;    # 你的域名
        server_name localhost;
        location / {
            root html/project;		# 部署包放置的位置
            index index.html;
            try_files $uri $uri/ /index.html;
        }
        # 后台接口代理设置
        location /api/ {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
            add_header Access-Control-Allow-Credentials true;     
            rewrite    ^/api/(.*)$ /$1 break;
            proxy_pass http://xxx.com; # 后台接口要代理的地址
            index  index.html;
        }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

浏览器访问地址就是localhost:6688

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码艺术创作者/article/detail/62766
推荐阅读
相关标签
  

闽ICP备14008679号