【web系列十二】element plus的一些实用技巧

目录

写在前面

基本功能实现

 手动设置菜单激活状态

el-table:span-method

el-table树状表格递归显示数据及双击单元格修改功能

树状表格递归显示数据

双击单元格修改

el-upload

el-tabs

记录当前tab页

一些小技巧或坑

date-picker

table

form

参考资料


写在前面

        element-plus的官网上已经把各种组件的实用说明写的比较清楚了,并且由于element-plus功能很强大,属性、组件都很多,不可能全部整理下来,也没有必要,大家有需要的可以直接去官网查看。因此这篇文章主要记录一下element-plus组件的一些实用技巧和注意事项,同时会提供对应的代码示例。

        这里插上官网地址:

一个 Vue 3 UI 框架 | Element Plus (gitee.io)

menu+router

基本功能实现

        这里使用el-menu组件和vue-router实现菜单栏以及页面的跳转。

         官网上说设置了router属性后就可以使用vue-router实现跳转了,咱们来试一试。

<template>
  <el-menu
    router
    class="el-menu-demo"
    mode="horizontal"
    background-color="#545c64"
    text-color="#fff"
    active-text-color="#ffd04b"
  >
    <el-menu-item index="'/home/' + /">首页</el-menu-item>
    <el-menu-item index="'/home/' + /page1">页面1</el-menu-item>
    <el-menu-item index="'/home/' + /page2">页面2</el-menu-item>
    <el-menu-item index="'/home/' + /page3">页面3</el-menu-item>
  </el-menu>
  <router-view />
</template>

<script></script>
<style></style>

        这里要注意,index的路径要用字符串拼接的形式

index="'/home/' + url"

        同时要记得先配置好vue-route,router/index.ts

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

const routes = [
	{
		path: '/',
		name: 'Home',
		component: Home
	},
	{
		path: '/page1',
		name: 'Page1',
		component: () => import(/* webpackChunkName: "about" */ '../views/Page1.vue')
	},
	{
		path: '/page2',
		name: 'Page2',
		component: () => import('../views/Page2.vue')
	},
	{
		path: '/page3',
		name: 'Page3',
		component: () => import('../views/Page3.vue')
	}
]

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

export default router

        实际效果如下:

 手动设置菜单激活状态

         el-menu有一个属性是default-active,用来设置默认激活的菜单。咱们可以把他设置成一个ref变量,变量的内容是菜单的index,也就是下面el-menu-item的index属性内容。这样当变量改变时,对应的菜单就会显示成激活状态了。

<template>
  <el-menu
    :default-active="activeIndex"
    mode="horizontal"
  >
    <el-menu-item index="1">Processing Center</el-menu-item>
    <el-sub-menu index="2">
      <template #title>Workspace</template>
      <el-menu-item index="2-1">item one</el-menu-item>
      <el-menu-item index="2-2">item two</el-menu-item>
      <el-menu-item index="2-3">item three</el-menu-item>
      <el-sub-menu index="2-4">
        <template #title>item four</template>
        <el-menu-item index="2-4-1">item one</el-menu-item>
        <el-menu-item index="2-4-2">item two</el-menu-item>
        <el-menu-item index="2-4-3">item three</el-menu-item>
      </el-sub-menu>
    </el-sub-menu>
    <el-menu-item index="3" disabled>Info</el-menu-item>
    <el-menu-item index="4">Orders</el-menu-item>
  </el-menu>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const activeIndex = ref('1')
</script>

el-table:span-method

        使用span-method方法可以合并行或列,官网上的说明不是很详细,这边直接拿官网的示例解释一下,比如我们想实现以下效果,将id奇偶行合并。

         我们先给el-table添加一个span-method属性

<el-table
   :data="tableData"
   :span-method="objectSpanMethod"
   border
   style="width: 100%; margin-top: 20px"
>
</el-table>

         然后我们定义一个接口,这个其实不定义也是可以的。

  • User是表格数据的接口,请根据实际修改
  • row表示表格每一行的数据
  • column表示表格每一列的数据
  • rowIndex表示表格的行索引,不包括表头,从0开始
  • columnIndex表示表格的列索引,从0开始

         要注意这里的变量除了User,名称都不能修改!

interface SpanMethodProps {
  row: User
  column: TableColumnCtx<User>
  rowIndex: number
  columnIndex: number
}

        最后我们定义一下合并规则

        判断条件还是比较好理解的就不说了,主要是返回值,先说第二个return,两个0代表是空值,也就是被合并的格给他设成空值就行了;第一个return,rowspan表示合并的行数,colspan表示合并的列数。这里的合并规则就是当列索引时0时,也就是从第一列开始每两行每一列合并成一个格子,且被合并的单元格置空。

const objectSpanMethod = ({
  row,
  column,
  rowIndex,
  columnIndex,
}: SpanMethodProps) => {
  if (columnIndex === 0) {
    if (rowIndex % 2 === 0) {
      return {
        rowspan: 2,
        colspan: 1,
      }
    } else {
      return {
        rowspan: 0,
        colspan: 0,
      }
    }
  }
}

el-table树状表格递归显示数据及双击单元格修改功能

树状表格递归显示数据

         先来看看效果,这个是我们的数据,假设是一个描述图书馆藏书情况的多层嵌套yaml文件。

'历史':
	'中国':
		'清朝': ['2本', 'A1-2']
		'明朝': ['3本', 'A3-5']
	'外国':
		'法国': ['2本', 'B1-2']
		'美国':
			'独立战争': ['1本', 'B3']
'音乐':
	'钢琴': ['5本', 'C1-5']
	'古典': ['1本', 'C6']

        显示出来效果如下

        可以看到表格中的格式与我们的数据格式一致的,废话不多说,直接上代码:

<template>
  <div>
    <el-table
      :data="tableData"
      style="width: 100%; margin-bottom: 20px"
      row-key="id"
	  max-height="800"
      border
      default-expand-all
    > <!--max-height随便用来控制表格高度,用height不好使-->
      <el-table-column fixed prop="class" label="分类" sortable width="250" />
      <el-table-column prop="num" label="数量" sortable width="250" />
      <el-table-column prop="position" label="位置" sortable min-width="300" />
    </el-table>
  </div>
</template>

<script lang="ts" setup>
import axios from 'axios';
import { ref, onBeforeMount } from 'vue';

interface Books {
  id: number;
  class: string;
  num?: string;
  position?: string;
  children?: Books[];
}

onBeforeMount(() => {
	getMessage();
});

let table_data = ref<Books[]>([]);

function getMessage(): void {
	axios.get('/getMessage').then((response) => {
		let idx: number = 0; // 保证递归时的idx是全局的
		table_data.value.splice(0, table_data.value.length);
		
		display(response.data['message'], idx, table_data.value);
	})
}

function display(data: any, idx: number, idArr: Books[] = []): number {
	for(let key in data) {
		let value: any = data[key];
		let type: string = getType(value);
		
		if(type === "dict") {
			let child_books: Books[] = [];
			idArr.push({
				id: idx++,
				class: key,
				children: child_books
			});
			idx = display(value, idx, child_books);
		}
		else {
			idArr.push({
				id: idx++,
				class: key,
				index: -1,
				num: <string>(<unknown>value[0]),
				position: <string>(<unknown>value[1])
			});
		}
	}
	return idx;
}

function getType(data: any): string {
	let type = Object.prototype.toString.call(data);
	
	if(type === "[object Object]") return "dict";
	else {
		if(type === "[object Array]") return "list";
		else return "other";
	}
}
</script>

双击单元格修改

        也先来看看效果,双击某个单元格,可以编辑,但是不能编辑原本就不应该存在数据的单元格。

         在原来的代码上修改

<template>
  <div>
    <el-table
      :data="tableData"
      style="width: 100%; margin-bottom: 20px"
      row-key="id"
	  max-height="800"
      border
      default-expand-all
	  
	  @cell-dblclick="editData"
	  :cell-class-name="setCellClassName"
    > 
	  <!--用cell-dblclick绑定一个鼠标双击事件editData-->
	  <!--用cell-class-name给每个单元格绑定位置信息-->
      <el-table-column fixed prop="class" label="分类" sortable width="250">
		<template #default='scope'>
			<el-input
				class="el-input"
				size="default"
				v-model="scope.row.class"
				v-if="scope.row.index+','+scope.column.index == current_cell"
				autofocus="true"
				@blur="hideInput"
			/>
			<!--给一个class,为了让el-input尺寸小一些,避免换行-->
			<!--v-model绑定该单元格的数据-->
			<!--v-if和v-else成对使用,根据当前单元格位置信息切换显示状态-->
			<!--autofocus,显示的时候自动聚焦-->
			<!--blur,失去焦点时执行的函数-->
			<span v-else>{
   
   { scope.row.class }}</span>
		</template>
	  </el-table-column>
      <el-table-column prop="num" label="数量" sortable width="250">
		<template #default='scope'>
			<el-input
				class="el-input"
				size="default"
				v-model="scope.row.num"
				v-if="scope.row.index+','+scope.column.index == current_cell"
				autofocus="true"
				@blur="hideInput"
			/>
			<span v-else>{
   
   { scope.row.num }}</span>
		</template>
	  </el-table-column>
      <el-table-column prop="position" label="位置" sortable min-width="300">
		<template #default='scope'>
			<el-input
				class="el-input"
				size="default"
				v-model="scope.row.position"
				v-if="scope.row.index+','+scope.column.index == current_cell"
				autofocus="true"
				@blur="hideInput"
			/>
			<span v-else>{
   
   { scope.row.position }}</span>
		</template>
	  </el-table-column>
    </el-table>
  </div>
</template>

<script lang="ts" setup>
import axios from 'axios';
import { ref, onBeforeMount } from 'vue';
import { toRaw } from '@/vue/reactivity';

interface Books {
  id: number;
  class: string;
  num?: string;
  position?: string;
  index?: number;   //增加一个index用于当前的row位置信息
  children?: Books[];
}

onBeforeMount(() => {
	getMessage();
});

let table_data = ref<Books[]>([]);
let current_cell = ref<string | null>(null);   // 记录双击选中的单元格位置信息

function getMessage(): void {
	axios.get('/getMessage').then((response) => {
		let idx: number = 0; // 保证递归时的idx是全局的
		table_data.value.splice(0, table_data.value.length);
		
		display(response.data['message'], idx, table_data.value);
	})
}

function display(data: any, idx: number, idArr: Books[] = []): number {
	for(let key in data) {
		let value: any = data[key];
		let type: string = getType(value);
		
		if(type === "dict") {
			let child_books: Books[] = [];
			idArr.push({
				id: idx++,
				class: key,
				children: child_books
			});
			idx = display(value, idx, child_books);
		}
		else {
			idArr.push({
				id: idx++,
				class: key,
				index: -1,
				num: <string>(<unknown>value[0]),
				position: <string>(<unknown>value[1])
			});
		}
	}
	return idx;
}

function getType(data: any): string {
	let type = Object.prototype.toString.call(data);
	
	if(type === "[object Object]") return "dict";
	else {
		if(type === "[object Array]") return "list";
		else return "other";
	}
}

// 在初始化表格的时候就给每个单元格赋予位置信息
function setCellClassName({ row, column, rowIndex, columnIndex }): void {
	row.index = rowIndex;
	column.index = columnIndex;
}

// 双击后先判断当前单元格是否可能存在数据,可能的话,把current_cell置为当前单元格的位置
function editData(row, colunm): void {
	if(row.children != undefined &&column.index != 0) {}
	else {
		current_cell.value = row.index + ',' + column.index;
	}
}

// 失去焦点后,把current_cell置为null
function hideInput(row): void {
	current_cell.value = null;
}
</script>

<style scope>
.el-input {
	width: 80%;
}
</style>

el-upload

        这个组件用于文件上传,先上一些参考文章

vue+element upload上传带参数的方法 - 开发技术 - 亿速云 (yisu.com)

ElementUI upload 文件自定义上传 和 文件自定义分块上传 - 简书 (jianshu.com)

(2条消息) vue中,Upload上传组件el-upload的使用-zip格式,大小不超过10M & store中获取和保存token_viceen的博客-CSDN博客

        这边列一个示例

<template>
    <div class="algo">
        <el-upload
            ref="upload"
			:action="action"                       <!--上传请求的服务器url,建议设成变量-->
			:data="upload_data"                    <!--请求时发送额外参数,建议设成变量-->
			:limit="1"                             <!--限制上传文件的数量-->
			:on-exceed="handleExceed"              <!--当超出限制时,执行的钩子函数,在这里就是当触发了limit后执行-->
			:on-success="handleSuccess"            <!--请求成功后执行的函数,相当于axios的then-->
			:on-error="handleError"                <!--请求失败后执行的函数,相当于axios的catch-->
			:beforeUpload="handleBeforeUpload"     <!--发送请求前执行的函数,在这里可以做一些条件判断,配置参数等-->
			:auto-upload="false"                   <!--不自动上传-->
        >
			<template #trigger>
				<el-button type="primary">选择文件</el-button>
			</template>
			<el-button type="success" @click="submitUpload">上传</el-button>
		</el-upload>
    </div>
</template>

<script lang="ts" setup>
import axois from "axios";
import { reactive } from 'vue';
import { genFileId } from 'element-plus';
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';

let action = ref<string>(axios.defaults.baseURL + '/upload');   // 这样就可以像发送其他axios请求一样,用axios的默认baseURL,而不用输一遍服务器地址,当然这个默认值需要提前配置的
let upload_data = reactive<string>({});  // 直接设置成对象,方便后面添加内容
const upload = ref<UploadInstance>();    // 这时上传对象的实例

// 点击后会发送提交请求,但是在发送请求前会先调用handleBeforeUpload函数
function submitUpload(): void {
  upload.value!.submit();
}

// 这个函数必须返回boolean参数,当返回false时,会自动停止发送上传请求
function handleBeforeUpload(file: UploadRawFile): boolean {
	const type = file.name.split('.')[file.name.split('.').length - 1]; // 注意这里不能像python一样写-1
	if(type !== 'zip') {                        // 要求上传的时zip
		alert("必须上传zip格式的文件!");
		return false;
	}
	
	upload_data['a'] = '111';                   // 在这里配置参数
	return true;
}

// 移除之前上传的内容,并用新的内容替代
function handleExceed: UploadProps['onExceed'] = (files) => {
  upload.value!.clearFiles();
  const file = files[0] as UploadRawFile;
  file.uid = genFileId();
  upload.value!.handleStart(file);
}

function handleSuccess(response: any):void {
	console.log(response);
}

function handleError(error: Error):void {
	alert(error);
}
</script>

el-tabs

记录当前tab页

        使用el-tabs当切换页面是,其实是无法获取到当前处于哪一个页面的。我们可以给el-tabs添加一个点击触发的事件,在事件内由全局变量记录当前页面。但是这样会带来一个问题就是,el-tabs生成时不会激活任何页面,因此要给el-tabs绑定一个v-model,值就等于被激活页面的name属性的值。代码如下:

<template>
	<div class="test">
		<el-tabs v-model="store.cur_tab" type="border-card" @tab-click="handleClick">
			<el-tab-pane label="first" name="first">
				<p>first</p>
			</el-tab-pane>
			<el-tab-pane label="second" name="second">
				<p>second</p>
			</el-tab-pane>
		</el-tabs>
	</div>
</template>

<script lang="ts" setup>
import { mainStore } from '@/store';
import { toRaw } from '@vue/reactivity';
import type { TabsPaneContext } from 'element-plus';

let store = mainStore();

function handleClick(tab: TabsPaneContext, event: Event): void {
	store.cur_tab = toRaw(toRaw(tab).props).name;
}
</script>

<style></style>

一些小技巧或坑

  1. element属性的值如果是函数或变量,则需要在属性前加上冒号
  2. element事件前要加上@

date-picker

  1. 默认的日期格式是Fri Jul  1 2022 00:00:00 GMT+0800(中国标准时间),增加value-format="YYYY-MM-DD HH:mm:ss"属性可以转换成2022-07-01 00:00:00格式
  2. 宽度控制:用css设置样式无法控制宽度,可以在元素中增加一个style属性
    <el-date-picker style="width:100%;"</el-date-picker>
    
    <el-date-picker style="width:250px;"</el-date-picker>

table

  1. 如果所有的el-table-column都设置了width,则可能出现无法占满整个屏幕的现象,这时只要把其中一个el-table-column的width改成min_width就可以了。
  2. fixed属性可以固定某个el-table-column,还可以通过赋值为'left' 或 'right'将某一列固定在左边或右边。
  3. 刷新table:给table添加key属性并绑定一个ref变量,当更新了data时,我们修改一下key对应的变量值,就可以触发table的重新渲染,将新的数据显示出来。
  4. 动态的嵌套表格vue+elementUI表格嵌套表单,包含联级下拉框、动态增加行_小佩丫的博客-CSDN博客_element表格嵌套表单理解element-ui中的slot-scope的理解_dongdongdongJL的博客-CSDN博客_element slot-scope

form

vue+element UI表单验证 - 走看看 (zoukankan.com)

未完待续。。。

参考资料

组件 | Element

 vue+elementUI表格嵌套表单,包含联级下拉框、动态增加行_小佩丫的博客-CSDN博客_element表格嵌套表单

 理解element-ui中的slot-scope的理解_dongdongdongJL的博客-CSDN博客_element slot-scope

 vue+element upload上传带参数的方法 - 开发技术 - 亿速云 (yisu.com)

 ElementUI upload 文件自定义上传 和 文件自定义分块上传 - 简书 (jianshu.com)

 vue中,Upload上传组件el-upload的使用-zip格式,大小不超过10M & store中获取和保存token_viceen的博客-CSDN博客

vue+element UI表单验证 - 走看看 (zoukankan.com)

猜你喜欢

转载自blog.csdn.net/Nichlson/article/details/125727294