This commit is contained in:
wang_yp 2025-04-08 01:29:17 +08:00
parent 2de5024376
commit 81d5e6a9d1
13 changed files with 497 additions and 221 deletions

View File

@ -17,6 +17,8 @@ const BTable = (props: any) => {
editCallback, editCallback,
deleteCallback, deleteCallback,
actionCloumn, actionCloumn,
onSizeChange,
onPageChange,
} = props; } = props;
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const onSelectChange = (newSelectedRowKeys: React.Key[]) => { const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
@ -29,16 +31,24 @@ const BTable = (props: any) => {
onChange: onSelectChange, onChange: onSelectChange,
}; };
const onShowSizeChange = (current, pageSize) => { const onShowSizeChange = (current, pageSize) => {
if (onSizeChange) {
onSizeChange(current, pageSize);
} else {
store.setPages({ store.setPages({
Offset: current, Offset: current,
Size: pageSize, Size: pageSize,
}); });
}
}; };
const onChange: PaginationProps["onChange"] = (page) => { const onChange: PaginationProps["onChange"] = (page) => {
if (onPageChange) {
onPageChange(page);
} else {
store.setPages({ store.setPages({
Offset: page, Offset: page,
Size: 20, Size: 20,
}); });
}
}; };
const cancel: PopconfirmProps["onCancel"] = (e) => {}; const cancel: PopconfirmProps["onCancel"] = (e) => {};

View File

@ -6,9 +6,10 @@ import { useEffect, useState } from "react";
import { Avatar, Breadcrumb, Layout, Menu, theme } from "antd"; import { Avatar, Breadcrumb, Layout, Menu, theme } from "antd";
import { UserOutlined } from "@ant-design/icons"; import { UserOutlined } from "@ant-design/icons";
import Sider from "antd/es/layout/Sider"; import Sider from "antd/es/layout/Sider";
import { items } from "./layout_config"; import { items, headItems } from "./layout_config";
import { Dropdown } from "antd/lib"; import { Dropdown } from "antd/lib";
import { Outlet, useNavigate } from "react-router"; import { Outlet, useNavigate } from "react-router";
const LayOut = (props: Store) => { const LayOut = (props: Store) => {
const { usrStore } = props; const { usrStore } = props;
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
@ -45,7 +46,13 @@ const LayOut = (props: Store) => {
<Layout> <Layout>
<Header style={headStyle}> <Header style={headStyle}>
<div style={logoStyle}>logo</div> <div style={logoStyle}>logo</div>
<Dropdown menu={{ items }}> <Dropdown menu={{ items: headItems,selectable: true, onClick: (e) => {
console.log(e.key==="loginout")
if (e.key==="loginout"){
window.localStorage.removeItem("token")
nav("/login")
}
} }}>
<Avatar icon={<UserOutlined />} /> <Avatar icon={<UserOutlined />} />
</Dropdown> </Dropdown>
</Header> </Header>

View File

@ -6,16 +6,12 @@ export const items = [
{ {
key: "/user", key: "/user",
label: `用户管理`, label: `用户管理`,
children: [ children: [{ key: "/user/list", label: `用户管理` }],
{ key: "/user/list", label: `用户管理` },
],
}, },
{ {
key: "/source", key: "/source",
label: `数据管理`, label: `数据管理`,
children: [ children: [{ label: "数据管理", key: "/source/list" }],
{ label: "数据管理", key: "/source/list" },
],
}, },
{ {
key: "/permission", key: "/permission",
@ -28,8 +24,17 @@ export const items = [
{ {
key: "/sys", key: "/sys",
label: `系统管理`, label: `系统管理`,
children: [ children: [{ label: `部门管理`, key: "/dep" }],
{ label: `部门管理`, key: "/dep" }, },
], ];
export const headItems = [
{
key: "my",
label: `我的`,
},
{
key: "loginout",
label: `退出登录`,
}, },
]; ];

View File

@ -1,74 +1,117 @@
.login_model { *,
*:before,
*:after {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.login {
background-color: #080710;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-repeat: no-repeat; .background {
background-size: 100% 100%; width: 430px;
display: flex; height: 520px;
align-items: center; position: absolute;
justify-content: center; transform: translate(-50%, -50%);
.login_box {
text-align: right;
width: 100%;
height: 100%;
#login_basic {
width: 100%;
}
.container {
height: 100%;
}
.login-wrapper {
background-color: #fff;
width: 35%;
height: 400px;
border-radius: 15px;
padding: 0 50px;
position: relative;
left: 50%; left: 50%;
top: 50%; top: 50%;
}
.background .shape {
height: 200px;
width: 200px;
position: absolute;
border-radius: 50%;
}
.shape:first-child {
background: linear-gradient(#1845ad, #23a2f6);
left: -80px;
top: -80px;
}
.shape:last-child {
background: linear-gradient(to right, #ff512f, #f09819);
right: -30px;
bottom: -80px;
}
form {
height: 520px;
width: 400px;
background-color: rgba(255, 255, 255, 0.13);
position: absolute;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} top: 50%;
left: 50%;
.header {
font-size: 38px;
font-weight: bold;
text-align: center;
line-height: 180px;
}
.input-item {
display: block;
width: 100%;
margin-bottom: 20px;
border: 0;
padding: 10px;
font-size: 15px;
outline: none;
}
.input-item:placeholder {
text-transform: uppercase;
}
.btn {
text-align: center;
cursor: pointer;
background-color: red;
color: #fff;
padding: 10px;
border-radius: 10px; border-radius: 10px;
width: 100%; backdrop-filter: blur(10px);
margin-top: 40px; border: 2px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 0 40px rgba(8, 7, 16, 0.6);
padding: 50px 35px;
} }
form * {
.msg { font-family: "Poppins", sans-serif;
color: #ffffff;
letter-spacing: 0.5px;
outline: none;
border: none;
}
form h3 {
font-size: 32px;
font-weight: 500;
line-height: 42px;
text-align: center; text-align: center;
line-height: 88px;
} }
a { label {
text-decoration-line: none; display: block;
color: #abc1ee; margin-top: 30px;
font-size: 16px;
font-weight: 500;
} }
input {
display: block;
height: 50px;
width: 100%;
background-color: rgba(255, 255, 255, 0.07);
border-radius: 3px;
padding: 0 10px;
margin-top: 8px;
font-size: 14px;
font-weight: 300;
}
::placeholder {
color: #e5e5e5;
}
button {
margin-top: 50px;
width: 100%;
background-color: #ffffff;
color: #080710;
padding: 15px 0;
font-size: 18px;
font-weight: 600;
border-radius: 5px;
cursor: pointer;
}
.social {
margin-top: 30px;
display: flex;
}
.social div {
background: red;
width: 150px;
border-radius: 3px;
padding: 5px 10px 10px 5px;
background-color: rgba(255, 255, 255, 0.27);
color: #eaf0fb;
text-align: center;
}
.social div:hover {
background-color: rgba(255, 255, 255, 0.47);
}
.social .fb {
margin-left: 25px;
}
.social i {
margin-right: 4px;
} }
} }

View File

@ -1,68 +1,39 @@
import { useNavigate } from "react-router-dom";
import "./login.less";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import React from "react"; import "./login.less";
import { FormInstance } from "antd/lib/form"; import { Store } from "antd/es/form/interface";
import SimpleForm from "@/components/form/simple_form"; import { useNavigate } from "react-router";
const Login = (props) => { import { useRef } from "react";
const Login = (props: Store) => {
const { usrStore } = props; const { usrStore } = props;
const formRef = React.useRef<FormInstance>(null); const formRef = useRef<any>(null);
const navigate = useNavigate(); const navigate = useNavigate();
const onFinish = async (values: any) => { const onFinish = async (e: any) => {
e.preventDefault();
const formData = new FormData(formRef.current);
const data = Object.fromEntries(formData);
let status = await usrStore.login({ let status = await usrStore.login({
userName: values.account, userName: data.account,
passWord: values.password, passWord: data.password,
}); });
if (status) { if (status) {
usrStore.closeLoginDilog(); usrStore.closeLoginDilog();
navigate("/user/list", { replace: true }); navigate("/user/list", { replace: true });
} }
}; };
const onFinishFailed = () => {};
const loginForm = [
{
type: "input",
label: "用户名",
name: "account",
value: "",
rules: [{ required: true, message: "请输入用户名!" }],
},
{
type: "password",
label: "密码",
name: "password",
value: "",
rules: [{ required: true, message: "请输入密码!" }],
},
];
return ( return (
<div className="login_model"> <div className="login">
<div className="login_box"> <div className="background">
<div className="container"> <div className="shape"></div>
<div className="login-wrapper"> <div className="shape"></div>
<div className="header">Login</div>
<div className="form-wrapper">
<SimpleForm
formRef={formRef}
formName="login_basic"
colProps={16}
formDatas={loginForm}
onFinish={onFinish}
initialValues={true}
onFinishFailed={onFinishFailed}
/>
<div
className="btn"
onClick={() => {
formRef.current?.submit();
}}
>
</div>
</div>
</div>
</div>
</div> </div>
<h3>Login Here</h3>
<form ref={formRef} onSubmit={onFinish} >
<label htmlFor="username">Username</label>
<input type="text" name="account" placeholder="Email or Phone" id="account" />
<label htmlFor="password">Password</label>
<input type="password" name="password" placeholder="Password" id="password" />
<button type="submit">Log In</button>
</form>
</div> </div>
); );
}; };

View File

@ -1,47 +1,237 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Space } from "antd"; import {
Button,
Checkbox,
Form,
Input,
message,
PaginationProps,
Popconfirm,
Space,
Table,
Typography,
} from "antd";
import { Store } from "antd/lib/form/interface"; import { Store } from "antd/lib/form/interface";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import BTable from "@/components/b_table";
import "./source.less"; import "./source.less";
import { DataType } from "@/util/model/interface";
import Upload from "./upload";
const Source = (props: Store) => { const Source = (props: Store) => {
const { sourceStore } = props; const { sourceStore } = props;
const [coloums,setColumns] = useState([]) const [form] = Form.useForm();
const [content,setContent] = useState([]) const [coloums, setColumns] = useState<any>([]);
const [content, setContent] = useState([]);
const [selectKey, setSelectKey] = useState<Array<string>>([]);
const [page, setPage] = useState<number>(1);
const [editingKey, setEditingKey] = useState("");
const [loading, setLoading] = useState(false);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const isEditing = (record) => record.key === editingKey;
const edit = (record: any) => {
form.setFieldsValue({ ...record });
setEditingKey(record.key);
};
// 获取列表数据 // 获取列表数据
useEffect(() => { useEffect(() => {
sourceStore.getHead().then((res)=>{ sourceStore.getHead().then((res) => {
res.forEach(element => { res.forEach((element) => {
element.dataIndex = "dbs_"+element.identity.toLowerCase() element.dataIndex = "dbs_" + element.identity.toLowerCase();
element.title = element.data_name element.title = element.data_name;
element.label = element.data_name;
element.value = element.identity;
element.editable = true;
}); });
setColumns(res) setColumns(res);
});
sourceStore.geContent().then((res)=>{
res.forEach(element => {
element.key = "dbs_"+element.identity
});
setContent(res)
}); });
getContent([], 1);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sourceStore]); }, [sourceStore]);
const save = async (key: React.Key) => {
try {
const row = (await form.validateFields()) as DataType;
const newData: any = [...content];
const index = newData.findIndex((item: any) => key === item.key);
if (index > -1) {
const item = newData[index];
setLoading(true);
let res = await sourceStore.modefyData(row, item.id_card);
if (res) {
getContent(selectKey, 1);
}
setLoading(false);
setEditingKey("");
}
} catch (errInfo) {
console.log("Validate Failed:", errInfo);
}
};
const getContent = (list, index) => {
sourceStore.geContent(list, index, 20).then((res) => {
res.forEach((element) => {
element.key = "dbs_" + element.identity;
});
setContent(res);
});
};
const saveClo = () => {
if (selectKey.length === 0) {
message.info("请勾选需要收藏的数据");
}
};
const cancel = () => {
setEditingKey("");
};
interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
editing: boolean;
dataIndex: string;
fixed: string;
index: number;
}
const actionCloumn = {
title: "操作",
with: 200,
fixed: "right",
render: (_: any, record) => {
const editable = isEditing(record);
return editable ? (
<span>
<Typography.Link
onClick={() => {
save(record.key);
}}
style={{ marginInlineEnd: 8 }}
>
</Typography.Link>
<Popconfirm title="Sure to cancel?" onConfirm={cancel}>
</Popconfirm>
</span>
) : (
<Space>
<Typography.Link
disabled={editingKey !== ""}
onClick={() => edit(record)}
>
</Typography.Link>
<Popconfirm
title="删除确认"
description="您是否需要删除该数据?"
onConfirm={() => {
// deleteCallback(record.identity);
}}
onCancel={cancel}
okText="Yes"
cancelText="No"
>
<Button type="dashed" danger size="small">
</Button>
</Popconfirm>
</Space>
);
},
};
const EditableCell: React.FC<React.PropsWithChildren<EditableCellProps>> = ({
editing,
dataIndex,
children,
...restProps
}) => {
const inputNode = <Input />;
return (
<td {...restProps}>
{editing ? (
<Form.Item name={dataIndex} style={{ margin: 0 }}>
{inputNode}
</Form.Item>
) : (
children
)}
</td>
);
};
const mergedColumns = coloums.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: DataType) => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
const onChange: PaginationProps["onChange"] = (page) => {
setPage(page);
getContent(selectKey, page);
cancel();
};
const rowSelection = {
selectedRowKeys,
preserveSelectedRowKeys: true,
fixed: true,
onChange: (keys, rowKeys, info) => {
setSelectedRowKeys(keys);
},
};
return ( return (
<div className="contentBox"> <div className="contentBox">
<Space direction="vertical" size="middle" style={{ display: "flex" }}> <Space direction="vertical" size="middle" style={{ display: "flex" }}>
<BTable <Upload />
store={sourceStore} <Space>
scroll={{ x: "max-content" }} <Checkbox.Group
columns={coloums} options={coloums}
dataSource={content} defaultValue={[
deleteCallback={(id) => { "01JM4XMY2N9KN23XSZJSQQM3HY",
sourceStore.deleteItem(id); "01JM4XMY2N9KN23XSZJSWQHHKA",
}} "01JM4XMY2N9KN23XSZJW02PRCF",
actionCloumn={(e)=>{ "01JM4XMY2N9KN23XSZJYCKT6XQ",
"01JM4XMY2N9KN23XSZK0AJ6QEZ",
}} "01JM4XMY2N9KN23XSZK10KM59Y",
editCallback={(record) => { "01JM4XMY2N9KN23XSZK479Y59M",
"01JM4XMY2N9KN23XSZK5F8Z2J8",
"01JM4XMY2N9KN23XSZK6W7Q25F",
"01JM4XMY2N9KN23XSZK7ET1GEB",
"01JM4XMY2N9KN23XSZK9DNRCZZ",
]}
onChange={(v) => {
setSelectKey(v);
getContent(v, 1);
}} }}
/> />
<Button onClick={saveClo}></Button>
</Space>
<Form form={form} component={false}>
<Table
loading={loading}
scroll={{ x: "max-content", scrollToFirstRowOnChange: true }}
components={{
body: { cell: EditableCell },
}}
bordered
rowSelection={rowSelection}
dataSource={content}
columns={[...mergedColumns, actionCloumn]}
rowClassName="editable-row"
pagination={{
onChange: onChange,
total: sourceStore.total,
current: page,
pageSize: 20,
showSizeChanger: false,
}}
/>
</Form>
</Space> </Space>
</div> </div>
); );

View File

@ -1,7 +1,6 @@
import { FormType } from "@/components/form/interface"; import { FormType } from "@/components/form/interface";
import { UserDataType } from "@/model/userModel"; import { UserDataType } from "@/model/userModel";
import { ColumnsType } from "antd/lib/table"; import { ColumnsType } from "antd/lib/table";
import dayjs from "dayjs";
export const defaultConfig = [ export const defaultConfig = [
{ {

View File

@ -0,0 +1,26 @@
import Config from "@/util/config";
import { Button, message, Upload, UploadProps } from "antd";
const Uploads = () => {
const props: UploadProps = {
name: "file",
action: `${Config.baseUrl}/public/fts/uploadthree`,
headers: {
authorization: "authorization-text",
},
onChange(info: any) {
if (info.file.status === "done" && info.file.response.code !== 200) {
message.error(`${info.file.response.msg}`);
} else if (info.file.status === "error") {
message.error(`${info.file.name} file upload failed.`);
}
},
};
return (
<Upload {...props}>
<Button></Button>
</Upload>
);
};
export default Uploads;

View File

@ -31,6 +31,9 @@ const User = (props: Store) => {
scroll={{ x: "max-content" }} scroll={{ x: "max-content" }}
columns={columns} columns={columns}
dataSource={usrStore.list} dataSource={usrStore.list}
deleteCallback={(record) => {
usrStore.deleteItem(record);
}}
editCallback={(record) => { editCallback={(record) => {
setIsModalOpen(true); setIsModalOpen(true);
formRef.current?.setFieldsValue(record); formRef.current?.setFieldsValue(record);

View File

@ -1,5 +1,5 @@
class UserConfig { class UserConfig {
static LOGINURI: string = "/anth/login/pc" static LOGINURI: string = "/anth/login"
static ADD: string = "/user"; static ADD: string = "/user";
static EDIT: string = "/user"; static EDIT: string = "/user";
static LIST: string = "/user/list"; static LIST: string = "/user/list";

View File

@ -6,18 +6,18 @@ import { base } from "@/service/base";
import { message } from "antd"; import { message } from "antd";
class SourceStore extends BaseStore<UserDataType> { class SourceStore extends BaseStore<UserDataType> {
headlist =[] headlist = []
contentList =[] contentList = []
total: any = 0;
constructor() { constructor() {
super(SourceConfig) super(SourceConfig)
makeObservable(this, { makeObservable(this, {
getHead:action, getHead: action,
headlist: observable,
headlist:observable, contentList: observable
contentList:observable
}) })
} }
async getHead(){ async getHead() {
let res = await base.get(SourceConfig.Headers, {}) let res = await base.get(SourceConfig.Headers, {})
if (res.code !== 200) { if (res.code !== 200) {
message.error(res.msg) message.error(res.msg)
@ -27,14 +27,35 @@ class SourceStore extends BaseStore<UserDataType> {
return res.data.record; return res.data.record;
} }
async geContent(){ async modefyData(obj, idcard) {
let res = await base.get(SourceConfig.Content, {}) let list: any = []
if (res.code !== 0) { for (let value in obj) {
if (obj[value]) {
list.push({
key: value,
val: obj[value]
})
}
}
let res = await base.put(SourceConfig.EDIT, {
list:list,
id_card:idcard,
})
if (res.code !== 200) {
message.error(res.msg) message.error(res.msg)
return false return false
} }
this.contentList = res.data; return true
return res.data; }
async geContent(list, page, size) {
let res = await base.post(SourceConfig.Content, { identity: list, offset: page ?? 1, size: size ?? 10 })
if (res.code !== 200) {
message.error(res.msg)
return false
}
this.total = res.data.count;
return res.data.record;
} }
} }
const sourceStore = new SourceStore(); const sourceStore = new SourceStore();

View File

@ -44,10 +44,11 @@ class UserStore extends BaseStore<UserDataType> {
} }
try { try {
let data = await base.post(UserConfig.LOGINURI, param) let data = await base.post(UserConfig.LOGINURI, param)
if (data.code !== 0) { if (data.code !== 200) {
message.error(data.msg) message.error(data.msg)
return false return false
} }
console.log(data.data.token)
window.localStorage.setItem("token", data.data.token ?? ""); window.localStorage.setItem("token", data.data.token ?? "");
return true return true
} catch (error) { } catch (error) {

View File

@ -1,4 +1,4 @@
class Config { class Config {
static baseUrl = "/"; static baseUrl = "http://127.0.0.1:12214/v1";
} }
export default Config; export default Config;