vue-icon(图标)

安装icons-vue的包

1
2
3
4
5
6
# NPM
$ npm install @element-plus/icons-vue
# Yarn
$ yarn add @element-plus/icons-vue
# pnpm
$ pnpm install @element-plus/icons-vue

在main.ts内全局注册element-ui的图标组件

1
2
3
4
5
6
7
// element icons
import * as Icons from "@element-plus/icons-vue";

// 注册element Icons组件
Object.keys(Icons).forEach(key => {
app.component(key, Icons[key as keyof typeof Icons]);
});

直接使用官网提供的系统图标库的方式:

官网地址:

https://element-plus.gitee.io/zh-CN/component/icon.html#%E7%9B%B4%E6%8E%A5%E4%BD%BF%E7%94%A8-svg-%E5%9B%BE%E6%A0%87

内部vue使用el-icon组件可直接引入,代码示例做了component动态加载

1
2
3
4
5
6
7
8
9
10
11
<template>
<el-icon class="collapse-icon" @click="collapse">
<component :is="themeConfig.isCollapse ? 'expand' : 'fold'"></component>
</el-icon>
</template>
//也可直接使用
<template>
<el-icon class="collapse-icon" @click="collapse">
<Expand/>
</el-icon>
</template>

效果如图:

如果觉得官网的图标库组件太少了,也可以使用iconfont官网的图标库

具体el-icon图标库可参见官网:

https://www.iconfont.cn/

现在有2千4百多万的图标还是很给力的

在搜索框里面找自己想要的图标,如中英文,布局库,并一起添加到购物车

点击购物车,将购物车里面的图标添加至项目(添加至项目是方面图标库的追加迭代)

然后将项目里面的图标库下载到本地,然后添加到项目中

拷贝iconfont.css和iconfont.ttf文件到项目中

iconfont.ttf为图标元素库,css为引用样式的,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@font-face {
font-family: iconfont;

/* Project id 4100450 */
src: url("iconfont.ttf?t=1685672070131") format("truetype");
}
.iconfont {
font-family: iconfont !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-zhongyingwen::before {
font-size: 25px; //定义每个图标的大小比例
content: "\e605";
}
.icon-zhuti::before {
content: "\e606";
}

并在main.ts全局注册iconfont.scss样式

1
import "@/assets/iconfont/iconfont.scss";

在vue里面引入对应图标库样式的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//主题库引入示例
<template>
<div class="theme-setting">
<i :class="'iconfont icon-zhuti'" class="toolBar-icon" @click="openDrawer"></i>
</div>
</template>
//中英文切换引入示例
<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>

实现效果如下:

Layout(主体布局组件的样式说明)

css中文官网:

https://developer.mozilla.org/zh-CN/docs/Web/CSS

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
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<!-- 分栏布局 -->
<template>
<el-container class="layout">
<div class="aside-split">
<div class="logo flx-center">
<img src="@/assets/images/logo.png" alt="logo" />
</div>
<el-scrollbar>
<div class="split-list">
<div
class="split-item"
:class="{ 'split-active': splitActive === item.path || `/${splitActive.split('/')[1]}` === item.path }"
v-for="item in menuList"
:key="item.path"
@click="changeSubMenu(item)"
>
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span class="title">{{ item.meta.title }}</span>
</div>
</div>
</el-scrollbar>
</div>
<el-aside :class="{ 'not-aside': !subMenu.length }" :style="{ width: isCollapse ? '65px' : '210px' }">
<div class="logo flx-center">
<span v-show="subMenu.length">{{ $t("layout.shortCompany") }}</span>
</div>
<el-scrollbar>
<el-menu
:default-active="activeMenu"
:router="false"
:collapse="isCollapse"
:collapse-transition="false"
:unique-opened="true"
background-color="#ffffff"
>
<SubMenu :menuList="subMenu" />
</el-menu>
</el-scrollbar>
</el-aside>
<el-container>
<el-header>
<ToolBarLeft />
<ToolBarRight />
</el-header>
<Main />
</el-container>
</el-container>
</template>

<script setup lang="ts" name="layoutColumns">
import { ref, computed, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { GlobalStore } from "@/stores";
import { AuthStore } from "@/stores/modules/auth";
import Main from "@/layouts/components/Main/index.vue";
import ToolBarLeft from "@/layouts/components/Header/ToolBarLeft.vue";
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";

const route = useRoute();
const router = useRouter();
const authStore = AuthStore();
const globalStore = GlobalStore();
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path));
const menuList = computed(() => authStore.showMenuListGet);
const isCollapse = computed(() => globalStore.themeConfig.isCollapse);

const subMenu = ref<Menu.MenuOptions[]>([]);
const splitActive = ref<string>("");
watch(
() => [menuList, route],
() => {
// 当前菜单没有数据直接 return
if (!menuList.value.length) return;
splitActive.value = route.path;
const menuItem = menuList.value.filter(
(item: Menu.MenuOptions) => route.path === item.path || `/${route.path.split("/")[1]}` === item.path
);
if (menuItem[0].children?.length) return (subMenu.value = menuItem[0].children);
subMenu.value = [];
},
{
deep: true,
immediate: true
}
);

// 切换 SubMenu
const changeSubMenu = (item: Menu.MenuOptions) => {
splitActive.value = item.path;
if (item.children?.length) return (subMenu.value = item.children);
subMenu.value = [];
router.push(item.path);
};
</script>

<style scoped lang="scss">
@import "./index.scss";
</style>

<style lang="scss">
.columns {
.el-menu,
.el-menu--popup {
.el-menu-item {
&.is-active {
background: var(--el-color-primary-light-9);
&::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 4px;
content: "";
background: var(--el-color-primary);
}
}
}
}
}
</style>

index.scss样式文件

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
.el-container { //整体页面的宽高展示
width: 100%;//使用百分比布局
height: 100%;
.aside-split {//侧边栏搜索的时候展示的样式
display: flex; //弹性盒子布局 说明见下面示例
flex-direction: column; //取值横竖和反转的排序布局 具体示例参见url(https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-direction)
flex-shrink: 0;//flex元素宽度之和超过容器整体宽度时产生收缩,值越大搜索程度越大,具体示例参见url(https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-shrink)
width: 70px;//侧边栏宽高度
height: 100%;
background-color: #ffffff;//背景色 白色
border-right: 1px solid var(--el-border-color);//右边框线条粗细及颜色 var(--el-border-color)说明具体参见(https://element-plus.org/zh-CN/component/border.html#%E8%BE%B9%E6%A1%86%E6%A0%B7%E5%BC%8F) 可以用作统一样式的自定义var 可参见url(https://developer.mozilla.org/zh-CN/docs/Web/CSS/var)
.logo {
box-sizing: border-box; //计算外面盒子的总高度和总宽度,属性值有两个border-box和content-box,分别含义为里面内容+边框为定义的总宽度和里面内容及为定义的总宽度,边框宽度另外计算
height: 55px;
border-bottom: 1px solid var(--el-border-color);//边框下侧线条粗细及颜色
img {
width: 32px;
object-fit: contain; //保持原图的宽高比进行缩放填充入布局框,适用于图片,其它属性值还有fill,cover,none,scale-down.
}
}
.el-scrollbar {
height: calc(100% - 55px);//计算剩余高度的方式
.split-list {
flex: 1; //以下3个属性的简写,flex-grow,flex-shrink,flex-basis
.split-item {
display: flex; //弹性盒子布局
flex-direction: column; //列式排放
align-items: center; //里面元素居中排放
justify-content: center; //多个元素居中相邻排列
height: 70px; //高度
cursor: pointer; //鼠标移入后变化鼠标样式的属性
transition: all 0.3s ease; //四个属性的简写,具体参见url(https://developer.mozilla.org/zh-CN/docs/Web/CSS/transition)
&:hover {//被选中后展示的样式
background-color: var(--el-color-primary);//背景色
}
.el-icon {
font-size: 21px;//图标大小
}
.title {
margin-top: 6px; //此组件离兄弟组件上方的距离
font-size: 12px; //字体大小
transform: scale(0.96);
}
.el-icon,
.title {
color: #333333;//字体颜色
}
}
.split-active { //选中后的对应样式
background-color: $primary-color !important;//$primary-color全局定义颜色样式的一种写法 !important优先使用该属性
.el-icon,
.title {
color: #ffffff;
}
}
}
}
}
.el-aside {//侧边栏展开的时候展示的样式
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden; //超出边框的内容进行隐藏
background-color: #ffffff;
border-right: 1px solid var(--el-border-color);
transition: all 0.3s ease;
.el-scrollbar {
height: calc(100% - 55px);
.el-menu {
overflow-x: hidden;//超出左右两侧内容时进行隐藏
border-right: none;//边框右侧不展示
}
}
.logo {
box-sizing: border-box;//计算外面盒子的总高度和总宽度,属性值有两个border-box和content-box,分别含义为里面内容+边框为定义的总宽度和里面内容及为定义的总宽度,边框宽度另外计算
height: 55px;
border-bottom: 1px solid var(--el-border-color);
span {
font-size: 24px;
font-style: Oblique;//字体样式斜体
font-weight: bold;//字体粗细
color: var(--el-color-primary);
white-space: nowrap;//文本内的换行无效,连续空白符会被合并,会被展示在一行,其它属性值效果具体参见url(https://developer.mozilla.org/zh-CN/docs/Web/CSS/white-space)
}
}
}
.not-aside {
width: 0 !important;
}
.el-header {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;//多个元素左右对其的方式展示
height: 55px;
padding: 0 15px;//内部元素左右兼具15px,四个元素是对应是上、右、下、左
background-color: #ffffff;
border-bottom: 1px solid var(--el-border-color);
:deep(.tool-bar-ri) {//深度选择器,使全局样式不会受到污染
.toolBar-icon,//两个图标的样式
.username {
color: var(--el-text-color-primary);
}
}
}
}

display的flex布局示例:

问题:

修改el-desc组件的子组件item的label的样式(默认一直为200px)

解决办法:

1
2
3
4
5
6
7
8
9
//在el-descriptions定义class名称,
<el-descriptions :column="1" class="orgEl_desc_class" border>
//底下引入style标签使用!important方式使其生效,如图:
<style>
/* .el-descriptions__label自定义样式 */
.orgEl_desc_class .el-descriptions__label {
width: 230px !important;
}
</style>

组件内文字显示在一行,可以用white-space: nowrap;的自定义样式

追加:

使用 overflow: hidden; 超过会隐藏

使用 text-overflow: ellipsis; 超出的部分用省略号表示

span内的文字独占一行剧中显示

display:block,让span形成块(独占一行),就像DIV一样,在设置内容居中css text-align:center。

Css布局全局参数整理

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
1.在配置文件里面定义好整理配置参数
// 主体布局框架框高度设置
export const LAYOUT_MENU_WIDTH: string = "330px";

export const LAYOUT_MENU_COLLAPSE_WIDTH: string = "65px";

export const LAYOUT_HEAD_HEIGHT: string = "55px";


2.在document的style设置属性元素
// 初始化 theme 布局布局框高比
const initThemeWidth = () => {
document.documentElement.style.setProperty("--layout-menu-width", LAYOUT_MENU_WIDTH);
document.documentElement.style.setProperty("--layout-menu-collapse-width", LAYOUT_MENU_COLLAPSE_WIDTH);
document.documentElement.style.setProperty("--layout-head-height", LAYOUT_HEAD_HEIGHT);
};

3.在app入口进行初始化加载
import { useTheme } from "@/hooks/useTheme";
const { initTheme } = useTheme();

initTheme();

4.对应css文件里面加载使用
width: var(--layout-menu-width);

使用el-col使form表单内的select和input输入框在一行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="head-search">
<el-form-item>
<el-col :span="11">
<el-form-item label="搜索条件:">
<el-select v-model="value" class="m-2" placeholder="Select">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="13">
<el-form-item>
<el-input placeholder="请输入搜索关键词" class="w-50 m-2" :suffix-icon="Search" size="large" />
</el-form-item>
</el-col>
</el-form-item>
</div>

使用el-tree-select代码实现

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
<el-form-item :label="$t('user.orgName')" prop="orgId">
<el-tree-select
class="select-width"
:placeholder="$t('form.selHolder').concat($t('user.orgName'))"
v-model="userReq!.orgId"
:props="defaultProps"
node-key="id"
:data="orgTreeList"
:filter-node-method="filterNodeMethod"
@change="getDepartList(1)"
check-strictly
:default-expanded-keys="userReq!.orgId?[userReq!.orgId]:[]"
:default-checked-keys="userReq!.orgId?[userReq!.orgId]:[]"
filterable
>
</el-tree-select>
</el-form-item>

const dialogProps = ref<DialogProps>({
title: "",
user: {} as UserEdit
});

let userReq = reactive<UserInfo.ReqUser>({
loginId: "",
userCname: "",
userEname: "",
tel: "",
roleLists: [],
isIpAuth: "",
ip: "",
email: "",
isDisabled: ""
});

// 接收父组件传过来的参数
const acceptParams = (params: DialogProps) => {
dialogProps.value = params;

if (dialogProps.value.user.id) {
userReq.orgId = dialogProps.value.user.coreOrg.id;
}

//初始化下拉框属性
initSelected();

Object.assign(userReq, dialogProps.value.user);
if (dialogProps.value.user.id) {
let roleLists = [];
for (let item of dialogProps.value.user.roleLists) {
roleLists.push(item.id);
}
userReq.roleLists = roleLists;
userReq.departId = dialogProps.value.user.coreDepartment.id;
} else {
userReq.orgId = undefined;
userReq.departId = undefined;
userReq.roleLists = [];
}

centerDialogVisible.value = true;
};

const orgTreeList = ref();
//初始化下拉框
const initSelected = async () => {
//获取机构属性列表,数组对象
const { data } = await getOrgTreeSelectList();
orgTreeList.value = data;
//获取角色列表
getRoleList();
//判断orgId是否为空,不为空初始化部门列表
if (userReq.orgId) {
getDepartList();
}
};

//树形选择框过滤方法
const filterNodeMethod = (orgInputValue: string, data: OrgInfo.ResOrgTreeSelect) => data.name.includes(orgInputValue)

实现效果:

使用el-select-v2代码实现

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
<el-form-item :label="$t('user.roleName')" prop="selectedRoleId">
<el-select-v2
v-model="userReq!.roleLists"
filterable
:options="roleOptions"
:placeholder="$t('form.selHolder').concat($t('user.roleName'))"
class="select-width"
multiple
/>
</el-form-item>

const roleList = ref<RoleInfo.ResAllRoleList[]>([]);
//初始化角色列表
const getRoleList = () => {
getAllRoleList().then(({ data }) => {
roleList.value = data;
roleOptions.value = Array.from({ length: roleList.value.length }).map((_, idx) => ({
value: +`${roleList.value[idx].id}`,
label: `${roleList.value[idx].roleShowName}`
}));
});
};

const roleOptions = ref();
roleOptions.value = Array.from({ length: roleList.value.length }).map((_, idx) => ({
value: `${roleList.value[idx].id}`,
label: `${roleList.value[idx].roleShowName}`
}));

实现效果和机构下拉框做联动

8.子组件调用父组件的方式,采用 $emit 方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//子模块 DialogForm
let $emit = defineEmits(["flushUserTable"]);
//回调父组件的方法
$emit("flushUserTable", userReq.id);

//父模块
<DialogForm ref="dialogRef" @flush-user-table="flushUserTable" />
//新增修改保存后回调父方法
const flushUserTable = (userId?: number) => {
if (userId) {
getUserTableList(pageNo.value);
} else {
//新增清空查询条件并返回第一页返回
userProp.value = undefined;
userPropValue.value = undefined;
getUserTableList();
}
};

父组件调用子组件的方式

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
//子模块 
//暴露方法给父组件传递参数
defineExpose({
acceptParams
});
// 接收父组件传过来的参数
const acceptParams = (params: DialogProps) => {
dialogProps.value = params;

if (dialogProps.value.user.id) {
userReq.orgId = dialogProps.value.user.coreOrg.id;
}

//初始化下拉框属性
initSelected();

Object.assign(userReq, dialogProps.value.user);
if (dialogProps.value.user.id) {
let roleLists = [];
for (let item of dialogProps.value.user.roleLists) {
roleLists.push(item.id);
}
userReq.roleLists = roleLists;
userReq.departId = dialogProps.value.user.coreDepartment.id;
} else {
userReq.orgId = undefined;
userReq.departId = undefined;
userReq.roleLists = [];
}

centerDialogVisible.value = true;
};


父组件
<DialogForm ref="dialogRef" @flush-user-table="flushUserTable" />

const openDialog = (title: string, rowData: UserEdit) => {
const params = {
title,
user: { ...rowData }
};
dialogRef.value?.acceptParams(params);
};

前端属性要设置为空,赋值为undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
Object.assign(userReq, dialogProps.value.user);
if (dialogProps.value.user.id) {
let roleLists = [];
for (let item of dialogProps.value.user.roleLists) {
roleLists.push(item.id);
}
userReq.roleLists = roleLists;
userReq.departId = dialogProps.value.user.coreDepartment.id;
} else {
userReq.orgId = undefined;
userReq.departId = undefined;
userReq.roleLists = [];
}

vue的三目运算符整理(?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<el-tree-select
class="select-width"
:placeholder="$t('form.selHolder').concat($t('user.orgName'))"
v-model="userReq!.orgId"
:props="defaultProps"
node-key="id"
:data="orgTreeList"
:filter-node-method="filterNodeMethod"
@change="getDepartList(1)"
check-strictly
:default-expanded-keys="userReq!.orgId?[userReq!.orgId]:[]"
:default-checked-keys="userReq!.orgId?[userReq!.orgId]:[]"
filterable
>
</el-tree-select>

一般el-select组件数据异步加载的编写方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<el-form-item :label="$t('user.deptName')" prop="depart">
<el-select
v-model="userReq!.departId"
filterable
:placeholder="$t('form.selHolder').concat($t('user.deptName'))"
class="select-width"
>
<el-option v-for="item in departList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>

const departList = ref<RoleInfo.DepartmentVO[]>([]);
//选中机构后的查询部门列表
const getDepartList = (flag?: number) => {
if (flag) {
//机构改变时清空部门id
userReq.departId = undefined;
}
const orgNumber = Number(userReq.orgId);
const orgIds = [orgNumber] as number[];
queryDepartmentVO({ ids: orgIds }).then(({ data }) => {
departList.value = data;
});
};

实现效果:

对象数组取对应属性,然后重新组成新数组的代码示例

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
// 接收父组件传过来的参数
const acceptParams = (params: DialogProps) => {
dialogProps.value = params;

if (dialogProps.value.user.id) {
userReq.orgId = dialogProps.value.user.coreOrg.id;
}

//初始化下拉框属性
initSelected();

Object.assign(userReq, dialogProps.value.user);
if (dialogProps.value.user.id) {
let roleLists = [];
for (let item of dialogProps.value.user.roleLists) {
roleLists.push(item.id);
}
userReq.roleLists = roleLists;
userReq.departId = dialogProps.value.user.coreDepartment.id;
} else {
userReq.orgId = undefined;
userReq.departId = undefined;
userReq.roleLists = [];
}

centerDialogVisible.value = true;
};

参考博客:

https://blog.csdn.net/weixin_49046027/article/details/123370324

键盘敲击触发事件,onkeyup事件(onkeydown和onkeypress),示例代码是回车事件

1
2
3
4
5
6
7
8
9
10
11
<el-form-item>
<el-input
:placeholder="$t('form.queryKey')"
v-model="userPropValue"
class="w-50 m-2"
:suffix-icon="Search"
size="large"
@blur="queryUserTableList"
@keyup.enter="queryUserTableList"
/>
</el-form-item>

vue判断字符串属性不是空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//根据条件查询用户信息
const queryUserTableList = async () => {
if (userProp.value == null || userProp.value.length == 0) {
ElMessage.error(t("form.selHolder"));
return;
}
getUserTableList();
};

//判断属性不为undefined
//判断orgId是否为空,不为空初始化部门列表
if (userReq.orgId) {
getDepartList();
}