当前位置: 首页 > news >正文

Vue - 基于Element UI封装一个表格动态列组件

1 组件需求背景

在后台管理系统中,表格的使用频率非常高,统一封装表格动态列组件并全局注册使用,可大大提升代码的复用性可维护性

2 全局注册

  • src/plugins/index.js:
import columns from './columns/index'export default {install(Vue) {// 动态列Vue.prototype.$columns = columns}
}
  • src/main.js:
import plugins from './plugins' // plugins
// 表格动态列组件
import DynamicColumn from '@/components/DynamicColumn'
Vue.component('DynamicColumn', DynamicColumn)
Vue.use(plugins)
  • 页面(Page.vue)中使用
 <DynamicColumnv-for="(ite, index) in columns":key="index":item="ite":data-list="infoList"/>infoList: [],
columns: this.$columns.getColumns('investmentDecision_list'),

Vue注册组件相关内容可见我的另外一篇博客:Vue - 组件注册及其原理

3 具体组件相关代码

  • src/plugins/columns/index.js:
const columns = {getColumns: function(columnType) {const list = columnList[columnType].listconst newColumn = []list.forEach((v, i) => {newColumn.push({...v,visible: v.visible === undefined ? true : v.visible, // 是否显示字段prop: v.prop, // 字段keylabel: v.label, // 字段名称align: v.align, // 对齐方式sortable: v.sortable, // 是否排序inputType: v.inputType, // 输入方式fixed: v.fixed, // 是否固定列tip: v.tip, // 是否列名解析width: v.width, // 列宽unit: v.unit, // 后缀,如"%"等keepDecimals: v.keepDecimals, // 保留小数位formatThousand: v.formatThousand, // 是否格式化千分位division: v.division, // 是否需要除数,如格式化万则需要除10000class: v.class, // 样式style: v.style, // 样式disabled: v.disabled, // 禁用controls: v.controls,tagTypeStyle: v.tagTypeStyle, // tag标签风格,函数类型,返回一个字符串,参考值为:default | primary | success | info | warning | dangerrender: v.render, // 渲染函数,返回一个处理后的值展示到页面中min: v.min, // 最小值max: v.max, // 最大值routerLink: v.routerLink,chartType: v.chartType, // Echart 图表类型,参考值为:1代表柱形折线图,2代表饼图chartData: v.chartData, // Echart 图表数据chartKey: v.chartKey, // Echart 图表Id的后缀columnTagText: v.columnTagText // 列标签标识})})return newColumn}
}export default columns
  • src/components/DynamicColumn.vue:
<!--动态列-->
<template><!-- :key="`${item.prop}+${item.label}`" --><!-- :show-overflow-tooltip="!item.chartType" --><el-table-columnv-if="item.visible":key="item.prop==='secCode'?Math.random():`${item.prop}+${item.label}`":prop="item.prop":label="item.label":fixed="item.fixed":min-width="getCellWidth(item,dataList)":align="item.align||'left'":show-overflow-tooltip="true":sortable="item.sortable"><template slot="header"><el-tooltipv-if="item.tip"class="item"effect="dark":content="item.tip"placement="top-start"><span><span v-if="item.valid" style="color: #ec051b; font-size: 1.5em">*</span><span>{{ item.label }}</span><i class="el-icon-question" /></span></el-tooltip><span v-else><span v-if="item.valid" style="color: #ec051b; font-size: 1.5em">*</span><span>{{ item.label }}</span></span></template><template slot-scope="scope"><el-inputv-if="item.inputType === 'input'"v-model="scope.row[item.prop]"size="mini":placeholder="$t('pleaseEnter')":disabled="item.disabled||(scope.row.disabled&&scope.row.disabled.includes(item.prop))"@change="onChange(scope.row,item)"@blur="onBlur(scope.row,item)"/><el-input-numberv-else-if="item.inputType === 'number'"v-model="scope.row[item.prop]"size="mini":placeholder="$t('pleaseEnter')":precision="item.keepDecimals":controls="item.controls":min="item.min":max="item.max":disabled="item.disabled||(scope.row.disabled&&scope.row.disabled.includes(item.prop))"@change="onChange(scope.row,item)"@blur="onBlur(scope.row,item)"/><el-selectv-else-if="item.inputType === 'select'"v-model="scope.row[item.prop]":placeholder="$t('pleaseSelect')"size="mini":disabled="item.disabled||(scope.row.disabled&&scope.row.disabled.includes(item.prop))"@click.native="lastValue = scope.row[item.prop]"@change="onChange(scope.row,item)"><el-optionv-for="ite in scope.row[item.prop + 'list'] || []":key="ite.value":label="ite.label":value="ite.value":disabled="ite.disabled"/></el-select><el-date-pickerv-else-if="item.inputType === 'date'"v-model="scope.row[item.prop]":format="item.format||'yyyy-MM-dd'":value-format="item.format||'yyyy-MM-dd'":type="item.dateType||'date'":placeholder="$t('pleaseSelect')":disabled="item.disabled||(scope.row.disabled&&scope.row.disabled.includes(item.prop))"size="mini"@change="onChange(scope.row,item)"/><el-switchv-else-if="item.inputType === 'switch'"v-model="scope.row[item.prop]":active-text="item.activeText":inactive-text="item.inactiveText":active-value="item.activeValue":inactive-value="item.inactiveValue"@change="onChange(scope.row,item)"/><el-radio-groupv-else-if="item.inputType === 'radio'"v-model="scope.row[item.prop]":disabled="item.disabled||(scope.row.disabled&&scope.row.disabled.includes(item.prop))"@change="onChange(scope.row,item)"><el-radiov-for="ite in scope.row[item.prop + 'List'] || []":key="ite.value":disabled="ite.disabled":label="ite.value">{{ ite.label }}</el-radio></el-radio-group><el-checkboxv-else-if="item.inputType === 'checkbox' && scope.row[item.prop]!==undefined"v-model="scope.row[item.prop]":disabled="item.disabled||(scope.row.disabled&&scope.row.disabled.includes(item.prop))"@change="onChange(scope.row,item)">{{ scope.row[item.prop+"Label"] }}</el-checkbox><el-tagv-else-if="item.tagTypeStyle":disable-transitions="true":type="item.tagTypeStyle(scope.row,scope.row[item.prop])||'primary'">{{ formatter(scope.row, item) }}</el-tag><router-linkv-else-if="!item.columnTagText && item.routerLink && item.routerLink(scope.row)"class="dy-router-link":to="item.routerLink(scope.row)"><span>{{ formatter(scope.row, item) }}</span></router-link><BarLineChartv-else-if="item.chartType==1 && scope.row[item.prop]":dom-id="`${scope.row[item.chartKey]}${item.prop}`":data="scope.row[item.prop]"height="50px"background-color="transparent"/><!-- <GridsBarLineChartv-else-if="item.chartType==2 && scope.row.chartData":dom-id="`${scope.row[item.chartKey]}${item.prop}`":data="scope.row.chartData"height="50px"/>--><divv-else-if="item.columnTagText":class="item.class":style="item.style"@click="handleClick(scope.row,item)"><div v-if="item.routerLink && item.routerLink(scope.row)"><router-link class="dy-router-link" :to="item.routerLink(scope.row)"><span>{{ formatter(scope.row, item) }}</span></router-link><div style="margin-top: -8px;"><el-tagv-if="item.columnTagText(scope.row)"type="danger"size="mini">{{ item.columnTagText(scope.row) }}</el-tag></div></div><div v-else>{{ formatter(scope.row, item) }}</div></div><spanv-else:class="item.class":style="item.style"@click="handleClick(scope.row,item)">{{ formatter(scope.row, item) }}</span></template></el-table-column>
</template><script>
import { moneyFormat, keepDecimals, isNumber } from '@/utils/utils'
import Sortable from 'sortablejs'
// 组件
import BarLineChart from '@/components/Echarts/BarLineChart'export default {components: {BarLineChart},props: {// 非必传,只有存在多个表格时,该参数作为表格的唯一标识,主要用于拖拽tableSign: {type: String,default: null},// 所有的列字段,用于拖拽schemas: {type: Array,default: () => {return []}},// 不在schemas内,并且在动态列之前的列数empty: {type: Number,default: 0},// 列字段item: {type: Object,default: () => {return {}}},// 自定义格式化方法format: {type: Function,default: null},// 自定义格式化方法对应的字段formatColumns: {type: Function,default: () => {return []}},// 表格数据dataList: {type: Array,default: () => []}},data() {return {lastValue: undefined // 上次的值}},mounted() {// 表格拖拽方法if (this.schemas.length > 0) {this.columnDrop()}},methods: {// 计算列宽(因为element自适应的宽度还是存在部分字段名称换行情况,所以需要重新计算)getCellWidth(item, dataList) {const { label, width: defaultWidth } = itemif (defaultWidth) {return defaultWidth}if (!label) {return 0}let renderTextMax = ''if (dataList.length > 0) {dataList.forEach((row) => {// 取内容文本的最大值const renderText = this.formatter(row, item) ?? ''this.calculateWidth(renderText) >this.calculateWidth(renderTextMax) && (renderTextMax = renderText)})}// 取表头和内容文本的较大值this.calculateWidth(label) > this.calculateWidth(renderTextMax) &&(renderTextMax = label)let width = 0const html = document.createElement('span')html.innerText = renderTextMaxhtml.className = 'getTextWidth'document.querySelector('body').appendChild(html)width = document.querySelector('.getTextWidth').offsetWidthdocument.querySelector('.getTextWidth').remove()if (width > 260) {return 280}return width + 20},// 计算字符宽度calculateWidth(renderText) {let flexWidth = 0for (let i = 0; i < String(renderText).length; i++) {const char = renderText[i]if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {// 如果是英文字符,为字符分配8个单位宽度flexWidth += 8} else if (char >= '\u4e00' && char <= '\u9fa5') {// 如果是中文字符,为字符分配16个单位宽度flexWidth += 16} else {// 其他种类字符,为字符分配8个单位宽度flexWidth += 8}}return flexWidth},// change事件,向父组件传递字段和值onChange(row, item) {row.oldValue = this.lastValue // 传选择之前的值this.setItemAndValue(this.$parent, row, item, 'columnChange')},// 点击事件,icon点击向父组件传递对应的字段信息handleClick(row, item) {this.setItemAndValue(this.$parent, row, item, 'columnClick')},// 失去焦点事件,icon点击向父组件传递对应的字段信息onBlur(row, item) {this.setItemAndValue(this.$parent, row, item, 'onBlur')},/*** @function setItemAndValue 向父组件传递相应的字段和值* @param {Object} parent 父组件原型* @param {Object} item 字段信息* @param {*} value 值* @param {String} functionType 函数类型*/setItemAndValue(parent, row, item, functionType) {if (parent && parent[functionType]) {parent[functionType](row, item)} else {if (parent.$parent) {this.setItemAndValue(parent.$parent, row, item, functionType)}}},// 格式化formatter(row, item) {// render优先级最高if (item.render) {return item.render(row, row[item.prop])}if (row[item.prop] === null ||row[item.prop] === undefined ||row[item.prop] === '') {// value为0或者false还是照样原来显示return '--'}let value = row[item.prop]if (this.format) {if (this.formatColumns.length > 0 &&this.formatColumns.includes(item.prop)) {// 指定自定义格式化的字段return this.format(row, item, row[item.prop])} else {// 未指定的往下走value = this.format(row, item, row[item.prop])}}if (isNumber(value)) {if (item.keepDecimals || item.keepDecimals === 0) {// 保留小数位value = keepDecimals(value, item.keepDecimals, item.division)}if (item.formatThousand) {value = moneyFormat(value, item.division) // 千分位}if (item.unit) {// 是否带单位,如万value = value + item.unit}}return value},/*** 列拖拽*/columnDrop() {// 页面多个表格 取到对应表格dom元素let wrapperClass = '.el-table__header-wrapper'if (this.tableSign) {const elClass = this.$parent.$el.getAttribute('class')if (!elClass.includes(this.tableSign)) {this.$parent.$el.setAttribute('class', `${elClass} ${this.tableSign}`)}wrapperClass = `.${this.tableSign} ${wrapperClass}`}const wrapperTr = document.querySelector(`${wrapperClass} tr`)this.sortable = Sortable.create(wrapperTr, {animation: 100, // 过渡动画delay: 0, // 延迟多久可以拖动onEnd: (evt) => {if (evt.oldIndex === evt.newIndex) returnconst overviewColumns = [...this.schemas]const visbleColumns = overviewColumns.filter((v) => v.visible) // 显示的列const empty = this.empty // 不在schemas内,并且在动态列之前的列数// 注意:动态列表包含visible为false数据,需要进行特殊筛选处理const oldItem = visbleColumns[evt.oldIndex - empty]const newItem = visbleColumns[evt.newIndex - empty]const realOldIndex = overviewColumns.findIndex((item) => item.prop === oldItem.prop)const realNewIndex = overviewColumns.findIndex((item) => item.prop === newItem.prop)overviewColumns.splice(realOldIndex, 1) // 删除原来位置的数据overviewColumns.splice(realNewIndex, 0, oldItem) // 在新的位置插入该数据this.$emit('changeColumn', overviewColumns)}})}}
}
</script>
<style lang="scss" scoped>
.el-table__row .el-form-item {margin-bottom: 0px !important;
}.el-input,
.el-input.is-disabled,
.el-range-editor.el-input__inner,
.el-date-editor.el-input {width: 100%;
}::v-deep.el-select > .el-input {width: 100%;
}::v-deep.el-input-number .el-input {width: 100%;
}
.el-input-number {width: 100%;
}.dy-router-link {color: #1890ff;display: inline-block;max-width: 100%;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;position: relative;&:hover:after {content: '';position: absolute;top: 0;right: 0;bottom: 0;left: 0;border-bottom: 1px solid #1890ff;}
}::v-deep .el-table__row {border: 1px solid #1890ff;.el-table .cell.el-tooltip {display: flex;align-items: center;}
}
</style>

相关文章:

Vue - 基于Element UI封装一个表格动态列组件

1 组件需求背景 在后台管理系统中&#xff0c;表格的使用频率非常高&#xff0c;统一封装表格动态列组件并全局注册使用&#xff0c;可大大提升代码的复用性和可维护性。 2 全局注册 src/plugins/index.js&#xff1a; import columns from ./columns/indexexport default …...

计算机网络:DNS域名解析系统

我最近开了几个专栏&#xff0c;诚信互三&#xff01; > |||《算法专栏》&#xff1a;&#xff1a;刷题教程来自网站《代码随想录》。||| > |||《C专栏》&#xff1a;&#xff1a;记录我学习C的经历&#xff0c;看完你一定会有收获。||| > |||《Linux专栏》&#xff1…...

java面试:==和equals有什么区别?

在 Java 中&#xff0c;"" 和 "equals" 有着不同的作用&#xff1a; "" 运算符&#xff1a; 在基本数据类型&#xff08;如 int、char 等&#xff09;中&#xff0c;"" 用于比较它们的值是否相等。 在引用类型中&#xff0c;"&q…...

数字人SaaS系统无限生成AI数字人!

市面上数字人软件层出不穷&#xff0c;选择一款适合的数字人软件是成功的第一步&#xff0c;只需要一款软件就解决数字人直播和数字人短视频的制作&#xff0c;青否数字人SaaS系统&#xff08;数字人源码&#xff1a;zhibo175&#xff09;你值得拥有&#xff01; 青否数字人Saa…...

【MySQL】——数据类型及字符集

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…...

Redis cluster集群设置密码

Redis cluster集群设置密码 1 备份数据 # 链接redis集群,执行rdb快照 bgsave # 备份dump.rdb文件 cp /data/redis/cluster/dump.rdb /data/redis/cluster/backup/dump.rdb.202312202 设置密码 必须保证每个节点的密码保持一致&#xff0c;不然 Redirected 的时候会失败 2.1…...

Docker 核心技术

Docker 定义&#xff1a;于 Linux 内核的 Cgroup&#xff0c;Namespace&#xff0c;以及 Union FS 等技术&#xff0c;对进程进行封装隔离&#xff0c;属于操作系统层面的虚拟化技术&#xff0c;由于隔离的进程独立于宿主和其它的隔离的进程&#xff0c;因此也称其为容器Docke…...

15 使用v-model绑定单选框

概述 使用v-model绑定单选框也比较常见&#xff0c;比如性别&#xff0c;要么是男&#xff0c;要么是女。比如单选题&#xff0c;给出多个选择&#xff0c;但是只能选择其中的一个。 在本节课中&#xff0c;我们演示一下这两种常见的用法。 基本用法 我们创建src/component…...

go语言指针变量定义及说明

go语言指针主要需要记住两个特殊符号&#xff0c; 一个是 & 用来获取变量对应的内存地址 另一个是 * 用来获取指针对应的变量值 下面是个最简单的go语言指针说明 package mainimport "fmt"//指针为内存地址func main() {var a string "指针对应的变量&…...

基于“Galera+MariaDB”搭建多主数据库集群的实例

1、什么是多主数据库集群 多主数据库集群是一种数据库集群架构&#xff0c;每个节点都可以接收写入操作和读取操作&#xff0c;并且通过心跳机制同步数据&#xff0c;保证数据一致性和高可用性。因多主数据库集群每个节点都可以承担读写操作&#xff0c;因此它可以充分利用各个…...

arcgis javascript api4.x加载天地图cgs2000坐标系

需求&#xff1a;arcgis javascript api4.x加载天地图cgs2000坐标系 效果&#xff1a; 示例代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"wid…...

算法学习——回溯算法

回溯算法 理论基础回溯法的效率回溯法解决的问题回溯法模板 组合思路回溯法三部曲 代码 组合&#xff08;优化&#xff09;组合总和III思路代码 电话号码的字母组合思路回溯法来解决n个for循环的问题回溯三部曲代码 组合总和思路代码 组合总和II思路代码 理论基础 什么是回溯法…...

C语言—小小圣诞树

这个代码会询问用户输入圣诞树的高度&#xff0c;然后根据输入的高度在控制台上显示相应高度的圣诞树。 #include <stdio.h>int main() {int height, spaces, stars;printf("请输入圣诞树的高度: ");scanf("%d", &height);spaces height - 1;st…...

Android消息公告上下滚动切换轮播实现

自定义控件 通过继承TextSwitcher实现 直接上代码 public class NoticesSwitcher extends TextSwitcher implements ViewSwitcher.ViewFactory {private Context mContext;private List<Notice> mData;private final long DEFAULT_TIME_SWITCH_INTERVAL 1000;//1spri…...

tensorflow入门 自定义模型

前面说了自定义的层&#xff0c;接下来自定义模型&#xff0c;我们以下图为例子 这个模型没啥意义&#xff0c;单纯是为了写代码实现这个模型 首先呢&#xff0c;我们看有几个部分&#xff0c;dense不需要我们实现了&#xff0c;我们就实现Res&#xff0c;为了实现那个*3,我们…...

虚拟机启动 I/O error in “xfs_read_agi+0x95“

1.在选择系统界面按e 进入维护模式 2.找到ro把ro改成 rw init/sysroot/bin/sh 然后按Ctrlx 3.找到坏掉的分区&#xff0c;以nvme0n1p3为例进行修复 xfs_repair -d /dev/nvme0n1p3 4.init 6 重新启动 以下情况 先umount 再修复 则修复成功...

【MYSQL】-库的操作

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …...

网络协议小记

一、TCP/IP协议 作为一个小萌新&#xff0c;当然我无法将tcp/ip协议的大部分江山和盘托出&#xff0c;但是其中很多面试可能问到的知识&#xff0c;我觉得有必要总结一下&#xff01; 首先&#xff0c;在学习tcp/ip协议之前&#xff0c;我们必须搞明白什么是tcp/ip协议。 1、…...

STM32-I2C通讯-AHT20温湿度检测

非常感谢&#xff0c;提供的视频学习 https://www.bilibili.com/video/BV1QN411D7ak/?spm_id_from333.788&vd_source8ca4826038edd44bb618801808a5e076 该文章注意&#xff1a;串口显示中文会乱码&#xff0c;必须选用支持ASCII的串口助手&#xff0c;才能正常显示中文。…...

【机器学习】043_准确率、精确率、召回率

一、定义 在处理偏斜数据集时&#xff0c;通常使用不同的误差度量&#xff0c;而不仅仅是使用分类误差来衡量算法性能。 1. 混淆矩阵的概念 二分类问题的混淆矩阵为2X2矩阵&#xff0c;由四部分组成&#xff1a; 假阴性&#xff08;FN&#xff09;&#xff1a;模型预测为负…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解

文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一&#xff1a;HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二&#xff1a;Floyd 快慢指针法&#xff08;…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...