推荐一个react拖拽排序的库,@dnd-kit

news2024/11/20 6:14:17

这里写目录标题

  • @dnd-kit
    • 基于这个库封装了一个组件
      • 效果图
      • 代码
        • index
        • less
        • components

@dnd-kit

官网
GitHub

基于这个库封装了一个组件

效果图

在这里插入图片描述

代码

index
import { forwardRef, ForwardedRef, useState, useRef, useCallback, useMemo } from 'react';
import { Checkbox } from 'antd';
import { CheckboxChangeEvent } from 'antd/es/checkbox';
import { Resizable, NumberSize, ResizeDirection } from 're-resizable';
import type { ERPTransferProps, ERPTransferRef } from './type';
import { LeftHeaderLeft, LeftHeaderRight, RightHeaderLeft, RightHeaderRight } from './components/headers';
import DraggableList from './components/draggable';
import { LeftSearch } from './components/search';
import './index.less';
import { debounce, isUndefined } from 'lodash-es';

type ResizeCallbackParams = [MouseEvent | TouchEvent, ResizeDirection, HTMLElement, NumberSize];

function Index<T>(props: ERPTransferProps<T>, ref: ForwardedRef<ERPTransferRef<T>>) {
  const {
    width = 688,
    height = 376,
    transferWidth = 336,
    allowResizable = false,
    dataSource,
    keyCode,
    nameCode,
    leftHeaderLeft,
    leftHeaderRight,
    rightHeaderLeft,
    rightHeaderRight,
    onChange,
  } = props;

  const [leftWidth, setLeftWidth] = useState(transferWidth);
  const [rightWidth, setRightWidth] = useState(transferWidth);
  const leftWidthRef = useRef(transferWidth);
  const rightWidthRef = useRef(transferWidth);
  const [selectedKeys, setSelectedKeys] = useState<(string | number)[]>([]);
  const [searchLeftData, setSearchLeftData] = useState<T[]>();
  const [searchRightData, setSearchRightData] = useState<T[]>();

  // 用map对象存一下dataSource
  const dataSourceMapMemo = useMemo(() => {
    return new Map(dataSource.map((i) => [i[keyCode], i]));
  }, [dataSource]);

  /* —————————————————————————拖拽的回调处理—————————————————————————————————— */
  const onLeftResize = useCallback((...rest: ResizeCallbackParams) => {
    const { width } = rest[3];
    setLeftWidth(leftWidthRef.current + width);
    setRightWidth(rightWidthRef.current - width);
  }, []);
  const onRightResize = useCallback((...rest: ResizeCallbackParams) => {
    const { width } = rest[3];
    setRightWidth(rightWidthRef.current + width);
    setLeftWidth(leftWidthRef.current - width);
  }, []);

  const onLeftResizeStop = useCallback((...rest: ResizeCallbackParams) => {
    const { width } = rest[3];
    leftWidthRef.current = leftWidthRef.current + width;
    rightWidthRef.current = rightWidthRef.current - width;
  }, []);
  const onRightResizeStop = useCallback((...rest: ResizeCallbackParams) => {
    const { width } = rest[3];
    leftWidthRef.current = leftWidthRef.current - width;
    rightWidthRef.current = rightWidthRef.current + width;
  }, []);

  /* —————————————————————————左侧—————————————————————————————————— */
  const handleClickItem = (e: CheckboxChangeEvent, keyCode: string) => {
    const checked = e.target.checked;
    if (checked) {
      const selectedKeysTemp = [...selectedKeys, keyCode];
      setSelectedKeys(selectedKeysTemp);
      handleOnchange(selectedKeysTemp);
    } else {
      const selectedKeysTemp = selectedKeys.filter((item) => item !== keyCode);
      setSelectedKeys(selectedKeysTemp);
      handleOnchange(selectedKeysTemp);
    }
  };
  const handleAllSelected = (all: boolean) => {
    if (all) {
      const selectedKeysTemp = dataSource.map((item) => item[keyCode]);
      setSelectedKeys(selectedKeysTemp);
      handleOnchange(selectedKeysTemp);
    } else {
      setSelectedKeys([]);
      handleOnchange([]);
    }
  };
  const handleLeftSearch = (searchText: string) => {
    if (searchText === '') {
      setSearchLeftData(undefined);
    } else {
      const searchResult = dataSource.filter((i) => i[nameCode].includes(searchText));
      setSearchLeftData(searchResult);
    }
  };
  /* —————————————————————————右侧—————————————————————————————————— */
  const handleOnClean = () => {
    setSelectedKeys([]);
    handleOnchange([]);
  };
  const handleRightSearch = (searchText: string) => {
    if (searchText === '') {
      setSearchRightData(undefined);
    } else {
      const searchResult = dataSource.filter((i) => i[nameCode].includes(searchText));
      setSearchRightData(searchResult);
    }
  };
  const handleDraggableListData = () => {
    if (!searchRightData) return selectedKeys;
    else {
      return searchRightData.filter((i) => selectedKeys.includes(i[keyCode])).map((i) => i[keyCode]);
    }
  };

  /* —————————————————————————onChange—————————————————————————————————— */
  const handleOnchange = (keys: (string | number)[]) => {
    let result: T[] = [];
    for (const key of keys) {
      if (dataSourceMapMemo.has(key)) {
        result.push(dataSourceMapMemo.get(key)!);
      }
    }
    onChange?.(result);
  };

  return (
    <div style={{ width, height }} className="panui-fi-transfer-erp-container">
      <Resizable
        size={{ width: leftWidth, height }}
        onResize={onLeftResize}
        onResizeStop={onLeftResizeStop}
        enable={{
          right: allowResizable,
        }}
      >
        <div className="left-wrapper">
          <div className="left-header">
            <LeftHeaderLeft
              numerator={selectedKeys.length}
              denominator={dataSource.length}
              leftHeaderLeft={leftHeaderLeft}
              onChange={handleAllSelected}
            />
            <LeftHeaderRight leftHeaderRight={leftHeaderRight} />
          </div>
          <LeftSearch onChange={debounce(handleLeftSearch, 300)} />
          <ul className="list">
            {(searchLeftData ?? dataSource).map((i) => (
              <li key={i[keyCode]} className="li-item">
                <Checkbox checked={selectedKeys.includes(i[keyCode])} onChange={(e) => handleClickItem(e, i[keyCode])}>
                  {i[nameCode]}
                </Checkbox>
              </li>
            ))}
            {searchLeftData?.length === 0 && <li className="li-item-no">无搜索结果</li>}
          </ul>
        </div>
      </Resizable>
      <Resizable
        size={{ width: rightWidth, height }}
        onResize={onRightResize}
        onResizeStop={onRightResizeStop}
        enable={{
          left: allowResizable,
        }}
      >
        <div className="right-wrapper">
          <div className="right-header">
            <RightHeaderLeft selected={selectedKeys.length} rightHeaderLeft={rightHeaderLeft} />
            <RightHeaderRight rightHeaderRight={rightHeaderRight} onClean={handleOnClean} />
          </div>
          <LeftSearch onChange={debounce(handleRightSearch, 300)} />
          <div className="list">
            <DraggableList
              data={handleDraggableListData()}
              setSortData={setSelectedKeys}
              dataSourceMap={dataSourceMapMemo}
              nameCode={nameCode}
              handleOnchange={handleOnchange}
              disabledDraggable={!isUndefined(searchRightData)}
            />
          </div>
        </div>
      </Resizable>
    </div>
  );
}

export default forwardRef(Index);
less
.panui-fi-transfer-erp-container {
  position: relative;
  display: flex;
  justify-content: space-between;
  .left-wrapper {
    width: 100%;
    height: 100%;
    border: 1px solid rgba(227, 231, 237, 1);
    border-radius: 4px;
    display: flex;
    flex-direction: column;
    .left-header {
      border-bottom: 1px solid #e3e7ed;
      color: #86909c;
      display: flex;
      justify-content: space-between;
      .left-header-left {
        height: 38px;
        line-height: 38px;
        padding-left: 12px;
        .ant-checkbox + span {
          color: #86909c;
        }
      }
      .left-header-right {
        line-height: 38px;
        padding-right: 12px;
      }
    }
    .list {
      margin: 0;
      padding: 0;
      list-style: none;
      flex: 1;
      overflow: hidden auto;
      .li-item {
        line-height: 32px;
        padding-left: 12px;
        cursor: pointer;
        &:hover {
          background-color: #e6ecfa;
        }
        .panui-base-checkbox-container {
          width: 100%;
          margin-right: 4px;
          .ant-checkbox {
            .ant-checkbox-inner {
              border-radius: 4px;
            }
            & + span {
              width: 100%;
            }
          }
        }
      }
      .li-item-no {
        text-align: center;
        color: #86909c;
        line-height: 64px;
      }
    }
  }
  .right-wrapper {
    width: 100%;
    height: 100%;
    border: 1px solid rgba(227, 231, 237, 1);
    border-radius: 4px;
    display: flex;
    flex-direction: column;
    .right-header {
      border-bottom: 1px solid #e3e7ed;
      color: #86909c;
      display: flex;
      justify-content: space-between;
      .right-header-left {
        line-height: 38px;
        padding-left: 12px;
      }
      .right-header-right {
        line-height: 38px;
        padding-right: 12px;
        color: #0e42d2;
        cursor: pointer;
      }
    }
    .list {
      padding: 0 12px;
      flex: 1;
      overflow: hidden auto;
      .li-item {
        width: 100%;
        line-height: 30px;
        display: flex;
        justify-content: space-between;
        border: 1px solid transparent;
        align-items: center;
        .li-item-name {
          height: 30px;
          display: flex;
          align-items: center;
          span {
            height: inherit;
          }
          .panui-icon-anticon {
            display: flex;
            align-items: center;
            cursor: grab;
          }
        }
        .panui-icon-anticon {
          cursor: pointer;
        }
      }
      .li-item[aria-pressed='true'] {
        background: rgba(223, 0, 36, 0.04);
        border: 1px solid rgba(223, 0, 36, 0.3);
        border-radius: 4px;
        .li-item-name {
          visibility: hidden;
        }
        .li-item-name + span {
          visibility: hidden;
        }
      }
      .li-item.li-item-overlay {
        background: rgba(255, 255, 255, 0.9);
        border: 1px solid rgba(223, 0, 36, 0.3);
        box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
        border-radius: 4px;
        cursor: grabbing;
        .panui-icon-anticon {
          cursor: grabbing;
        }
        .li-item-name {
          color: #86909c;
        }
        .li-item-name + span {
          visibility: hidden;
        }
      }
      .li-item.li-item-disabled-draggable {
        .panui-icon-anticon {
          cursor: default;
        }
      }
    }
  }
  .search-container {
    padding: 8px 12px;
  }
}
components
// header
import React from 'react';
import { Checkbox } from 'antd';
interface LeftHeaderLeftProps {
  leftHeaderLeft?: (numerator: number, denominator: number) => React.ReactNode;
  numerator: number;
  denominator: number;
  onChange: (all: boolean) => void;
}

export const LeftHeaderLeft = (props: LeftHeaderLeftProps) => {
  const { leftHeaderLeft, numerator, denominator, onChange } = props;
  return (
    <div className="left-header-left">
      <Checkbox
        indeterminate={numerator > 0 && denominator !== numerator}
        checked={numerator === denominator}
        onChange={(e) => onChange(e.target.checked)}
      >
        {leftHeaderLeft ? leftHeaderLeft(numerator, denominator) : `${numerator}/${denominator}`}
      </Checkbox>
    </div>
  );
};

export const LeftHeaderRight = ({ leftHeaderRight }: { leftHeaderRight?: React.ReactNode }) => {
  return leftHeaderRight ? <div>{leftHeaderRight}</div> : <span className="left-header-right">可选择</span>;
};

export const RightHeaderLeft = ({
  selected,
  rightHeaderLeft,
}: {
  selected: number;
  rightHeaderLeft?: (selected: number) => React.ReactNode;
}) => {
  return rightHeaderLeft ? (
    <div>{rightHeaderLeft(selected)}</div>
  ) : (
    <span className="right-header-left">
      已选择 <span style={{ color: '#1D2129' }}>{selected}</span>
    </span>
  );
};

export const RightHeaderRight = ({
  rightHeaderRight,
  onClean,
}: {
  rightHeaderRight?: React.ReactNode;
  onClean: () => void;
}) => {
  return rightHeaderRight ? (
    <div>{rightHeaderRight}</div>
  ) : (
    <span className="right-header-right" onClick={onClean}>
      清空
    </span>
  );
};

// Search
import { Input } from 'antd';
export function LeftSearch({ onChange }: { onChange: (searchText: string) => void }) {
  return (
    <div className="search-container">
      <Input placeholder="搜索" suffix="放大镜" onChange={(e) => onChange(e.target.value)} />
    </div>
  );
}

// draggable
import React, { useState } from 'react';
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragEndEvent,
  DragOverlay,
  DragStartEvent,
} from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { restrictToVerticalAxis, restrictToParentElement } from '@dnd-kit/modifiers';

import { SortableItem } from './sortItem';
import { isEmpty } from 'lodash-es';

interface DraggableListProps<T> {
  data: (string | number)[];
  setSortData: React.Dispatch<React.SetStateAction<(string | number)[]>>;
  dataSourceMap: Map<string | number, T>;
  nameCode: string;
  handleOnchange: (keys: (string | number)[]) => void;
  disabledDraggable?: boolean;
}

export default function Index<T>(props: DraggableListProps<T>) {
  const { data, setSortData, dataSourceMap, nameCode, handleOnchange, disabledDraggable } = props;
  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    }),
  );

  const handleDeleteItem = (id: string | number) => {
    setSortData((items) => {
      const keys = items.filter((item) => item !== id);
      handleOnchange(keys);
      return keys;
    });
  };
  const [activeId, setActiveId] = useState<string | number | null>(null);

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
      modifiers={[restrictToParentElement]}
    >
      <SortableContext items={data} strategy={verticalListSortingStrategy} disabled={disabledDraggable}>
        {data.map((i) => (
          <SortableItem
            key={i}
            id={i}
            name={dataSourceMap.get(i)?.[nameCode]}
            deleteItem={handleDeleteItem}
            className={disabledDraggable ? 'li-item-disabled-draggable' : ''}
          />
        ))}
        {isEmpty(data) && disabledDraggable && (
          <span
            style={{
              display: 'inline-block',
              width: '100%',
              textAlign: 'center',
              color: '#86909c',
              lineHeight: '64px',
            }}
          >
            无搜索结果
          </span>
        )}
      </SortableContext>
      <DragOverlay>
        {activeId ? (
          <SortableItem
            key={activeId}
            id={activeId}
            name={dataSourceMap.get(activeId)?.[nameCode]}
            className="li-item-overlay"
          />
        ) : null}
      </DragOverlay>
    </DndContext>
  );

  function handleDragStart(event: DragStartEvent) {
    setActiveId(event.active.id);
  }
  function handleDragEnd(event: DragEndEvent) {
    const { active, over } = event;
    if (active.id !== over?.id && over) {
      setSortData((items) => {
        const oldIndex = items.indexOf(active.id);
        const newIndex = items.indexOf(over?.id);
        const keys = arrayMove(items, oldIndex, newIndex);
        handleOnchange(keys);
        return keys;
      });
    }
  }
}
// SortableItem
import { useSortable } from '@dnd-kit/sortable';
import classNames from 'classnames';
import { CSS } from '@dnd-kit/utilities';
import { PWrong1Outlined, PDrag2Filled } from '@panui/icons';

interface SortableItemProps {
  id: string | number;
  name: string;
  deleteItem?: (id: string | number) => void;
  className?: string;
}

export function SortableItem(props: SortableItemProps) {
  const { id, name, deleteItem, className } = props;
  const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  return (
    <div ref={setNodeRef} style={style} className={classNames(['li-item', className])} {...attributes}>
      <div className="li-item-name">
        <span {...listeners}>
          <PDrag2Filled />
        </span>
        <span style={{ marginLeft: '8px' }}>{name}</span>
      </div>
      <span onClick={() => deleteItem?.(id)}>
        <PWrong1Outlined />
      </span>
    </div>
  );
}

PS:

  1. 线性的复选框是又封装了一层,贴的代码里换成了antd的

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

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

相关文章

【论文文献管理与同步】Zotero + 坚果云 + Zotfile :多设备论文管理与同步

【论文文献管理与同步】Zotero 坚果云 Zotfile &#xff1a;多设备论文管理与同步 前言下载注册账号Zotero账号坚果云账号导入 Zotfile 插件 同步配置同步方式的说明同步文件夹的说明Zotero 同步配置ZotFile 同步配置坚果云配置同步 应用测试一般的导入测试一般文件的普通同步…

数据结构--归并排序(Merge Sort)

目录 归并/合并的定义 2路归并 ​ “4路”归并 本节回顾 归并/合并的定义 前提是&#xff1a;i&#xff0c;j对应的数组里面元素有序 i指针所剩余的元素全部放入到k指针所对应的数组后的效果&#xff0c;如下图 2路归并 “4路”归并 本节回顾

【网络编程】套接字编程——UDP通信

文章目录 一、预备知识1. 源IP地址和目的IP地址2. 源MAC地址和目的MAC地址3. 端口号PORT和进程标识符PID 二、TCP协议和UDP协议1. TCP协议和UDP协议2. 网络字节序 三、socket编程1. 套接字常见的API2. 套接字的数据结构 四、UDP服务器1. echo服务器2. bash服务器3. 简易的公共聊…

Spring MVC 十:异常处理

异常是每一个应用必须要处理的问题。 Spring MVC项目&#xff0c;如果不做任何的异常处理的话&#xff0c;发生异常后&#xff0c;异常堆栈信息会直接抛出到页面。 比如&#xff0c;我们在Controller写一个异常&#xff1a; GetMapping(value"/hello",produces{&qu…

200行C++代码写一个Qt俄罗斯方块小游戏

小小演示一下&#xff1a; 大体思路&#xff1a; 其实很早就想写一个俄罗斯方块了&#xff0c;但是一想到那么多方块还要变形&#xff0c;还要判断落地什么的就脑壳疼。直到现在才写出来。 俄罗斯方块这个小游戏的小难点其实就一个&#xff0c;就是方块的变形&#xff0c;看似…

【智能家居项目】裸机版本——项目介绍 | 输入子系统(按键) | 单元测试

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f3c0;项目简介&#x1f3c0;输入子系统(按键)⚽应用层⚽设备层⚽ 内核层抽象层⚽…

基于谷歌Transeformer构建人工智能问答系统

目录 1 项目背景 2 关键技术 2.1 Transeformer模型 2.2 Milvus向量数据库 3 系统代码实现 3.1 运行环境构建 3.2 数据集介绍 3.3 预训练模型下载 3.4 代码实现 3.4.1 创建向量表和索引 3.4.2 构建向量编码模型 3.4.3 数据向量化与加载 3.4.4 构建检索web 3.5 运行结…

VS+Qt+C++ GDAL读取tif图像数据显示

程序示例精选 VSQtC GDAL读取tif图像数据显示 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《VSQtC GDAL读取tif图像数据显示》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;…

基于SpringBoot的美容院管理系统设计与实现

目录 前言 一、技术栈 二、系统功能介绍 管理员功能实现 美容部位管理 销量信息统计 已支付订单 技师功能实现 统计美容用品库存 预约信息管理 前台功能实现 普通用户管理 会员管理 普通用户功能实现 美容用品 购物车 我的订单 会员功能实现 美容项目 预约信…

PyCharm中使用pyqt5的方法2-1

qt可以用来设计界面&#xff0c;而pyqt是将qt移植到Python上&#xff0c;通过python语言设计界面&#xff0c;目前最新的版本是qt5。 在PyCharm中使用pyqt5的步骤分为下载和配置两个部分。 1 在PyCharm中下载安装pyqt5相关模块 1.1 下载步骤 PyCharm中要下载的pyqt5相关模块…

kafka集群是如何选择leader,你知道吗?

前言 kafka集群是由多个broker节点组成&#xff0c;这里面包含了许多的知识点&#xff0c;以下的这些问题你都知道吗? 你知道topic的分区leader是怎么选举的吗&#xff1f;你知道zookeeper中存储了kafka的什么信息吗&#xff1f;起到什么做呢&#xff1f;你知道kafka消息文件…

【精彩回顾】 用sCrypt在Bitcoin上构建智能合约

2023年3月24日&#xff0c;sCrypt在英国Exeter大学举办了关于智能合约的大学讲学。sCrypt首席执行官刘晓晖做了题为“用sCrypt在Bitcoin上构建智能合约”的演讲&#xff0c;并与到场的老师、学生进行了深入交流、互动。这次课程着重讲解了 BSV 智能合约的基础概念&#xff0c;以…

一图读懂「五度易链」企业创新服务解决方案,打造卓越营商环境!

“五度易链”紧密围绕园区企业及产业发展需求&#xff0c;基于数据积累和应用&#xff0c;创新企业服务机制&#xff0c;提升企业服务效能&#xff0c;以数字化手段为企业发展纾困解难&#xff0c;赋能企业高质量发展。并帮助园区在运营方面打破数据壁垒&#xff0c;实现数据监…

调度算法+等待/周转时间计算

周转时间 作业完成时刻 - 到达时刻 等待时间 开始时刻 - 到达时刻 平均时间就是用总时间除以作业个数 先来先服务调度算法&#xff08;FCFS&#xff09; 非抢占 优先级调度算法 系统总是调度优先级最高的那个进程运行。 优先级可以分为静态优先级和动态优先级。静态优先…

113. 路径总和ii

力扣题目链接(opens new window) 给定一个二叉树和一个目标和&#xff0c;找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树&#xff0c;以及目标和 sum 22&#xff0c; 在路径总和题目的基础上&…

新媒体运营的未来:ChatGPT的智能助手

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 新媒体运营是数字时代的…

Java括号匹配

目录 一、题目描述 二、题解 一、题目描述 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭…

LwIP笔记01:LwIP入门

1. LwIP简介 小型开源的TCP/IP协议栈交换机、路由器、光纤收发器、云台接入、无线网关、远程模块、工业控制器、网络摄像头 TCP/IP模型 &#xff08;1&#xff09;应用层&#xff1a;HTTP、MQTT、NTP、FTP、...... &#xff08;2&#xff09;传输层&#xff1a;TCP、UDP &…

【kubernetes】【基础资源使用】kubernetes中的Deployment使用

1 Why need Deployment? K8S中Pod是用户管理工作负载的基本单位&#xff0c;Pod通常通过Service进行暴露&#xff0c;因此&#xff0c;通常需要管理一组Pod&#xff0c;RC和RS主要就实现了一组Pod的管理工作&#xff0c;其中&#xff0c;RC和RS的区别在于&#xff0c;RS提供更…

如何使用pycharm连接Mysql数据库!!!

1、Mysql的安装&#xff1a; MySQL针对不同的用户提供了2中不同的版本&#xff1a; MySQL Community Server&#xff1a;社区版。由MySQL开源社区开发者和爱好者提供技术支持&#xff0c;对开发者开放源代码并提供免费下载。MySQL Enterprise Server&#xff1a;企业版。包括最…