React学习笔记(番外二)——列表多选批量处理复合组件
React学习笔记(番外二)——列表多选批量操作复合组件
- 前言
- 〇、Show you the code
- 一、 任务分析及拆解
- 表头行的Checkbox——总开关
- 记录行的Checkbox——行级开关
- 二、 基础实现
- 表头行的文件——header-row.js
- 记录行的文件——record-row.js
- 页面的文件App.js
- 阶段效果
- 三、 完善控制逻辑
- custom-hooks.js——自定义React Hook
- 4处useState
- 4处useCallback
- 1处useMemo
- App.js——部分改造
- header-row.js——部分改造
- record-row.js——部分改造
- 阶段效果
- 四、 最后一步
- 后记
前言
近期有需求需要实现如题的列表多选批量处理复合组件
。因为是两部分组件互相控制,对于初次实现的我来说还是有一定困难的。经过几天的实现、Bugfix,现对这种比较常见的复合组件的实现作以记录。
〇、Show you the code
成熟的程序猿讲究【Talk is cheap, show me the code】,如果你只想看代码:
本篇文章中,最终的完整代码已托管在CodeSandBox,点击前往,查看代码及编译、渲染结果
一、 任务分析及拆解
先来看一下上面说的到底是什么。如下图,当我们有一个比较长的列表时,有时希望一次性多选几行,进行批量操作。
我们可以很轻松地将所需的复合组件拆分为以下几部分子组件:
- 表头行的Checkbox——总开关
- 记录行的Checkbox——行级开关
- 右上方的操作按钮
表头行的Checkbox——总开关
这个Checkbox比较特殊,有3个状态,如下图:
- 未选中状态: 点击后变为
全部选中
状态。此状态下,列表中的记录都是未选中
状态。表头行Checkbox
由于点击达到该状态时所有记录行Checkbox
都变为未选中
状态,所有记录行Checkbox
由于点击都达到未选中
状态时,表头行Checkbox
也受控变为未选中
状态。 - 部分选中状态: 点击后变为
全部选中
状态。此状态下,列表中有部分记录([1, n-1])为选中状态。该状态只能由部分记录行Checkbox
受到点击变为选中
状态而达到。 - 全部选中状态: 点击后变为
未选中
状态。此状态下,列表中的记录都是选中
状态。表头行Checkbox
由于点击达到该状态时所有记录行Checkbox
都变为选中
状态,所有记录行Checkbox
由于点击都达到选中
状态时,表头行Checkbox
也受控变为全部选中
状态。
记录行的Checkbox——行级开关
记录行前的就是常规的Checkbox,只有两种状态,如下图:
- 未选中状态: 点击后变为
选中
状态。此状态下,当前行的记录是未选中
状态。所有记录行Checkbox
都为该状态时表头行Checkbox
受控变为未选中
状态。 - 选中状态: 点击后变为
未选中
状态。此状态下,当前行的记录是选中
状态。部分记录行Checkbox
为该状态时表头行Checkbox
受控变为部分选中
状态。所有记录行Checkbox
都为该状态时表头行Checkbox
受控变为全部选中
状态。
二、 基础实现
我们将一个包含列表的页面分为三个部分:
- 表头行的文件——header-row.js
- 记录行的文件——record-row.js
- 页面的文件——App.js
接下来分别实现三个部分
表头行的文件——header-row.js
import "./index.css";
import React from "react";
import { Checkbox } from "antd";const HeaderRow = () => {return (<div className="header-row"><div className="column0"><Checkbox /></div><div className="column1">Id</div><div className="column2">Title</div><div className="column3">Desc</div></div>);
};export default HeaderRow;
记录行的文件——record-row.js
import "./index.css";
import React from "react";
import { Checkbox } from "antd";const RecordRow = (props) => {let { record } = props;let { id, title, desc } = record;return (<div className="record-row"><div className="column0"><Checkbox /></div><div className="column1">{id}</div><div className="column2">{title}</div><div className="column3">{desc}</div></div>);
};export default RecordRow;
页面的文件App.js
import "./styles.css";
import React, { useMemo } from "react";
import HeaderRow from "./components/header-row";
import RecordRow from "./components/record-row";const MainPage = (props) => {// 生成要显示的记录数据,现实中应该通过后台请求获得let records = useMemo(() => {let _records = [];for (let i = 0; i < 10; i++) {_records.push({id: i + 1,title: `这是测试标题${i + 1}`,desc: `这是测试描述${i + 1}`});}return _records;}, []);// 记录行的组件列表let recordRowViews = useMemo(() => {return records.map((item) => {return <RecordRow key={`record-${item.id}`} record={item} />;});}, [records]);return (<div><HeaderRow />{recordRowViews}</div>);
};export default MainPage;
阶段效果
基础实现比较简单,当然功能也不完善。我们还没有写Checkbox
之间的控制逻辑,先看看目前的效果:
三、 完善控制逻辑
因为实现中涉及到多个页面具有同样的控件和逻辑需要,因此实现中尽可能地考虑代码的复用性。这里的代码实现拆分为以下几个部分:
- custom-hooks.js——包含几个自定义React Hook,最主要的Checkbox互相控制算法逻辑部分
- App.js——前文中已有的文件,做部分改造
- header-row.js——前文中已有的文件,做部分改造
- record-row.js——前文中已有的文件,做部分改造
custom-hooks.js——自定义React Hook
import { useState, useMemo, useCallback } from "react";/*** 列表多选,批量处理的逻辑Hook* @param showedRecords 经过过滤后页面需要显示的列表项* @returns {[[string],boolean,boolean,boolean,function,function]}*/
const useBatchOperation = (allRecords) => {// 表示部分[1, n-1]行被勾选的状态,仅控制UIlet [partialChecked, setPartialChecked] = useState(false);// 表示是否所有的条目当前都被选中(即UI表现为表头的checkbox是否勾选)let [headerChecked, setHeaderChecked] = useState(false);// 表示当前用户是否主动点击了表头的那一个checkbox,点击后无论状态如何,其他checkbox都要跟随该状态,同headerChecked配合控制使用let [operateAll, setOperateAll] = useState(false);// 当前勾选的行所代表的数据单元组成的列表let [checkedItemList, setCheckedItemList] = useState([]);// 选中某行记录let checkItem = useCallback((item) => {let newCheckedItemList = [...checkedItemList];if (!Array.isArray(item)) {item = [item];}for (let i = 0; i < item.length; i++) {let index = checkedItemList.indexOf(item[i]);if (index === -1) {newCheckedItemList.push(item[i]);}}setCheckedItemList(newCheckedItemList);return newCheckedItemList;},[checkedItemList]);// 取消选中某行记录let uncheckItem = useCallback((item, clear = false) => {let newCheckedItemList = [];if (!clear) {if (!Array.isArray(item)) {item = [item];}for (let i = 0; i < checkedItemList.length; i++) {if (item.indexOf(checkedItemList[i]) === -1) {newCheckedItemList.push(checkedItemList[i]);}}}setCheckedItemList(newCheckedItemList);return newCheckedItemList;},[checkedItemList]);// 记录行checkbox的onChange事件let onSingleCheckBoxChange = useCallback((checked, record) => {// 按照判断逻辑,必须放在第一行setOperateAll(false);let newCheckedItemList;if (checked) {newCheckedItemList = checkItem(record);} else {newCheckedItemList = uncheckItem(record);}setPartialChecked(newCheckedItemList.length > 0 &&newCheckedItemList.length < allRecords.length);setHeaderChecked(newCheckedItemList.length === allRecords.length);},[checkItem, uncheckItem, allRecords]);// 表头行checkbox的onChange事件let onBatchCheckBoxChange = useCallback(() => {if (!headerChecked) {checkItem(allRecords);} else {uncheckItem(null, true);}setPartialChecked(false);setHeaderChecked(!headerChecked);setOperateAll(true);}, [headerChecked, checkItem, uncheckItem, allRecords]);// 选中的记录,其Id组成的列表let checkedIdList = useMemo(() => {if (checkedItemList.length > 0) {return checkedItemList.map((item) => {return item.id;});}return [];}, [checkedItemList]);return [checkedIdList,partialChecked,operateAll,headerChecked,onSingleCheckBoxChange,onBatchCheckBoxChange];
};export { useBatchOperation };
这里代码量有110行,还是比较多的,我们拆开来讲一下逻辑。一共用了3种React Hook,分别是useState
、useCallback
和useMemo
。其中useState
4处,useCallback
4处,useMemo
1处。
4处useState
// 表示部分[1, n-1]行被勾选的状态,仅控制UIlet [partialChecked, setPartialChecked] = useState(false);// 表示是否所有的条目当前都被选中(即UI表现为表头的checkbox是否勾选)let [headerChecked, setHeaderChecked] = useState(false);// 表示当前用户是否主动点击了表头的那一个checkbox,点击后无论状态如何,其他checkbox都要跟随该状态,同headerChecked配合控制使用let [operateAll, setOperateAll] = useState(false);// 当前勾选的行所代表的数据单元组成的列表let [checkedItemList, setCheckedItemList] = useState([]);
- partialChecked: 标识
表头行Checkbox
当前是否处于部分选中
状态,仅用于控制UI样式,初始值为false
- headerChecked: 标识
表头行Checkbox
当前是否处于全部选中
状态,即是否所有记录行Checkbox
都为选中状态
,初始值为false
- operateAll: 标识当前用户是否主动点击了
表头行Checkbox
,点击后所有记录行Checkbox
必须跟随改变为同样的状态,初始值为false
- checkedItemList: 当前勾选的记录行所代表的数据组成的列表,前文段落页面的文件App.js中
records
数组的子集,初始值为空数组[]
4处useCallback
代码较长,不再复制徒增篇幅。
- checkItem: 勾选某行
记录行Checkbox
后执行的事件方法。实际代码逻辑是给checkedItemList
列表中新增一个记录数据,然后通过setCheckedItemList
更新最新的checkedItemList
,之所以使用useCallback
封装一层,是为了其访问到的checkedItemList
是当前最新的。 - uncheckItem: 和
checkItem
作用相反,取消勾选某行记录行Checkbox
后执行的事件方法。实际代码逻辑是在checkedItemList
列表中找到并删除一个记录数据… - onSingleCheckBoxChange: 当
记录行Checkbox
被点击而发生状态变化时执行的事件方法。代码逻辑包括:根据当前状态是选中
还是未选中
调用checkItem
或uncheckItem
更新checkedItemList
,根据checkedItemList
的length
判断表头行Checkbox
是否应该改为部分选中
、全部选中
或未选中
状态等。 - onBatchCheckBoxChange : 当
表头行Checkbox
被点击而发生状态变化时执行的事件方法。代码逻辑包括:根据当前状态是全部选中
还是未选中
调用checkItem
或uncheckItem
更新checkedItemList
等。
1处useMemo
- checkedIdList: 根据
checkedItemList
实时计算,表示当前被选中的记录的id
组成的列表,暴露出去,向后台发请求时用。
App.js——部分改造
......
const MainPage = (props) => {......// 使用自定义hook-useBatchOperation,传入records列表let [checkedIdList,partialChecked,operateAll,headerChecked,onSingleCheckBoxChange,onBatchCheckBoxChange] = useBatchOperation(records);let recordRowViews = useMemo(() => {return records.map((item) => {return (<RecordRowkey={`record-${item.id}`}record={item}operateAll={operateAll}headerChecked={headerChecked}onSingleCheckBoxChange={onSingleCheckBoxChange}/>);});}, [records, operateAll, headerChecked, onSingleCheckBoxChange]);return (<div><HeaderRowpartialChecked={partialChecked}headerChecked={headerChecked}onBatchCheckBoxChange={onBatchCheckBoxChange}/>{recordRowViews}</div>);
};export default MainPage;
为了不占过多篇幅,用......
代替未发生改变的部分。
主要修改3个部分:
- 创建一个自定义Hook-useBatchOperation的实例,获得暴露出来的6个变量。
RecrodRow
传入operateAll
、headerChecked
和onSingleCheckBoxChange
HeaderRow
传入partialChecked
、headerChecked
、onBatchCheckBoxChange
剩下暂时未用到的
checkedIdList
留给后面的batch-operation-buttons.js
header-row.js——部分改造
......
const HeaderRow = (props) => {let { partialChecked, headerChecked, onBatchCheckBoxChange } = props;......<Checkboxindeterminate={partialChecked}checked={headerChecked}onChange={onBatchCheckBoxChange}/>......);
};export default HeaderRow;
主要修改2个部分:
- 引入父组件
App.js
传入的partialChecked
、headerChecked
、onBatchCheckBoxChange
- 将上述值和方法传入
Checkbox
控件
record-row.js——部分改造
......
const RecordRow = (props) => {......let { record, operateAll, headerChecked, onSingleCheckBoxChange } = props;let [checked, setChecked] = useState(false);useEffect(() => {if (operateAll) {setChecked(headerChecked);}}, [operateAll, headerChecked, setChecked]);let wrappedOnChange = useCallback(() => {setChecked(!checked);onSingleCheckBoxChange(!checked, record);}, [checked, setChecked, record, onSingleCheckBoxChange]);return (......<Checkboxchecked={checked}onChange={wrappedOnChange}onClick={wrappedOnChange}/>......);
};export default RecordRow;
主要修改4个部分:
- 引入父组件
App.js
传入的operateAll
、headerChecked
、onSingleCheckBoxChange
- 添加一个
useEffect
Hook,如果表头行Checkbox
被点击,跟随其改变状态 - 给
onSingleCheckBoxChange
封装一层,被点击时不仅要更新自身状态,还要触发自定义Hook的数据更新 - 将上述值和方法传入
Checkbox
控件
阶段效果
- 全选 / 取消全选
- 部分选中
四、 最后一步
经过前文中的努力,UI上表现达到预期了,所需的数据(checkedIdList
)也拿到了,距离实现文章开头的效果还差右上角的操作按钮。这里我们新建一个batch-operation-buttons.js
import "./index.css";
import React, { useCallback } from "react";const BatchOperationButtons = (props) => {let { checkedIdList } = props;let handleClick = useCallback((func) => {switch (func) {case 0:alert(`批量删除记录,IdList:${JSON.stringify(checkedIdList)}`);break;case 1:alert(`批量下载记录,IdList:${JSON.stringify(checkedIdList)}`);break;default:throw new TypeError(`操作码[${func}]未实现!`);}},[checkedIdList]);if (checkedIdList?.length < 1) {return null;}return (<div className="batch-operation-buttons"><span className="delete-button" onClick={() => handleClick(0)}>删除</span><span>/</span><span className="download-button" onClick={() => handleClick(1)}>下载</span></div>);
};export default BatchOperationButtons;
注意: 这里的
alert
是为了演示方便,现实情况下应该改为向后台发请求,按照checkedIdList
和操作的func
对选中的记录进行批量操作.
App.js中仅需改动以下代码即可
......return (<div><div className="buttons"><BatchOperationButtons checkedIdList={checkedIdList} /></div>......</div>);
后记
这次的实现中首次学习并使用了自定义Hook
。实现之后发现了它的重要意义,即我们可以通过自定义Hook
真正地将HTML模板和JS数据逻辑拆分为两个独立的文件,一个只管模板内容,一个只管数据的更新。官方一点的说法即:实现了Model(custom-hooks.js)
和View(App.js)
的分离,意义重大。
为了便于理解,你可以想象,在学会使用自定义Hook
之前,App.js
和custom-hooks.js
里的代码统统写在App.js
里,这样一个控件里的代码量将非常大,维护起来比较麻烦。拆分为两个文件后,模板有Bug我们就只改View
文件,数据有Bug我们就只改Model
文件。
相关文章:

React学习笔记(番外二)——列表多选批量处理复合组件
React学习笔记(番外二)——列表多选批量操作复合组件前言〇、Show you the code一、 任务分析及拆解表头行的Checkbox——总开关记录行的Checkbox——行级开关二、 基础实现表头行的文件——header-row.js记录行的文件——record-row.js页面的文件App.js…...

Pom.xml详解
目录 1、Maven的下载安装 2、什么是pom? 3、较完整的pom元素 4、默认生成Maven工程的pom内容 5、自定义的属性变量 6、依赖管理 6.1、整体依赖关系列表 6.2、依赖关系的传递性 6.3、依赖传递可能造成的问题 6.3.1、scope依赖范围 6.3.2、依赖调节 6.3.3…...

浅谈软件测试需求管理
什么是需求管理? 需求管理,指对产品、系统或工程的开发需求的搜集、定义、分析、评审、整理、维护、追溯和复用等相关的管理工作和流程。通常特指应用程序或软件系统的研发需求。需求管理和配置管理、测试管理、缺陷管理、风险管理、变更管理等管理流程…...

面试题复盘
Vuex与本地存储的区别Vuex是一个专门为Vue.js应用程序开发的状态管理模式和库。它提供了一个中央存储库,用于存储应用程序的所有组件之间共享的状态【组件间通信的一种方法,一般用于中大型应用】。Vuex的主要目的是在Vue.js应用程序中管理复杂的状态逻辑…...

Telerik UI for WPF 2023 R1
Telerik UI for WPF 2023 R1 之 WPF 的 Telerik 用户界面,WPF 控件库开发人员信任,快速构建美观、高性能的 WPF 业务应用程序。现在支持 .NET 6 和 7.0。 概述部分背景图像 主要特征 现代专业主题图标,现代专业主题 通过各种受 Office、Wind…...

基于 CentOS7 的 KVM 部署 + 虚拟机创建
目录一、实验环境二、部署 KVM三、创建虚拟机四、远程管理 KVM 虚拟机FAQ一、实验环境 实验环境:VMware Workstation 16 Pro 打开虚拟机之前,首先开启 VMware Workstation Pro 16 上的硬件辅助虚拟化功能,如下图所示: 二、部署 …...

Python自动化测试实战篇(5)优化selenium+unittest+ddt,搞定100条测试用例只执行前50条
这些是之前的文章,里面有一些基础的知识点在前面由于前面已经有写过,所以这一篇就不再详细对之前的内容进行描述 Python自动化测试实战篇(1)读取xlsx中账户密码,unittest框架实现通过requests接口post登录网站请求&…...

C语言--数据的存储2
目录前言练习有符号类型与无符号类型char类型的取值范围有符号char无符号char有符号与无符号类型混合运算有符号无符号类型形成的bugchar类型取值范围应用浮点型在内存中的存储浮点数的存储浮点数存储规则浮点数取出规则前言 上篇文章我们讲解了数据类型,类型的基本…...

Ubuntu 安装 Qt5.7.0
下载 地址:https://download.qt.io/https://download.qt.io/ 文件夹说明: snapshots:预览版,该文件夹中包含最新的测试版本。 online:在线安装包。 official_releases:最终发布版。 new_archive&#…...

“世界”的伊利,“三难”的潘刚
(图片来源于网络,侵删) 来源 | 螳螂观察 文 | 叶小安 一棵草,一头牛,到一杯牛奶,乳品如何守住舌尖上的安全? 央视财经频道专访中,伊利集团董事长兼总裁潘自信满满地介绍了现代智…...

【新】华为OD机试 - 开心消消乐(Python)
开心消消乐 题目 给定一个 N 行 M 列的二维矩阵,矩阵中每个位置的数字取值为 0 或 1,矩阵示例如: 1 1 0 0 0 0 0 1 0 0 1 1 1 1 1 1现需要将矩阵中所有的 1 进行反转为 0,规则如下: 当点击一个 1 时,该 1 被反转为 0,同时相邻的上、下、左、右,以及左上、左下、右上…...

山东大学2022-2023数据仓库挖掘期末考题回忆
2023.2.14 一、 1.数据预处理的过程和解决问题 2.什么是离群点,检测离群点的四个方法 3.数据仓库的四个特点,画出数据仓库结构图 4.维度归约的两个方法及区别。 二、 两个模型用来预测新冠病毒的阳性和阴性 1.分别求准确率,精确率,…...

SSM整合
SSM整合 ContextLoaderListener Spring提供了监听器ContextLoaderListener,实现ServletContextListener接口,可监听 ServletContext的状态,在web服务器的启动,读取Spring的配置文件,创建Spring的IOC容器。 web 应用中…...

Android平台版本所对应的 API 级别
平台版本API级别版本号备注Android 1333TIRAMISU平台亮点Android 1232S_V2平台亮点31S平台亮点Android 1130R平台亮点Android 1029Q平台亮点Android 928P平台亮点Android 8.127O_MR1平台亮点Android 8.026O平台亮点Android 7.1.1Android 7.125N_MR1平台亮点Android 7.024N平台亮…...

入职字节外包一个月,我离职了
有一种打工人的羡慕,叫做“大厂”。 真是年少不知大厂香,错把青春插稻秧。 但是,在深圳有一群比大厂员工更庞大的群体,他们顶着大厂的“名”,做着大厂的工作,还可以享受大厂的伙食,却没有大厂…...

中创教育PMP分享,复盘没效果?该怎么办
复盘的清单框架 一、现在情况如何 二、当初是怎么决定的 三、让我们再审视下思考的前提 四、复盘他人 复盘没效果,我们可以试试下面的提问: 一、现在情况如何 现在做到什么程度? 当时定的目标是多少? 现在的结果和目标对比处于什么状态? 有没…...

Kubelet监控指标说明
Probe路径 含义:kubelet以及kubelet监控的Pod的存活性请求路径:“/metrics/probes”指标 名称类型含义upGauge服务是否存活prober_probe_totalCounter按结果计算容器的活动探测、就绪探测或启动探测的累计数目以及存活性结果 Metrics路径 含义&#x…...

python中的数据类型(价值6980的全能工程师课程笔记)
概述 python数据类型主要分为以下七大类: Numbers(数字) Boolean(布尔) String(字符串) List(列表) Tuple(元组) Dictionary(字典) Set(集合) Python 的七个标准数据类型中: 不可变数据类型(4 个):Number(数字)、Boolean(布尔)、String(字符串)、T…...

Tomcat 配置IPV6
文章目录一、场景二、tomcat开启ipv6三、ipv6环境配置四、访问总结一、场景 我们在linux下安装一个tomcat,启用ipv6的方式,然后在windows下用浏览器访问这个tomcat 二、tomcat开启ipv6 在server.xml配置文件的里面加上 address”[::]” ,这…...

JavaEE——MyBatis的简单介绍和使用
MyBatis是什么 MyBatis是一个支持普通SQL查询,存储过程以及高级映射的持久层框架,他消除了几乎所有的JDBC代码和参数的手动设置以及对结果集的检索,使用简单的XML或注解进行配置和原始映射,将接口和Java的POJO映射成数据库中的记…...

主数据管理平台如何进行模型管理
企业管理主数据,模型管理尤为重要。在对主数据进行建模操作时,既要保证数据通用性、安全性,又要符合企业的业务需求。今天小亿带你认识一下亿信华辰睿码主数据管理平台里的主数据模型管理。 主数据模型分类 一款专业的主数据管理平台&#…...

在 Kubernetes 上运行《我的世界》
最近给小朋友装上了叨叨许久的 Minecraft(我的世界),为了体验安装的是开源启动器 HMCL。其实这游戏我也关注比较久了,不过感觉太耗时间。但被小朋友拉上一起玩,便研究了下自建服务器。GitHub 发现已经有人做好了 Minec…...

万字长文掌握Python高并发
文章目录0 前言1 并发、并行、同步、异步、阻塞、非阻塞1.1 并发1.2 并行1.3 同步1.4 异步1.5 阻塞1.6 非阻塞2 多线程2.1 Python线程的创建方式2.1.1 方式一2.1.2 方式二 继承Thread2.1.3 通过线程池创建多线程2.2 聊聊GIL2.2.1 Python线程与操作系统线程的关系2.3 线程同步2.…...

高性能办公娱乐迷你主机——Maxtang大唐AMD5600U
今天给大家介绍一款AMD5600U迷你主机,说起这款处理器大家应该并不陌生,像联想小新、YOGA以及ThinkBook等很多款用的都是这个型号,不过笔记本的价格基本都在3999-4999这个价位区间,同样的处理器,笔记本卖那么贵…...

牛客教你用雇主品牌力抢人才!附6类校招玩法
最新校招数据显示,79%的应届生在Offer抉择时首要考量薪资福利。但谈钱多伤感情啊~牛客从100案例中挑出6种最潮的校招雇主品牌玩法,助力你抢人才。01、英特尔中国:“芯”动小镇雇主是否能让自己产生激情和热情,已经成为应届生选择O…...

leaflet: 鼠标mouseover显示城市信息,mouseout隐藏信息(067)
第067个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中显示城市信息,这里给marker添加鼠标事件,用到了mouseover和mouseout,用于控制信息的显示和隐藏。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码…...

docker部署springboot项目
1、创建放置项目jar包和Dockerfile的文件夹 cd usr/ mkdir reggie cd reggie/ 2、上传Dockerfile和项目jar包 Dockerfile内容如下: # 基础镜像使用java FROM java:8 # 作者 MAINTAINER chenxiansheng # VOLUME 指定了临时文件目录为/tmp。 # 其效果是在主机 /v…...

简单实用的CSS属性(滑轮滚动保持头部不动、暂无数据显示、元素隔开距离、带背景的文字效果、网页上禁止选中文字、校验值有效为绿色无效为红色、)
简单实用的CSS属性(滑轮滚动保持头部不动、暂无数据显示、元素隔开距离、带背景的文字效果、网页上禁止选中文字、校验值有效为绿色无效为红色、) 目录 一、滑轮滚动保持头部不动 二、暂无数据显示 三、元素隔开距离 四、带背景的文字效果 backgro…...

Unity 工具 之 SoftMask软遮罩 实现 UI 边缘渐变过渡的简单使用介绍
Unity 工具 之 SoftMask软遮罩 实现 UI 边缘渐变过渡的简单使用介绍 目录 Unity 工具 之 SoftMask软遮罩 实现 UI 边缘渐变过渡的简单使用介绍 一、简单介绍 二、Mask 实现的遮罩效果 三、Soft Mask 实现遮罩效果 四、 Soft Mask 的一些设置 五、插件下载 一、简单介绍 U…...

Python-第六天 Python数据容器
Python-第六天 Python数据容器一、数据容器入门1.数据容器二、数据容器:list(列表)1.列表的定义2.列表的下标(索引)3.列表的常用操作(方法)4.练习案例:常用功能练习5.list(列表)的遍历5.1 列表的…...