基于el-table的表格点选和框选功能

news2024/9/30 7:30:12

开篇

本篇文章旨在实现一个基于el-table的表格点选和框选功能,除此之外,还支持多种模式的切换、自定义勾选日期等。且,该表格后续可能还会持续优化!

功能介绍

  • 表格点选和框选功能(没有点击ctrl键的情况下)
  • 表格点选和框选功能的互斥功能,意思是当现在选择了该单元格后,按住ctrl键再次点击,就会取消选择
  • 当前日期高亮
  • 周、双周、月份模式的切换以及自定义日期的选择
  • 对于可点选和框选功能的范围判断(表头和名称不可选择)

代码实现

index.vue

<template>
  <div class="container">
    <!-- 顶部功能栏区域 -->
    <data class="top-bar">
      <!-- 切换模式按钮 -->
      <el-button size="mini" type="primary" @click="turnWeek"></el-button>
      <el-button size="mini" type="primary" @click="turnTwoWeeks"
        >两周</el-button
      >
      <el-button size="mini" type="primary" @click="turnMonth"></el-button>
      <!-- 自定义时间范围选择器 -->
      <div class="time-range-picker">
        <span class="arrow-icon">
          <el-icon><ArrowLeftBold /></el-icon>
        </span>
        <!-- 起始日期 -->
        <el-date-picker
          class="time-picker"
          v-model="dateForm.startDate"
          type="datetime"
          :shortcuts="shortcuts"
          @change="(value) => handleDateChange('startDate', value)"
        />
        <span style="padding: 0 10px">-</span>
        <!--结束日期 -->
        <el-date-picker
          class="time-picker"
          v-model="dateForm.endDate"
          type="datetime"
          :shortcuts="shortcuts"
          @change="(value) => handleDateChange('endDate', value)"
        />
        <span class="arrow-icon">
          <el-icon><ArrowRightBold /></el-icon>
        </span>
      </div>
    </data>
    <!-- 表格区域 -->
    <div class="table-container">
      <!-- 月份选择 -->
      <month-schedule-table
        :table-data="tableData"
        :switch-model="switchModel"
        :dateForm="dateForm"
        :customDateFlag="customDateFlag"
        @updateDateForm="handleUpdateDateForm"
        :table-width="tableWidth"
      ></month-schedule-table>
    </div>
  </div>
</template>

<script>
import {
  defineComponent,
  onMounted,
  reactive,
  toRefs,
  ref,
  watchEffect,
  onUpdated,
  onUnmounted,
  nextTick,
} from "vue";
import MonthScheduleTable from "./components/MonthScheduleTable.vue";
export default defineComponent({
  name: "DepartmentScheduleConfig",
  components: {
    MonthScheduleTable,
  },
  setup() {
    const state = reactive({
      tableData: [],
      switchModel: "month",
      // 自定义日期选择相关
      dateForm: {
        startDate: "",
        endDate: "",
      },
      customDateFlag: false, // 是否在自定义切换日期的标识
    });

    const tableWidth = ref(0);
    onMounted(() => {
      // 先初始化一些假数据用着
      initData();

      nextTick(() => {
        getTableWidth();
      });
    });

    const getTableWidth = () => {
      // 求表格宽度
      let container = document.querySelector(".container");
      if (container) {
        const observer = new ResizeObserver(() => {
          tableWidth.value = container.clientWidth;
        });

        observer.observe(container);

        // 初始获取一次宽度
        tableWidth.value = container.clientWidth;
      }
    };

    // 每次更新表格时,也要重新获取表格宽度
    onUpdated(() => {
      getTableWidth();
    });

    window.addEventListener("resize", getTableWidth);

    // 页面关闭时,移除事件监听器
    onUnmounted(() => {
      window.removeEventListener("resize", getTableWidth);
    });

    // 切换成周模式
    const turnWeek = () => {
      state.switchModel = "week";
      state.customDateFlag = false;
    };

    // 切换成两周模式
    const turnTwoWeeks = () => {
      state.switchModel = "twoWeeks";
      state.customDateFlag = false;
    };

    // 切换成月模式
    const turnMonth = () => {
      state.switchModel = "month";
      state.customDateFlag = false;
    };

    // 初始化数据
    const initData = () => {
      const obj = {};

      for (let i = 0; i <= 31; i++) {
        obj[`date${i}`] = null;
      }

      const arr = [];
      for (let j = 1; j < 10; j++) {
        const tmpObj = { ...obj };
        tmpObj.id = j;
        tmpObj.name = `zzz${j}`;
        arr.push(tmpObj);
      }
      console.log("arr", arr);
      arr[0].date1 = "X";
      arr[3].date3 = "Y";
      state.tableData = arr;
    };

    /**
     * 自定义日期相关
     */
    // 日期选择面板扩展
    const shortcuts = [
      {
        text: "今天",
        value: new Date(),
      },
      {
        text: "昨天",
        value: () => {
          const date = new Date();
          date.setDate(date.getDate() - 1);
          return date;
        },
      },
      {
        text: "一周前",
        value: () => {
          const date = new Date();
          date.setDate(date.getDate() - 7);
          return date;
        },
      },
    ];

    // 日期change方法
    const handleDateChange = (key, value) => {
      state.dateForm[key] = new Date(value);
      console.log("dateForm", state.dateForm);
      state.customDateFlag = true;
    };

    // 更新日期选择器
    const handleUpdateDateForm = (val) => {
      console.log("val", val);
      state.dateForm["startDate"] = val.startDate;
      state.dateForm["endDate"] = val.endDate;
    };

    return {
      ...toRefs(state),
      turnWeek,
      turnTwoWeeks,
      turnMonth,
      tableWidth,
      shortcuts,
      handleDateChange,
      handleUpdateDateForm,
    };
  },
});
</script>

<style style="scss" scoped>
.container {
  width: 100%;
  .top-bar {
    display: flex;
    .time-range-picker {
      margin-left: 5px;
      .arrow-icon {
        cursor: pointer;
        padding: 8px;
      }
    }
  }
  .table-container {
    width: 100%;
    margin-top: 5px;
  }
}
</style>

MonthScheduleTable.vue

<template>
    <div class="container">
        <!-- 表格区域 -->
        <data 
            class="wrap"
            @mousedown="handleMouseDown"
            @mousemove="handleMouseMove"
            @mouseup="handleMouseUp"
        >
            <!-- 框选矩形 -->
            <div 
                v-if="rectVisible" 
                class="select-rect"
                :style="{ left: rect.left + 'px', top: rect.top + 'px', width: rect.width + 'px', height: rect.height + 'px' }"
            ></div>

            <!-- 表格 -->
            <div class="con">
                <el-table 
                    :data="tableData" 
                    border 
                    style="width: 100%" 
                    :cell-style="CellStyle"
                    @cell-click="handleCellClick">
                    <!-- 姓名 -->
                    <el-table-column prop="name" label="姓名" :width="getColumnWidth()"> </el-table-column>
                    <!-- 
                        日期 
                        这一块日期应该根据月、周、两周三种模式来动态显示,不应该写死
                    -->
                    <el-table-column
                        class="headerSelect"
                        :label-class-name="isWeekend(item.date, item.day)"
                        v-for="(item, index) in currDateArr" 
                            :key="index" 
                            :label="item.date"
                        >
                        <el-table-column 
                            :label-class-name="isWeekend(item.date, item.day)"
                            :class-name="isWeekend(item.date, item.day)"
                            :prop="getProp(index)" 
                            :label="item.day" 
                            :class="{'headerSelect': true}"
                            :width="getColumnWidth()">
                            <template #default="scope">
                                <div :data-row="scope.row.name" :data-column="getProp(index)" class="cell-content">
                                    {{ scope.row[getProp(index)] }}
                                </div>
                            </template>
                        </el-table-column>
                    </el-table-column>
                </el-table>
            </div>
        </data>
    </div>
</template>

<script>
import { computed } from '@vue/reactivity';
import { defineComponent, onMounted, reactive, toRefs, watchEffect, ref, onUpdated } from 'vue'
export default defineComponent({
    name: 'MonthScheduleTable',
    emits: ['updateDateForm'],
    props: {
        tableData: {
            type: Array,
            default: () => []
        },
        switchModel: {
            type: String,
            default: 'month'
        },
        tableWidth: {
            type: Number,
            default: 0
        },
        dateForm: {
            type: Object,
            default: () => ({})
        },
        customDateFlag: {
            type: Boolean,
            default: false
        }
    },
    setup(props, { emit }) {
        const state = reactive({
            tableData: computed(() => props.tableData), // 数组数据源
            // 鼠标框选功能相关
            selectedCells: [], // 选择的单元格
            rectVisible: false, // 是否显示框选矩形
            rect: {left: 0, top: 0, width: 0, height: 0}, // 矩形的坐标信息
            downX: 0, // 鼠标按下时的X坐标
            downY: 0, // 鼠标按下时的Y坐标
            upX: 0, // 鼠标松开时的X坐标
            upY: 0, // 鼠标松开时的Y坐标
            isMouseDown: false, // 鼠标是否移动
            isCellClick: false,// 是否是单元格被点击了
        })

        // 给当前的单元格分配prop
        const getProp = idx => {
            return `date${idx}`
        }

        // 判断鼠标是否在可选区域内
        const isInSelectableArea = event => {
            const target = event.target;
            // 查找最近的表头元素(th 或 thead)
            const headerElement = target.closest('th, thead');
            // 如果目标元素位于表头中,返回 false
            if (headerElement) return false;

            const headerSelect = document.querySelector('.con')
            if (!headerSelect) return false;

            const headerRect = headerSelect.getBoundingClientRect()
            const isInHeader = 
                event.clientX >= headerRect.left && 
                event.clientX <= headerRect.right &&
                event.clientY >= headerRect.top && 
                event.clientY <= headerRect.bottom;

            const cell = target.closest('td, th');
            const columnIndex = cell ? cell.cellIndex : undefined;
            
            return isInHeader && columnIndex > 0; // 从第二列开始
        }


        // 判断当前是否只点击了一个单元格
        // 表格单元格点击事件
        const handleCellClick = (row, column, cell, event) => {
            if (!isInSelectableArea(event)) return;

            state.isCellClick = true

            if (event.ctrlKey) { // 判断是否按下了Ctrl键
                // 当鼠标左键+ctrl同时按下时,实现多选功能
                /**
                 * 若当前的cell.classList包含highlight类,则移除,并把该单元格的数据移除出数组;
                 * 若不包含,证明之前并未选择过,则添加hightlight类,并把数据push进数组
                 */
                if (cell.classList.contains('highlight')) {
                    cell.classList.remove('highlight')
                    // 将该单元格的数据移出数组
                    const index = state.selectedCells.findIndex(item => item.row === row.name && item.column === column.property)
                    if (index > -1) {
                        state.selectedCells.splice(index, 1)
                    }
                } else {
                    cell.classList.add('highlight')
                    // 将数据加入数组
                    state.selectedCells.push({ row: row.name, column: column.property, value: row[column.property] });
                }
            } else {
                // 普通高亮的逻辑
                // 清除所有已高亮的单元格
                const highlightedCells = document.querySelectorAll('.highlight')
                highlightedCells.forEach(cell => cell.classList.remove('highlight'))
                // 清空当前已选择的数组
                state.selectedCells = []
                // 将当前单元格高亮
                cell.classList.add('highlight')
                // 将数据加入数组
                state.selectedCells.push({ row: row.name, column: column.property, value: row[column.property] });
            }

            // 将单元格点击标识和鼠标移动标识置为false
            state.isCellClick = false
            state.isMouseDown = false
        }

        // 当鼠标落下时
        const handleMouseDown = event => {
            if (!isInSelectableArea(event)) return;// 判断是否在可选区域内

            state.isMouseDown = true
            /**
             * 在鼠标落下时,应当判断是ctrl+鼠标左键触发的事件,还是直接由鼠标左键触发的事件
             * 若是直接由鼠标左键点击触发的事件,则应该清空当前的selectedCells数组,并移除所有单元格的的高亮
             */
            if (!event.ctrlKey) {
                const highlightedCells = document.querySelectorAll('.highlight')
                highlightedCells.forEach(cell => cell.classList.remove('highlight'))

                state.selectedCells = []
            }
            state.rectVisible = true
            state.downX = event.clientX
            state.downY = event.clientY
            state.upX = event.clientX
            state.upY = event.clientY

            state.rect.left = document.querySelector('.wrap').getBoundingClientRect().left
            state.rect.top = document.querySelector('.wrap').getBoundingClientRect().top
            state.rect.width = 0
            state.rect.height = 0
        }

        // 当鼠标移动时
        const handleMouseMove = event => {
            if (!state.rectVisible || !isInSelectableArea(event)) return;// 判断是否在可选区域内

            if (state.rectVisible) {
                const moveX = event.clientX
                const moveY = event.clientY
                // 计算框选矩形的宽高
                state.rect.width = Math.abs(moveX - state.downX)
                state.rect.height = Math.abs(moveY - state.downY)
                state.rect.left = Math.min(moveX, state.downX) - event.currentTarget.getBoundingClientRect().left
                state.rect.top = Math.min(moveY, state.downY) - event.currentTarget.getBoundingClientRect().top
            }
        }

        // 当鼠标抬起时
        const handleMouseUp = (event) => {
            if (!state.rectVisible || !isInSelectableArea(event)) return;// 判断是否在可选区域内

            if (state.rectVisible) {
                state.rectVisible = false

                // 获取所有单元格
                const cells = document.querySelectorAll('.el-table__body-wrapper td')
                const rect = state.rect

                // 判断是否有一些单元格已经高亮
                let anyHighlighted = false

                // 用于存放被框选的单元格
                const selectedCells = []

                cells.forEach(cell => {
                    const cellRect = cell.getBoundingClientRect()
                    const tableRect = document.querySelector('.wrap').getBoundingClientRect()

                    // 计算相对位置
                    const cellLeft = cellRect.left - tableRect.left
                    const cellTop = cellRect.top - tableRect.top

                    // 判断单元格是否在框选区域内
                    const cellInSelection = (
                        cellLeft < rect.left + rect.width &&
                        cellLeft + cellRect.width > rect.left &&
                        cellTop < rect.top + rect.height &&
                        cellTop + cellRect.height > rect.top
                    )

                    if (cellInSelection) {
                        selectedCells.push(cell)
                    }
                })

                
                if (selectedCells.length > 1) {
                    selectedCells.forEach(sltCell => {
                        // 判断单元格是否已经高亮
                        const isHighlighted = sltCell.classList.contains('highlight')
                        if (isHighlighted) {
                            anyHighlighted = true
                        }
                        // 若使用ctrl+鼠标左键
                        if (event.ctrlKey) {
                            // 若被框选的单元格全都没有高亮,则将这些单元格高亮,并将数据push到数组中
                            if (!anyHighlighted) {
                                sltCell.classList.add('highlight')
                                state.selectedCells.push(getCellData(sltCell))
                            } else {
                                /**
                                 * 若被框选的单元格中,有已经高亮的单元格,则需要把其中高亮的单元格取消高亮,并把这些被取消高亮
                                 * 的单元格的数据从数组中移除
                                 * 同时,把没有高亮的单元格高亮,并将数据push到数组中
                                 */
                                if (isHighlighted) {
                                    sltCell.classList.remove('highlight')
                                    const idxToRemove = state.selectedCells.findIndex(sc => sc.row === getCellData(sltCell).row && sc.column === getCellData(sltCell).column)
                                    if (idxToRemove > -1) {
                                        state.selectedCells.splice(idxToRemove, 1)
                                    }
                                } else {
                                    // 若当前没有高亮的,则高亮,并把数据添加到数组中
                                    sltCell.classList.add('highlight')
                                    state.selectedCells.push(getCellData(sltCell))
                                }
                            }
                        } else {
                            // 普通点击框选事件
                            sltCell.classList.add('highlight')
                            state.selectedCells.push(getCellData(sltCell))
                        }                            
                    })
                }
            }
        }

        // 获取单元格数据
        const getCellData = cell => {
            const cellContent = cell.querySelector('.cell-content')
            if (cellContent) {
                const row = cellContent.dataset.row
                const column = cellContent.dataset.column
                const value = cellContent.textContent

                return { row, column, value }
            }
        }

        // 根据当前的模式,动态获取数据
        const daysOfWeek = ['日', '一', '二', '三', '四', '五', '六' ]

        // 月份模式
        const getMonthDays = () => {
            const days = []

            let currYear = new Date().getFullYear()
            let currMonth = new Date().getMonth()
            // 获取当前月的第一天
            const startDate  = new Date(currYear, currMonth, 2)
            startDate.setHours(0, 0, 0, 0)  // 确保是当天的0点

            // 获取当前月的最后一天
            const endDate = new Date(currYear, currMonth + 1, 0)
            endDate.setHours(23, 59, 59, 999)  // 确保是当天的最后一毫秒

            const date = new Date(new Date(currYear, currMonth, 1))
            while(date.getMonth() === currMonth) {
                days.push({
                    day: date.getDate().toString(),
                    date: daysOfWeek[date.getDay()]
                })
                date.setDate(date.getDate() + 1)
            }

            // 转化为时间选择器可以使用的格式
            const minDateFormatted = startDate.toISOString().slice(0, 10)
            const maxDateFormatted = endDate.toISOString().slice(0, 10)

            emit('updateDateForm', { startDate: minDateFormatted, endDate: maxDateFormatted })

            return days
        }

        // 一周模式
        const getWeekDays = () => {
            const days = []

            const currDay = new Date().getDay()
            const startDate = new Date(new Date()) 
            // 当选择了这些模式之后,应该把开始日期和结束日期传给父组件,以便父组件上的时间选择器来展示
            // 找到最小和最大的日期
            startDate.setDate(new Date().getDate() - currDay + 1) // 获取当前周的周一
            // 获取当前格式的当前周的周日
            const endDate = new Date(startDate)
            endDate.setDate(startDate.getDate() + 6)
            
            for (let i = 0; i < 7; i++) {
                const d = new Date(startDate)
                d.setDate(startDate.getDate() + i)
                days.push({
                    day: d.getDate().toString(),
                    date: daysOfWeek[d.getDay()]
                })
            }

            // 转化为时间选择器可以使用的格式
            const minDateFormatted = startDate.toISOString().slice(0, 10)
            const maxDateFormatted = endDate.toISOString().slice(0, 10)

            emit('updateDateForm', { startDate: minDateFormatted, endDate: maxDateFormatted })

            return days
        }

        // 两周模式
        const getTwoWeeksDays = () => {
            const days = []

            const currDay = new Date().getDay()
            const startDate = new Date(new Date()) 
            startDate.setDate(new Date().getDate() - currDay + 1) // 获取当前周的周一
            // 获取当前格式的当前周的周日
            const endDate = new Date(startDate)
            endDate.setDate(startDate.getDate() + 13)
            
            for (let i = 0; i < 14; i++) {
                const d = new Date(startDate)
                d.setDate(startDate.getDate() + i)
                days.push({
                    day: d.getDate().toString(),
                    date: daysOfWeek[d.getDay()]
                })
            }

            // 转化为时间选择器可以使用的格式
            const minDateFormatted = startDate.toISOString().slice(0, 10)
            const maxDateFormatted = endDate.toISOString().slice(0, 10)

            emit('updateDateForm', { startDate: minDateFormatted, endDate: maxDateFormatted })

            return days
        }

        // 自定义选择日期模式
        const getCustomDateRange = (startDate, endDate) => {
            const days = []

            const start = new Date(startDate)
            const end = new Date(endDate)
            const date = new Date(start)

            while (date <= end) {
                days.push({
                    day: date.getDate().toString(),
                    date: daysOfWeek[date.getDay()]
                })
                date.setDate(date.getDate() + 1)
            }

            return days
        }

        // 获取当前日期
        const isCrrentDay = () => {
            let d = new Date().getDate()
            return d.toString()
        }

        // 判断是否是周末
        const isWeekend = (date, day) => {
            if (day === isCrrentDay()) return 'currDay';
            if (date === '六' || date === '日') return 'weekend';
            else return ''
        }

        

        const headerCellStyle = (row, column, rowIndex, columnIndex) => {
            // console.log('row', row);
        }

        const CellStyle = (row, column) => { 
            // console.log('row', row);
            if (row.column.className === 'weekend') {
                return {
                    backgroundColor: 'rgb(116, 107, 230)'
                }
            }
            
        }

        const currDateArr = computed(() => {
            if (!props.customDateFlag && props.switchModel === 'month') {
                return getMonthDays()
            } else if (!props.customDateFlag && props.switchModel === 'week') {
                return getWeekDays()
            } else if (!props.customDateFlag && props.switchModel === 'twoWeeks') {
                return getTwoWeeksDays()
            } else if (props.customDateFlag) {
                return getCustomDateRange(props.dateForm.startDate, props.dateForm.endDate)
            }
        })

        var currWidth = ref(0)
        watchEffect(() => {
            currWidth.value = computed(() => props.tableWidth).value

            // 根据当前日期,给表头设置背景
            if (currDateArr.value.length > 0) {
                const ths = document.querySelectorAll('.el-table__header .el-table__cell')
                if (ths.length > 0) {
                    // 获取当前日期
                    let date = new Date().getDay()
                    console.log('date', date);
                }
            }
        })

        onUpdated(() => {
            // 根据当前日期,给表头设置背景
            if (currDateArr.value.length > 0) {
                const ths = document.querySelectorAll('.el-table__header .el-table__cell')
                if (ths.length > 0) {
                    // 获取当前日期
                    let date = new Date().getDay()
                }
            }
        })

        // 动态设置列的宽度
        const getColumnWidth = () => {
            const containerWidth = currWidth.value // 减去name列的值
            const columnCount = currDateArr.value.length + 1
            return `${Math.floor(containerWidth / columnCount)}px`
        }

        return {
            ...toRefs(state),
            handleCellClick,
            currDateArr,
            getProp,
            handleMouseDown,
            handleMouseMove,
            handleMouseUp,
            getColumnWidth,
            isWeekend,
            headerCellStyle,
            CellStyle
        }
    }
})
</script>

<style style="scss" scoped>
/* 当单元格被选择时,高亮 */
::v-deep .el-table td.highlight {
  background-color: yellow!important;
  color: red;
}

::v-deep .el-table thead th.weekend {
    background-color: rgb(116, 107, 230)!important;
}

::v-deep .el-table th.currDay {
    background-color: green!important;
}

::v-deep .el-table .headerSelect {
  background-color: green!important;
}

.container {
    width: 100%;
    .wrap {
        width: 100%;
        height: 100vh;
        position: relative;
        display: flex;
        /* 子项超出容器宽度时自动换行 */
        flex-wrap: wrap;
        /* 禁止用户复制文本 */
        user-select: none;
        .select-rect {
            position: absolute;
            border: 1px dashed #999;
            background-color: rgba(0,0,0,0.1);
            z-index: 1000;
            pointer-events: none;
        }

        .con {
            max-width: 100%;
        }
    }
}
</style>

效果演示

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

因为此表格还在持续开发中,所以现在代码还有很多有待优化和完善的地方,还请谅解。
希望本文对您能有所帮助!
感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1991600.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI安全新纪元:智能体驱动的网络安全新范式

近日&#xff0c;ISC.AI 2024第十二届互联网安全大会在北京盛大开幕。本次大会以"打造安全大模型&#xff0c;引领安全行业革命"为主题&#xff0c;旨在呼吁行业以大模型重塑安全体系&#xff0c;以保障数字经济的稳健发展。 在企业安全运营与策略实践论坛上&#x…

人工智能时代,数字化工厂如何改革?提升竞争力?

在人工智能时代&#xff0c;数字化工厂通过数据驱动的决策、智能制造、柔性生产、物联网整合以及人机协作&#xff0c;实现生产效率和产品质量的全面提升&#xff0c;并不断创新以保持竞争力。 --题记 在人工智能时代&#xff0c;数字化工厂的改革…

usb摄像头 按钮 静止按钮

usb摄像头 按钮 静止按钮 来分析一个UVC的摄像头的枚举信息 UVC学习&#xff1a;UVC中断端点介绍 https://www.eet-china.com/mp/a269529.html 输入命令lsusb -d 0c45:62f1 -v https://www.miaokee.com/705548.html >Video Class-Specific VS Video Input Header Descrip…

如何恢复未保存/删除的 WPS 文档文件

“如何恢复 WPS 文件&#xff1f;文件已损坏&#xff0c;似乎我之前没有保存过&#xff0c;软件退出&#xff0c;导致文件丢失。有什么方法可以恢复未保存的 WPS 文档吗&#xff1f;” WPS 办公套件是金山软件开发的一款很棒的套件。说实话&#xff0c;它更适合个人和办公室工…

GEE数据——全球固定宽带和移动(蜂窝)网络性能(网速)(2019-2024)

全球固定宽带和移动&#xff08;蜂窝&#xff09;网络性能 简介 全球固定宽带和移动&#xff08;蜂窝&#xff09;网络性能&#xff0c;分配给缩放级别 16 的网络 mercator 瓷砖&#xff08;赤道处约 610.8 米乘 610.8 米&#xff09;。 数据以 Shapefile 格式和 Apache Par…

在那曲,一场别开生面的盛会即将上演

当夏日的暖阳轻抚着那曲的广袤草原&#xff0c;一场展现高原之魂的盛会悄然临近。2024年8月8日&#xff0c;那曲市将举办一场独特的选美比赛——“最美牦牛”评选比赛&#xff0c;邀请各界人士、朋友共襄盛举。 牦牛&#xff0c;被誉为“高原之舟”&#xff0c;它们在这片古老的…

【nginx】解决k8s中部署nginx转发不会自动更新域名解析启动失败的问题

文章目录 1. 问题2.解决办法3.扩展说明3.1 DNS解析阶段划分3.2 问题说明3.2.1 先看/etc/resolv.conf说明3.2.2 针对第一个问题3.2.3 针对第二个问题 【后端】NginxluaOpenResty高性能实践 参考&#xff1a; https://blog.csdn.net/u010837612/article/details/123275026 1. 问…

【Java安全开发全流程解读】Ting-Write-Nuclei V1.0发布 【万户OA-SQL注入1Day-POC】

加微信即可加入WingBy交流群 不存在娱乐化 前言 nuclei-poc一键生成工具市面上很多也很全&#xff0c;比如burp插件生成POC projectdiscovery/nuclei-burp-plugin: Nuclei plugin for BurpSuite (github.com) GUI nuclei-plus/README_zh.md at main Yong-An-Dang/nuclei-p…

七夕情人节送什么礼物?看完这篇你就知道了

在这个充满爱意的时刻&#xff0c;送上一份精心挑选的礼物&#xff0c;不仅能表达你的爱意&#xff0c;更能加深彼此之间的情感联系。然而&#xff0c;选择一份合适的情人节礼物并非易事&#xff0c;因为每个人都有其独特的需求和喜好。如果你还在为情人节送什么礼物而纠结&…

1、.Net UI框架:Xamarin Forms - .Net宣传系列文章

Xamarin.Forms是一个跨平台移动应用开发框架&#xff0c;它允许开发者使用C#和.NET进行一次编码&#xff0c;然后在iOS、Android、macOS和Windows等多个平台上运行。Xamarin.Forms是Xamarin的一部分&#xff0c;而Xamarin是微软的.NET跨平台开发工具集&#xff0c;它提供了一套…

荟萃科技:海外调查问卷的核心在哪?

那就需要知道海外问卷调查是什么&#xff1f; 海外问卷调查是国外的企业为了收集消费者的意见反馈&#xff0c;在市场上发布付费问卷&#xff0c;从而进一步改善他们的产品或者服务。 我们就是回答这些付费问卷来赚取佣金。 核心来了&#xff0c;参与这些调查问卷需要有一定…

防近视台灯什么牌子好?刨析防近视台灯的详细效果

当代不管是学习还是工作&#xff0c;大家处于“卷”的状态&#xff0c;夜晚办公的上班族或是学生党都只多不少。普通的台灯只有最基础的照明作用&#xff0c;长时间下来对眼睛伤害大&#xff0c;严重还会产生眼部疾病。而在这一时刻防近视台灯——护眼灯的出现&#xff0c;成为…

ECCV2024,清华百度提出ReSyncer:可实现音频同步嘴唇动作视频生成。

清华&百度等联合提出了ReSyncer&#xff0c;可以实现更高稳定性和质量的口型同步&#xff0c;而且还支持创建虚拟表演者所必需的各种有趣属性&#xff0c;包括快速个性化微调、视频驱动的口型同步、说话风格的转换&#xff0c;甚至换脸。 ReSyncer的工作原理可以简单理解为…

暑假看这个,一个月英语水平突飞猛进

恭喜考研党们尘埃落定&#xff0c;静等9月1号入学。 闲暇之余&#xff0c;可以看看各个领域的书&#xff0c;看优秀老师的讲课视频&#xff0c;尝试学习一门新技能....不仅能学到很多知识&#xff0c;还能让生活更加丰富。 推荐几个适合大部分人提升能力的网站。 1、中国大学MO…

JVM知识总结(即时编译)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 即时编译 Java编译器经过解释执行&#xff0c;其执行速度必然会比…

【Linux】学习Linux,需要借助具象化的思维

指令与图形化界面 导读一、命令行与图形化界面二、命令行与图形化界面的发展历程1.2.1 打字机的起源1.2.2 肖尔斯和格利登型打字机1.2.3 鼠标的发明1.2.4 图形化界面&#xff08;GUI&#xff09;的发展 三、命令行与图形化之间的联系3.1 图形化界面的人机交互3.2 命令行界面的人…

SOLIDWORKS 机电协同设计:无缝集成,提升创新速度

在现代制造业中&#xff0c;产品的复杂性和多样性日益增加&#xff0c;这要求设计团队能够在更短的时间内交付高质量的产品。为了应对这一挑战&#xff0c;越来越多的企业开始采用机电一体化的设计方法&#xff0c;其中SOLIDWORKS成为了实现这一目标的理想工具。作为SOLIDWORKS…

趋动科技联合超聚变,让超融合彻底释放算力潜能

近日&#xff0c;趋动科技联合超聚变推出基于FusionOne HCI超融合的AI算力资源池化解决方案。该方案基于业内领先的AI算力资源池化技术&#xff0c;实现智能调度、异构算力融合管理等功能&#xff0c;让客户能够低成本获取AI算力&#xff0c;便捷使用AI算力&#xff0c;加速AI业…

Linux中的无人职守安装脚本kickstart

一、linux中的无人职守安装脚本kickstart 作用&#xff1a;自动化装系统。就是创建虚拟机&#xff0c;建立完成以后&#xff0c;不需要再进入配置root等&#xff0c;直接就可以自动安装&#xff0c;快速装机。 1.1 提前准备 准备一台红帽linux7系统 linux7 下载图形化界面 […

Docker 常规安装简介

Docker常规安装简介 欢迎关注我的B站&#xff1a;https://space.bilibili.com/379384819 1. 安装mysql 1.1 docker hub上面查找mysql镜像 网址&#xff1a; https://hub.docker.com/_/mysql 1.2 从docker hub上&#xff08;阿里云加速器&#xff09;拉取mysql镜像到本地标…