Compare commits
No commits in common. "658b7e6b59b49978ff276bdc2bb9a2c147909ae2" and "b2430abe736d07ead80891629e57b1210e23fa17" have entirely different histories.
658b7e6b59
...
b2430abe73
|
|
@ -9,10 +9,6 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^6.1.0",
|
"@ant-design/icons": "^6.1.0",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
|
||||||
"@dnd-kit/modifiers": "^9.0.0",
|
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
|
||||||
"antd": "^5.13.2",
|
"antd": "^5.13.2",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|
@ -440,73 +436,6 @@
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@dnd-kit/accessibility": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=16.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@dnd-kit/core": {
|
|
||||||
"version": "6.3.1",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.3.1.tgz",
|
|
||||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@dnd-kit/accessibility": "^3.1.1",
|
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
|
||||||
"tslib": "^2.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=16.8.0",
|
|
||||||
"react-dom": ">=16.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@dnd-kit/modifiers": {
|
|
||||||
"version": "9.0.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz",
|
|
||||||
"integrity": "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
|
||||||
"tslib": "^2.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@dnd-kit/core": "^6.3.0",
|
|
||||||
"react": ">=16.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@dnd-kit/sortable": {
|
|
||||||
"version": "10.0.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
|
|
||||||
"integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
|
||||||
"tslib": "^2.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@dnd-kit/core": "^6.3.0",
|
|
||||||
"react": ">=16.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@dnd-kit/utilities": {
|
|
||||||
"version": "3.2.2",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
|
|
||||||
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=16.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emotion/hash": {
|
"node_modules/@emotion/hash": {
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz",
|
||||||
|
|
@ -4646,12 +4575,6 @@
|
||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
||||||
"license": "0BSD"
|
|
||||||
},
|
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^6.1.0",
|
"@ant-design/icons": "^6.1.0",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
|
||||||
"@dnd-kit/modifiers": "^9.0.0",
|
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
|
||||||
"antd": "^5.13.2",
|
"antd": "^5.13.2",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button, Card, Col, Drawer, Form, Input, InputNumber, Popconfirm, Row, Select, Space, Table, Tag, Tooltip, Typography, App } from 'antd';
|
import { Button, Card, Col, Drawer, Form, Input, InputNumber, Popconfirm, Row, Select, Space, Table, Tag, Tooltip, Typography, App } from 'antd';
|
||||||
import * as AntIcons from "@ant-design/icons";
|
import * as AntIcons from "@ant-design/icons";
|
||||||
import { DndContext, PointerSensor, useSensor, useSensors, DragEndEvent } from '@dnd-kit/core';
|
import { CheckSquareOutlined, ClusterOutlined, DeleteOutlined, EditOutlined, FolderOutlined, InfoCircleOutlined, MenuOutlined, PlusOutlined, ReloadOutlined, SearchOutlined } from "@ant-design/icons";
|
||||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
|
||||||
import { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
|
||||||
import { CheckSquareOutlined, ClusterOutlined, DeleteOutlined, EditOutlined, FolderOutlined, InfoCircleOutlined, MenuOutlined, PlusOutlined, ReloadOutlined, SearchOutlined, HolderOutlined } from "@ant-design/icons";
|
|
||||||
import { createPermission, deletePermission, listMyPermissions, updatePermission } from "@/api";
|
import { createPermission, deletePermission, listMyPermissions, updatePermission } from "@/api";
|
||||||
import { useDict } from "@/hooks/useDict";
|
import { useDict } from "@/hooks/useDict";
|
||||||
import { usePermission } from "@/hooks/usePermission";
|
import { usePermission } from "@/hooks/usePermission";
|
||||||
|
|
@ -30,42 +26,6 @@ const legacyIconAliases: Record<string, string> = {
|
||||||
setting: "SettingOutlined"
|
setting: "SettingOutlined"
|
||||||
};
|
};
|
||||||
|
|
||||||
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
|
||||||
'data-row-key': string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DragContext = React.createContext<any>({});
|
|
||||||
|
|
||||||
const DraggableRow = ({ children, ...props }: RowProps) => {
|
|
||||||
const id = props['data-row-key']?.toString();
|
|
||||||
const {
|
|
||||||
attributes,
|
|
||||||
listeners,
|
|
||||||
setNodeRef,
|
|
||||||
setActivatorNodeRef,
|
|
||||||
transform,
|
|
||||||
transition,
|
|
||||||
isDragging,
|
|
||||||
} = useSortable({
|
|
||||||
id: id || 'empty-id',
|
|
||||||
});
|
|
||||||
|
|
||||||
const style: React.CSSProperties = {
|
|
||||||
...props.style,
|
|
||||||
transform: CSS.Transform.toString(transform && { ...transform, scaleY: 1 }),
|
|
||||||
transition,
|
|
||||||
...(isDragging ? { position: 'relative', zIndex: 9999, background: '#fafafa', boxShadow: '0 5px 15px rgba(0,0,0,0.15)' } : {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DragContext.Provider value={{ setActivatorNodeRef, listeners }}>
|
|
||||||
<tr {...props} ref={setNodeRef} style={style} {...attributes}>
|
|
||||||
{children}
|
|
||||||
</tr>
|
|
||||||
</DragContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuIconOptions = Object.keys(AntIcons)
|
const menuIconOptions = Object.keys(AntIcons)
|
||||||
.filter((key) => /(?:Outlined|Filled|TwoTone)$/.test(key))
|
.filter((key) => /(?:Outlined|Filled|TwoTone)$/.test(key))
|
||||||
.sort((left, right) => left.localeCompare(right));
|
.sort((left, right) => left.localeCompare(right));
|
||||||
|
|
@ -122,14 +82,6 @@ export default function Permissions() {
|
||||||
const iconGridRef = useRef<HTMLDivElement | null>(null);
|
const iconGridRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const sensors = useSensors(
|
|
||||||
useSensor(PointerSensor, {
|
|
||||||
activationConstraint: {
|
|
||||||
distance: 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
|
@ -154,21 +106,6 @@ export default function Permissions() {
|
||||||
}, [data, query]);
|
}, [data, query]);
|
||||||
|
|
||||||
const treeData = useMemo(() => buildTree(filtered), [filtered]);
|
const treeData = useMemo(() => buildTree(filtered), [filtered]);
|
||||||
|
|
||||||
const flatVisualKeys = useMemo(() => {
|
|
||||||
const keys: string[] = [];
|
|
||||||
const traverse = (nodes: TreePermission[]) => {
|
|
||||||
nodes.forEach(node => {
|
|
||||||
keys.push(node.permId.toString());
|
|
||||||
if (node.children && node.children.length > 0) {
|
|
||||||
traverse(node.children);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
traverse(treeData);
|
|
||||||
return keys;
|
|
||||||
}, [treeData]);
|
|
||||||
|
|
||||||
const currentPermType = Form.useWatch("permType", form);
|
const currentPermType = Form.useWatch("permType", form);
|
||||||
const selectedIcon = Form.useWatch("icon", form);
|
const selectedIcon = Form.useWatch("icon", form);
|
||||||
|
|
||||||
|
|
@ -209,46 +146,6 @@ export default function Permissions() {
|
||||||
setIconSearchKeyword("");
|
setIconSearchKeyword("");
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragEnd = async ({ active, over }: DragEndEvent) => {
|
|
||||||
if (active.id !== over?.id) {
|
|
||||||
const activeId = Number(active.id);
|
|
||||||
const overId = Number(over?.id);
|
|
||||||
const activeNode = data.find(n => n.permId === activeId);
|
|
||||||
const overNode = data.find(n => n.permId === overId);
|
|
||||||
if (!activeNode || !overNode) return;
|
|
||||||
if (activeNode.parentId !== overNode.parentId) {
|
|
||||||
message.warning("只能在同级节点之间排序");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const siblings = data
|
|
||||||
.filter(n => n.parentId === activeNode.parentId)
|
|
||||||
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
||||||
|
|
||||||
const activeIndex = siblings.findIndex(n => n.permId === activeId);
|
|
||||||
const overIndex = siblings.findIndex(n => n.permId === overId);
|
|
||||||
|
|
||||||
const newSiblings = [...siblings];
|
|
||||||
const [removed] = newSiblings.splice(activeIndex, 1);
|
|
||||||
newSiblings.splice(overIndex, 0, removed);
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const updates = newSiblings.map((node, index) => {
|
|
||||||
if (node.sortOrder !== index) {
|
|
||||||
return updatePermission(node.permId, { sortOrder: index });
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
});
|
|
||||||
await Promise.all(updates);
|
|
||||||
message.success(t("common.success"));
|
|
||||||
load();
|
|
||||||
} catch (e) {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const openCreate = () => {
|
const openCreate = () => {
|
||||||
setEditing(null);
|
setEditing(null);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
|
|
@ -326,21 +223,6 @@ export default function Permissions() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
|
||||||
key: 'sortHandle',
|
|
||||||
width: 40,
|
|
||||||
align: 'center' as const,
|
|
||||||
render: () => {
|
|
||||||
const { setActivatorNodeRef, listeners } = React.useContext(DragContext);
|
|
||||||
return (
|
|
||||||
<HolderOutlined
|
|
||||||
ref={setActivatorNodeRef}
|
|
||||||
style={{ touchAction: 'none', cursor: 'move', color: '#999' }}
|
|
||||||
{...listeners}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t("permissions.permName"),
|
title: t("permissions.permName"),
|
||||||
dataIndex: "name",
|
dataIndex: "name",
|
||||||
|
|
@ -432,29 +314,7 @@ export default function Permissions() {
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="app-page__content-card permissions-content-card flex-1 flex flex-col overflow-hidden" styles={{ body: { padding: 0, flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" } }}>
|
<Card className="app-page__content-card permissions-content-card flex-1 flex flex-col overflow-hidden" styles={{ body: { padding: 0, flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" } }}>
|
||||||
<DndContext sensors={sensors} modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
|
<Table className="permissions-table-full" rowKey="permId" loading={loading} dataSource={treeData} columns={columns} pagination={false} size="middle" scroll={{ x: 'max-content', y: '100%' }} expandable={{ defaultExpandAllRows: false, rowExpandable: (record) => record.permType !== "button" && !!record.children?.length }} />
|
||||||
<SortableContext
|
|
||||||
items={flatVisualKeys}
|
|
||||||
strategy={verticalListSortingStrategy}
|
|
||||||
>
|
|
||||||
<Table
|
|
||||||
className="permissions-table-full"
|
|
||||||
rowKey="permId"
|
|
||||||
loading={loading}
|
|
||||||
dataSource={treeData}
|
|
||||||
columns={columns}
|
|
||||||
pagination={false}
|
|
||||||
size="middle"
|
|
||||||
scroll={{ x: 'max-content', y: '100%' }}
|
|
||||||
expandable={{ defaultExpandAllRows: false, rowExpandable: (record) => record.permType !== "button" && !!record.children?.length, expandIconColumnIndex: 1 }}
|
|
||||||
components={{
|
|
||||||
body: {
|
|
||||||
row: DraggableRow,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SortableContext>
|
|
||||||
</DndContext>
|
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Drawer title={<Space><ClusterOutlined aria-hidden="true" /><span>{editing ? t("permissions.drawerTitleEdit") : t("permissions.drawerTitleCreate")}</span></Space>} open={open} onClose={() => setOpen(false)} width={520} destroyOnHidden forceRender footer={<div className="app-page__drawer-footer"><Button onClick={() => setOpen(false)}>{t("common.cancel")}</Button><Button type="primary" loading={saving} onClick={submit}>{t("common.save")}</Button></div>}>
|
<Drawer title={<Space><ClusterOutlined aria-hidden="true" /><span>{editing ? t("permissions.drawerTitleEdit") : t("permissions.drawerTitleCreate")}</span></Space>} open={open} onClose={() => setOpen(false)} width={520} destroyOnHidden forceRender footer={<div className="app-page__drawer-footer"><Button onClick={() => setOpen(false)}>{t("common.cancel")}</Button><Button type="primary" loading={saving} onClick={submit}>{t("common.save")}</Button></div>}>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export default defineConfig({
|
||||||
"/sys": "http://10.100.53.199:8080",
|
"/sys": "http://10.100.53.199:8080",
|
||||||
"/api": "http://10.100.53.199:8080",
|
"/api": "http://10.100.53.199:8080",
|
||||||
"/ws": {
|
"/ws": {
|
||||||
target: "ws://10.100.51.199:8080",
|
target: "ws://10.100.53.199:8080",
|
||||||
ws: true
|
ws: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue