index.jsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. import * as React from "react";
  2. import { useState } from "react";
  3. import {
  4. FolderRegular,
  5. DocumentRegular,
  6. DocumentPdfRegular,
  7. VideoRegular,
  8. ChevronDown16Filled,
  9. TextField16Regular,
  10. Calendar16Regular,
  11. SelectAllOn16Regular,
  12. CommentAdd16Regular,
  13. MoreHorizontal16Filled,
  14. NumberCircle116Regular,
  15. Add16Filled
  16. } from "@fluentui/react-icons";
  17. import {
  18. DataGridBody,
  19. DataGridRow,
  20. DataGrid,
  21. DataGridHeader,
  22. DataGridHeaderCell,
  23. DataGridCell,
  24. TableCellLayout,
  25. createTableColumn,
  26. Button,
  27. Menu,
  28. MenuTrigger,
  29. MenuList,
  30. MenuItem,
  31. MenuPopover,
  32. Tooltip,
  33. MenuDivider,
  34. Input,
  35. Select,
  36. } from "@fluentui/react-components";
  37. import { DatePicker } from "@fluentui/react-datepicker-compat";
  38. const List = (editType) => {
  39. // const defaultSelectedItems = React.useMemo(() => new Set([1]), []);
  40. //数据类型常量
  41. const DATA_TYPE = {
  42. 文本: "文本",
  43. 数字: "数字",
  44. 日期: "日期",
  45. 选项: "选项"
  46. }
  47. /**
  48. * 列头下拉菜单
  49. * @param {*} dataType 表现类型
  50. */
  51. const HeaderMenuList = (props) => {
  52. return (
  53. <Menu>
  54. <MenuTrigger disableButtonEnhancement>
  55. <Button
  56. appearance="subtle"
  57. icon={<ChevronDown16Filled />}
  58. ></Button>
  59. </MenuTrigger>
  60. <MenuPopover>
  61. <MenuList>
  62. {props.dataType == DATA_TYPE.数字 && <MenuItem onClick={() => {
  63. ascendingData(props.columnId);
  64. }}>从小到大 </MenuItem>}
  65. {props.dataType == DATA_TYPE.数字 && <MenuItem onClick={() => {
  66. descendingData(props.columnId);
  67. }}>从大到小</MenuItem>}
  68. {(props.dataType == DATA_TYPE.文本 || props.dataType == DATA_TYPE.选项) && <MenuItem onClick={() => {
  69. ascendingData(props.columnId);
  70. }}>A到Z </MenuItem>}
  71. {(props.dataType == DATA_TYPE.文本 || props.dataType == DATA_TYPE.选项) && <MenuItem onClick={() => {
  72. descendingData(props.columnId);
  73. }}>Z到A </MenuItem>}
  74. {(props.dataType == DATA_TYPE.日期) && <MenuItem onClick={() => {
  75. ascendingData(props.columnId);
  76. }}>从旧到新 </MenuItem>}
  77. {(props.dataType == DATA_TYPE.日期) && <MenuItem onClick={() => {
  78. descendingData(props.columnId);
  79. }}>从新到旧 </MenuItem>}
  80. <MenuDivider />
  81. <MenuItem>筛选方式</MenuItem>
  82. <MenuDivider />
  83. <MenuItem>按“{props.columnId}”分组</MenuItem>
  84. <MenuDivider />
  85. <MenuItem><HeaderMenuColumnSet dataType={props.dataType} /></MenuItem>
  86. <MenuDivider />
  87. <MenuItem><HeaderMenuAmount dataType={props.dataType} /></MenuItem>
  88. </MenuList>
  89. </MenuPopover>
  90. </Menu>
  91. );
  92. }
  93. const [dataSortType, setDataSortType] = useState(({
  94. sortColumn: "序号",
  95. sortDirection: "ascending",
  96. }));
  97. const ascendingData = (columnId) => {
  98. setDataSortType(({
  99. sortColumn: columnId,
  100. sortDirection: "ascending",
  101. }));
  102. }
  103. const descendingData = (columnId) => {
  104. setDataSortType(({
  105. sortColumn: columnId,
  106. sortDirection: "descending",
  107. }));
  108. }
  109. /**
  110. * 列设置的二级菜单
  111. * @returns
  112. */
  113. const HeaderMenuColumnSet = (props) => {
  114. return (
  115. <Menu>
  116. <MenuTrigger disableButtonEnhancement>
  117. <MenuItem>列设置</MenuItem>
  118. </MenuTrigger>
  119. <MenuPopover>
  120. <MenuList>
  121. <MenuItem>编辑</MenuItem>
  122. <MenuItem>隐藏此列</MenuItem>
  123. <MenuItem>设置此列的格式</MenuItem>
  124. <MenuItem>向左移动</MenuItem>
  125. <MenuItem>向右移动</MenuItem>
  126. <MenuItem>显示/隐藏列</MenuItem>
  127. <MenuItem>加宽列</MenuItem>
  128. <MenuItem>窄列</MenuItem>
  129. <MenuItem>固定到筛选器窗口</MenuItem>
  130. <MenuItem>添加列</MenuItem>
  131. </MenuList>
  132. </MenuPopover>
  133. </Menu>
  134. );
  135. }
  136. /**
  137. * 列头“合计”二级菜单
  138. */
  139. const HeaderMenuAmount = (props) => {
  140. return (
  141. <Menu>
  142. <MenuTrigger disableButtonEnhancement>
  143. <MenuItem>合计</MenuItem>
  144. </MenuTrigger>
  145. <MenuPopover>
  146. <MenuList>
  147. <MenuItem>无</MenuItem>
  148. <MenuItem>计数</MenuItem>
  149. {props.dataType == DATA_TYPE.数字 && <MenuItem>平均值</MenuItem>}
  150. {props.dataType == DATA_TYPE.数字 && <MenuItem>最大值</MenuItem>}
  151. {props.dataType == DATA_TYPE.数字 && <MenuItem>最小值</MenuItem>}
  152. {props.dataType == DATA_TYPE.数字 && <MenuItem>求和</MenuItem>}
  153. {props.dataType == DATA_TYPE.数字 && <MenuItem>标准偏差</MenuItem>}
  154. {props.dataType == DATA_TYPE.数字 && <MenuItem>方差</MenuItem>}
  155. </MenuList>
  156. </MenuPopover>
  157. </Menu>
  158. );
  159. }
  160. /**
  161. * 鼠标移动到姓名列上时,显示两个按钮
  162. * @param item 包含姓名标签的对象
  163. * @returns 返回 JSX 元素
  164. */
  165. const ChangeBtnShowType = (props) => {
  166. const [showButton, setShowButton] = useState(false);
  167. const handleMouseEnter = () => {
  168. setShowButton(true);
  169. };
  170. const handleMouseLeave = () => {
  171. setShowButton(false);
  172. };
  173. return (
  174. <div
  175. style={{
  176. width: "100%",
  177. display: "flex",
  178. justifyContent: "space-between",
  179. alignItems: "center",
  180. }}
  181. onMouseEnter={handleMouseEnter}
  182. onMouseLeave={handleMouseLeave}
  183. >
  184. {editType.edit == "true" ? <Input type="text" style={{ width: "calc(100% - 70px)" }} defaultValue={props.cellData} /> : props.cellData}
  185. {showButton && (
  186. <div>
  187. <Tooltip content="更多操作" positioning="below">
  188. <Button
  189. appearance="subtle"
  190. icon={<MoreHorizontal16Filled />}
  191. onClick={() => {
  192. alert("更多操作");
  193. }}
  194. ></Button>
  195. </Tooltip>
  196. <Tooltip content="添加注释" positioning="below">
  197. <Button appearance="subtle" icon={<CommentAdd16Regular />}></Button>
  198. </Tooltip>
  199. </div>
  200. )}
  201. </div>
  202. );
  203. };
  204. const tableData = [
  205. {
  206. 序号: { label: 1 },
  207. 姓名: { label: "陈丹" },
  208. 性别: { label: "女" },
  209. 年龄: { label: "18岁" },
  210. 最后在线时间: { label: "2024-11-03" },
  211. },
  212. {
  213. 序号: { label: 2 },
  214. 姓名: { label: "蔡青松" },
  215. 性别: { label: "男", },
  216. 年龄: { label: "30岁" },
  217. 最后在线时间: { label: "2024-11-04" },
  218. },
  219. {
  220. 序号: { label: 3 },
  221. 姓名: { label: "杨东明" },
  222. 性别: { label: "男" },
  223. 年龄: { label: "25岁" },
  224. 最后在线时间: { label: "2024-11-05" },
  225. },
  226. {
  227. 序号: { label: 4 },
  228. 姓名: { label: "吴瑞强" },
  229. 性别: { label: "男" },
  230. 年龄: { label: "25岁" },
  231. 最后在线时间: { label: "2024-11-06" },
  232. },
  233. ]
  234. const [newTableData, setTabeData] = useState(tableData);
  235. /**
  236. * 新增空行
  237. */
  238. const AddNewRow = () => {
  239. setTabeData(newTableData.concat([{
  240. 序号: { label: newTableData.length + 1 },
  241. 姓名: { label: "" },
  242. 性别: { label: "" },
  243. 年龄: { label: "" },
  244. 最后在线时间: { label: "" },
  245. }]));
  246. }
  247. return (
  248. <DataGrid
  249. items={newTableData}
  250. columns={[
  251. createTableColumn({
  252. columnId: "姓名",
  253. compare: (a, b) => {
  254. return a.姓名.label.localeCompare(b.姓名.label);
  255. },
  256. renderHeaderCell: () => {
  257. return (
  258. <TableCellLayout media={<TextField16Regular />}>
  259. 姓名
  260. <HeaderMenuList dataType={DATA_TYPE.文本} title="姓名" columnId="姓名" />
  261. </TableCellLayout>
  262. );
  263. },
  264. renderCell: (item) => {
  265. return <ChangeBtnShowType cellData={item.姓名.label} />
  266. },
  267. }),
  268. createTableColumn({
  269. columnId: "序号",
  270. fixed: true,
  271. compare: (a, b) => {
  272. return a.序号.label - b.序号.label;
  273. },
  274. renderHeaderCell: () => {
  275. return (
  276. <TableCellLayout media={<NumberCircle116Regular />}>
  277. 序号
  278. <HeaderMenuList dataType={DATA_TYPE.数字} title="序号" columnId="序号" />
  279. </TableCellLayout>
  280. );
  281. },
  282. renderCell: (item) => {
  283. return editType.edit == "true" ? <Input type="number" defaultValue={item.序号.label} style={{ width: "100%" }} /> : item.序号.label;
  284. },
  285. }),
  286. createTableColumn({
  287. columnId: "性别",
  288. compare: (a, b) => {
  289. return a.性别.label.localeCompare(b.性别.label);
  290. },
  291. renderHeaderCell: () => {
  292. return (
  293. <TableCellLayout media={<SelectAllOn16Regular />}>
  294. 性别
  295. <HeaderMenuList dataType={DATA_TYPE.选项} title="性别" columnId="性别" />
  296. </TableCellLayout>
  297. );
  298. },
  299. renderCell: (item) => {
  300. return editType.edit == "true" ? <Select style={{ width: "calc(100% - 70px)" }}>
  301. <option value="男" selected={item.性别.label == "男"}>男</option>
  302. <option value="女" selected={item.性别.label == "女"}>女</option>
  303. </Select> : item.性别.label;
  304. },
  305. }),
  306. createTableColumn({
  307. columnId: "年龄",
  308. compare: (a, b) => {
  309. return a.年龄.timestamp - b.年龄.timestamp;
  310. },
  311. renderHeaderCell: () => {
  312. return (
  313. <TableCellLayout media={<TextField16Regular />}>
  314. 年龄
  315. <HeaderMenuList dataType={DATA_TYPE.文本} title="年龄" columnId="年龄" />
  316. </TableCellLayout>
  317. );
  318. },
  319. renderCell: (item) => {
  320. return item.年龄.label;
  321. },
  322. }),
  323. createTableColumn({
  324. columnId: "最后在线时间",
  325. compare: (a, b) => {
  326. return new Date(a.最后在线时间.label) - new Date(b.最后在线时间.label);
  327. },
  328. renderHeaderCell: () => {
  329. return (
  330. <TableCellLayout media={<Calendar16Regular />}>
  331. 最后在线时间
  332. <HeaderMenuList dataType={DATA_TYPE.日期} title="最后在线时间" columnId="最后在线时间" />
  333. </TableCellLayout>
  334. );
  335. },
  336. renderCell: (item) => {
  337. return editType.edit == "true" ? <Input type="date" defaultValue={item.最后在线时间.label} /> : item.最后在线时间.label;
  338. },
  339. }),
  340. ]}
  341. selectionMode="multiselect"
  342. sortable
  343. sortState={dataSortType}
  344. subtleSelection
  345. resizableColumns
  346. // defaultSelectedItems={defaultSelectedItems}
  347. style={{ minWidth: "550px" }}
  348. >
  349. <DataGridHeader>
  350. <DataGridRow>
  351. {({ renderHeaderCell }) => (
  352. <DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>
  353. )}
  354. </DataGridRow>
  355. </DataGridHeader>
  356. <DataGridBody>
  357. {({ item, rowId }) => (
  358. <DataGridRow
  359. key={rowId}
  360. selectionCell={{ radioIndicator: { "aria-label": "Select row" } }}
  361. >
  362. {({ renderCell }) => (
  363. <DataGridCell>{renderCell(item)}</DataGridCell>
  364. )}
  365. </DataGridRow>
  366. )}
  367. </DataGridBody>
  368. <Button
  369. appearance="subtle"
  370. icon={<Add16Filled />}
  371. style={{ color: "#ca5010" }}
  372. onClick={() => {
  373. AddNewRow();
  374. }}
  375. >添加新项目</Button>
  376. </DataGrid>
  377. );
  378. };
  379. export default List;