1.Vue3项目本地目录

~/workspace/jiange/git/sgi/sgi-core-fe

项目描述:

统一管理平台前端项目.

尚硅谷视频代码仓库地址:

https://gitee.com/jch1011/vue3_admin_template-bj1/tree/master

本地项目地址:

~/workspace/gitee/vue3_admin_template-bj1-master

技术栈:

vue项目内校验语法,格式的一些插件

1
2
3
-.eslintrc.js  #Es语法校验设置
-.prettierrc.js #格式化配置
-.stylelintrc #样式格式化配置

Vue项目结构

1
2
3
4
-index.html  #程序入口html
-src
|-main.ts #项目入口文件
|-App.vue #项目主组件

Vue 的组件可以按两种不同的风格书写:选项式 API 和组合式 API。

选项式api

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
//说明 data()方法vue组件初始化就执行,mounted()方法vue组件初始化完成后执行,methods是定义方法的
<script>
export default {
// data() 返回的属性将会成为响应式的状态
// 并且暴露在 `this` 上
data() {
return {
count: 0
}
},

// methods 是一些用来更改状态与触发更新的函数
// 它们可以在模板中作为事件处理器绑定
methods: {
increment() {
this.count++
}
},

// 生命周期钩子会在组件生命周期的各个不同阶段被调用
// 例如这个函数就会在组件挂载完成后被调用
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>

<template>
<button @click="increment">Count is: {{ count }}</button>
</template>

组合式api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 通过ref能直接绑定数据对象,用const修饰,onMounted直接引入生命周期钩子,function定义好increment()方法
<script setup>
import { ref, onMounted } from 'vue'

// 响应式状态
const count = ref(0)

// 用来修改状态、触发更新的函数
function increment() {
count.value++
}

// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>

<template>
<button @click="increment">Count is: {{ count }}</button>
</template>

vue文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- `template` 对应 HTML 代码 -->
<template>
<div>
<!-- 一些 HTML -->
</div>
</template>

<!-- `script` 部分对应 JavaScript 代码 -->
<!-- 还支持其他语言,例如 `lang="ts"` 代表当前使用 TypeScript 编写 -->
<script>
export default {
// 这里是变量、函数等逻辑代码
}
</script>

<!-- `style` 部分对应 CSS 代码 -->
<!-- 还支持开启 `scoped` 标识,让 CSS 代码仅对当前组件生效,不会全局污染 -->
<style scoped>
/* 一些 CSS 代码 */
</style>

添加和删除列表操作的例子

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
<script setup>
import { ref } from 'vue'

// 给每个 todo 对象一个唯一的 id
let id = 0

const newTodo = ref('')
const todos = ref([
{ id: id++, text: 'Learn HTML' },
{ id: id++, text: 'Learn JavaScript' },
{ id: id++, text: 'Learn Vue' }
])

function addTodo() {
todos.value.push({ id: id++, text: newTodo.value })
newTodo.value = ''
}

function removeTodo(todo) {
todos.value = todos.value.filter((t) => t !== todo)
}
</script>

<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
</template>

computed方法的运用

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
<script setup>
import { ref, computed } from 'vue'

let id = 0

const newTodo = ref('')
const hideCompleted = ref(false)
const todos = ref([
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
])

const filteredTodos = computed(() => {
return hideCompleted.value
? todos.value.filter((t) => !t.done)
: todos.value
})

function addTodo() {
todos.value.push({ id: id++, text: newTodo.value, done: false })
newTodo.value = ''
}

function removeTodo(todo) {
todos.value = todos.value.filter((t) => t !== todo)
}
</script>

<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
<button @click="hideCompleted = !hideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
</template>

<style>
.done {
text-decoration: line-through;
}
</style>

watch监听方法的使用说明

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
<script setup>
import { ref, watch } from 'vue'

const todoId = ref(1)
const todoData = ref(null)

async function fetchData() {
todoData.value = null
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
todoData.value = await res.json()
}

fetchData()

watch(todoId, fetchData)
</script>

<template>
<p>Todo id: {{ todoId }}</p>
<button @click="todoId++">Fetch next todo</button>
<p v-if="!todoData">Loading...</p>
<pre v-else>{{ todoData }}</pre>
</template>

通过defineProps函数实现父组件动态传递数值给子组件

ChildVue

1
2
3
4
5
6
7
8
9
<script setup>
const props = defineProps({
msg: String
})
</script>

<template>
<h2>{{ msg || 'No props passed yet' }}</h2>
</template>

FatherVue

1
2
3
4
5
6
7
8
9
10
11
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'

const greeting = ref('Hello from parent')

</script>

<template>
<ChildComp :msg="greeting" />
</template>

子组件定义插槽会自动的获取父组件的值

ChildVue

1
2
3
<template>
<slot>Fallback content</slot>
</template>

FatherVue

1
2
3
4
5
6
7
8
9
10
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'

const msg = ref('from parent')
</script>

<template>
<ChildComp>Message: {{ msg }}</ChildComp>
</template>

通过emit函数实现子组件向父组件传递值

ChildVue

1
2
3
4
5
6
7
8
9
<script setup>
const emit = defineEmits(['response'])

emit('response', 'hello from child')
</script>

<template>
<h2>Child component</h2>
</template>

FatherVue

1
2
3
4
5
6
7
8
9
10
11
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'

const childMsg = ref('No child msg yet')
</script>

<template>
<ChildComp @response="(msg) => childMsg = msg" />
<p>{{ childMsg }}</p>
</template>

变量操作的使用说明

App 开发

桌面程序开发

node.js的版本维护说明,中文下载官网(https://nodejs.org/zh-cn/download)

package.json说明:

依赖包管理,替换国内npm镜像源:

1
2
3
4
5
6
7
8
9
#查看npm的镜像源
npm config get registry
#修改镜像
npm config set registry https://registry.npmmirror.com

npm install -g cnpm --registry=https://registry.npm.taobao.org 
#再次查看镜像源
npm config get registry

单文件测试地址

Vue SFC Playground

Typescript类型:

在 JavaScript 中,let 和 const 都是用来声明变量的关键字,它们的主要区别在于变量的可变性和作用域范围。

  • let 声明的变量是可变的(mutable),可以重新赋值,但不能重新声明。

  • const 声明的变量是不可变的(immutable),不能重新赋值,也不能重新声明。

问题:

1.打包问题

error Delete ␍ prettier/prettier 打包出错

解决方案:

设置下git拉取代码换行符自动转换问题:

1
git config --global core.autocrlf false

然后重新拉取下分支即可正常打包.

2.i18n再js或ts文件中的使用

1
2
3
4
5
import i18n from "@/lang/index"

const tips = i18n.global.t('return')
//取i18n的locale值
const localeValue =i18n.global.locale.value

参考博客:

https://www.jianshu.com/p/fffe291d7d3e

  1. 测试环境项目打包时提示

import_vite_plugin_svg_icons.createSvgIconsPlugin) is not a function

原因是因为,查看package.json里面的vite_plugin_svg_icons发现版本是0.1.0所以没有对应方法

解决办法是gitlab上的test分支没有同步跟新导致的,删除原有测试分支,重新clone下测试分支即可解决

参考博客:

https://www.cnblogs.com/yehx/p/17081436.html

4.i18n切换,下拉框里面的枚举值没有正常切换

解决办法:使用vue的computed方法进行加载切换,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// * 系统全局字典
//引入中英文切换
import i18n from "@/languages";
//使用computed进行挂载
import { computed } from "vue";
//定义i18n.global.t方法简写
const $t = i18n.global.t;

/**
* @description:是否停用
*/
export const isDisabledType = computed(() => {
return [
{ label: $t("dict.whether.yes"), value: "Y" },
{ label: $t("dict.whether.no"), value: "N" }
];
});

5.重置自定义store仓库的方法

注意执行方法调用需要带()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//使用store自带的reset方法
import { AuthStore } from "@/stores/modules/auth";
import { TabsStore } from "@/stores/modules/tabs";

const authStore = AuthStore();
const tabsStore = TabsStore();

// 3.清空authStore和tabsStore
authStore.$reset();
tabsStore.$reset();

//第二种方式,在actions自定义清空属性的方法
//重置store
async resetStore() {
this.routeName = "";
this.authButtonList = {};
this.authMenuList = [];
this.authMenuTitleMap = new Map();
}
//引用方法
// 3.清空authStore和tabsStore
authStore.resetStore();
tabsStore.$reset();

退出时执行结果如下:

退出前:

退出后:

6.vue3添加加载刷新

方式一:局部刷新tabs页

MoreButton.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { computed, inject, nextTick } from "vue";
import { KeepAliveStore } from "@/stores/modules/keepAlive";

const keepAliveStore = KeepAliveStore();

const refreshCurrentPage: Function = inject("refresh") as Function;
// refresh current page
const refresh = () => {
setTimeout(() => {
keepAliveStore.removeKeepAliveName(route.name as string);
refreshCurrentPage(false);
nextTick(() => {
keepAliveStore.addKeepAliveName(route.name as string);
refreshCurrentPage(true);
});
}, 0);
};

方式二

App.vue(注释部分)

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
<template>
<el-config-provider :locale="i18nLocale" :button="config" :size="assemblySize">
<!-- <router-view v-if="isRouter"></router-view> -->
<router-view></router-view>
</el-config-provider>
</template>

<script setup lang="ts">
// import { reactive, computed, nextTick, provide, ref } from "vue";
import { reactive, computed } from "vue";
import { GlobalStore } from "@/stores";
import { useTheme } from "@/hooks/useTheme";
import { getBrowserLang } from "@/utils/util";
import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/es/locale/lang/zh-cn";
import en from "element-plus/es/locale/lang/en";

const globalStore = GlobalStore();
const { initTheme } = useTheme();
initTheme();

//刷新路由配置 TODO 后面需要全局刷新在开启对应注释
// const isRouter = ref(true);
// const reload = () => {
// isRouter.value = false;
// nextTick(() => {
// isRouter.value = true;
// });
// };
// provide("reload", reload);

// element config
const config = reactive({ autoInsertSpace: false });

// element language
const i18nLocale = computed(() => {
if (globalStore.language == "zh") return zhCn;
if (globalStore.language == "en") return en;
return getBrowserLang() == "zh" ? zhCn : en;
});

// element assemblySize
const assemblySize = computed(() => globalStore.assemblySize);
</script>

对应页面使用Language.vue(注释掉的部分,主要使用inject方法)

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
<template>
<el-dropdown trigger="click" @command="handleSetLanguage">
<i :class="'iconfont icon-zhongyingwen'" class="toolBar-icon"></i>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :disabled="language && language === 'zh'" command="zh">简体中文</el-dropdown-item>
<el-dropdown-item :disabled="language === 'en'" command="en">English</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>

<script setup lang="ts">
import { computed, onMounted } from "vue";
import { useI18n } from "vue-i18n";
import { GlobalStore } from "@/stores";
import { getBrowserLang } from "@/utils/util";
import { localSet } from "@/utils/util";
import { switchLanguage } from "@/api/modules/language";
import { synLanguageConfig } from "@/utils/language";
import { LanguageSwitch } from "@/api/interface";
// import { inject } from "vue";
import { TabsStore } from "@/stores/modules/tabs";
import { initDynamicRouter } from "@/routers/modules/dynamicRouter";

const i18n = useI18n();
const globalStore = GlobalStore();
const language = computed((): string => globalStore.language);
// const reload: any = inject("reload");

// 切换语言
const handleSetLanguage = async (lang: string) => {
//刷新页面
// reload();
initLanguage(lang);
if (globalStore.token) {
//调用后端接口
let param: LanguageSwitch = { language: synLanguageConfig(globalStore.language) };
await switchLanguage(param);
// 3.添加动态路由
await initDynamicRouter();
//成功后,刷新TabsStore的值
TabsStore().flushTabs();
}
};

const initLanguage = (lang: string) => {
//调试中英文设置(cn|en)
// lang = "cn";
i18n.locale.value = lang;
globalStore.updateLanguage(lang);
localSet("GlobalState.language", lang);
let title = globalStore.title;
//i18n切换标签页 过滤动态路由页签中英文切换
if (title.indexOf(".") != -1) {
title = i18n.t(globalStore.title);
}
//页签标题中英文显示
document.title = title + "-" + i18n.t("login.systemName");
};

onMounted(() => {
initLanguage(language.value || getBrowserLang());
});
</script>

7.vue3组合式api自带组件定义引用使用

1
import { ref, reative, toRefs, toRef, computed, watch, readonly, onCreated, onMounted, onUpdated, nextTick, provide, inject, h}

参考博客

https://juejin.cn/post/7220681875743866939

8.用户管理菜单出现下面警告

Component inside renders non-element root node that cannot be animated,如图:

点击其他页面component就会加载不了页面.

解决办法:

因为自定义的userManage下面的vue的template下面出现了多个根标签导致的,外层套个div即可解决

参考博客:

https://juejin.cn/post/7000664887290495013

https://blog.csdn.net/sinat_36728518/article/details/123106147

9.v-for和v-show组合使用,v-for和v-if尽量不要组合使用

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<el-table-column label="角色" width="120" show-overflow-tooltip>
<template #default="{ row }">
<el-text
class="mx-1"
v-for="(item, index) in row.roleLists"
:key="item.id"
v-show="index !== row.roleLists.length - 1"
>
{{ item.roleName }},
</el-text>
<el-text
class="mx-1"
v-for="(item, index) in row.roleLists"
:key="item.id"
v-show="index === row.roleLists.length - 1"
>
{{ item.roleName }}
</el-text>
</template>
</el-table-column>

参考博客: https://zhuanlan.zhihu.com/p/613703420

el-table-column自定义说明参考博客: https://www.cnblogs.com/mmzuo-798/p/14626515.html

表格渲染说明:

https://blog.csdn.net/HanXiaoXi_yeal/article/details/121767161

https://blog.csdn.net/meimeib/article/details/111075715

https://blog.csdn.net/qq_44813690/article/details/114634419

table字体颜色和背景

https://blog.csdn.net/qq_32610671/article/details/90731672

table背景颜色不生效

https://blog.csdn.net/weixin_46533954/article/details/119912772

10.vite vue3的打包管理工具,配置代理说明参考博客

https://zxuqian.cn/vite-proxy-config/

vite打包后在本地运行

https://blog.csdn.net/pujun1201/article/details/125720592

引入静态文件

https://www.qiyuandi.com/zhanzhang/zonghe/10423.html

https://blog.csdn.net/qq_41038929/article/details/120568082

https://juejin.cn/post/7109441209315098654

https://blog.csdn.net/qq_41038929/article/details/120568082

11.cascader输入框内容宽度自适应:

https://www.cnblogs.com/robinunix/p/11679265.html

https://github.com/element-plus/element-plus/issues/5331

12.promise用法:

https://blog.csdn.net/Adam_captain/article/details/120923905

13.表格居中显示的方式,参考博客:

https://www.jianshu.com/p/8c2849558786

14.form表单

居中展示:

https://blog.csdn.net/TheSpiritIsHeart/article/details/118579244

15.div文字靠上排列的方式

https://zhidao.baidu.com/question/2266241751789252548.html

16.组合是函数的用法:

https://juejin.cn/post/7220681875743866939

computed函数用法

https://www.51cto.com/article/694187.html

reactive对应用法参考博客:

https://blog.csdn.net/wwwwwqj/article/details/113947571