Xiaopeng Zhang hace 4 meses
padre
commit
5ae44bf660
Se han modificado 11 ficheros con 1373 adiciones y 53 borrados
  1. 4 0
      package.json
  2. 699 0
      src/AI.js
  3. 158 0
      src/AIService.js
  4. 52 0
      src/Chart.js
  5. 64 0
      src/Chart2.js
  6. 23 0
      src/Home2.js
  7. 0 50
      src/Test.js
  8. 17 0
      src/Title.js
  9. 1 1
      src/Tree.css
  10. 350 0
      src/Tree2.css
  11. 5 2
      src/index.js

+ 4 - 0
package.json

@@ -3,11 +3,13 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@ant-design/plots": "^2.6.4",
     "@emotion/react": "11.14.0",
     "@emotion/styled": "11.14.1",
     "@mui/icons-material": "5.18.0",
     "@mui/lab": "5.0.0-alpha.75",
     "@mui/material": "5.5.3",
+    "@mui/x-charts": "^6.0.0-alpha.15",
     "@mui/x-data-grid": "6.20.4",
     "@testing-library/dom": "10.4.0",
     "@testing-library/jest-dom": "6.6.3",
@@ -15,9 +17,11 @@
     "@testing-library/user-event": "13.5.0",
     "react": "17.0.2",
     "react-dom": "17.0.2",
+    "react-google-charts": "^5.2.1",
     "react-router": "6.30.1",
     "react-router-dom": "6.30.1",
     "react-scripts": "5.0.1",
+    "recharts": "^2.1.9",
     "rsuite": "^5.83.2",
     "serve": "^14.2.4",
     "source-map-explorer": "^2.5.3",

+ 699 - 0
src/AI.js

@@ -0,0 +1,699 @@
+import * as React from 'react';
+
+import AIService from './AIService';
+import Service from './Service';
+import {useNavigate, useLocation} from "react-router";
+//registerAllModules();
+import { DataGrid, GridActionsCellItem, zhCN as zh_CN} from '@mui/x-data-grid';
+import Box from '@mui/material/Box';
+import MuiAppBar from '@mui/material/AppBar';
+import { styled, useTheme } from '@mui/material/styles';
+import Toolbar from '@mui/material/Toolbar';
+import Typography from '@mui/material/Typography';
+import CssBaseline from '@mui/material/CssBaseline';
+import AccountCircle from '@mui/icons-material/AccountCircle';
+import Fab from '@mui/material/Fab';
+import Tooltip from '@mui/material/Tooltip';
+import DeleteIcon from '@mui/icons-material/Delete';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import Drawer from '@mui/material/Drawer';
+import Divider from '@mui/material/Divider';
+import IconButton from '@mui/material/IconButton';
+import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
+import ChevronRightIcon from '@mui/icons-material/ChevronRight';
+import MenuIcon from '@mui/icons-material/Menu';
+import AddIcon from '@mui/icons-material/Add';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogActions from '@mui/material/DialogActions';
+import CircularProgress from '@mui/material/CircularProgress';
+import Backdrop from '@mui/material/Backdrop';
+import List from '@mui/material/List';
+import ListItem from '@mui/material/ListItem';
+import ListItemButton from '@mui/material/ListItemButton';
+import ListItemIcon from '@mui/material/ListItemIcon';
+import ListItemText from '@mui/material/ListItemText';
+import HomeIcon from '@mui/icons-material/Home';
+import TipsAndUpdatesIcon from '@mui/icons-material/TipsAndUpdates';
+import Stack from '@mui/material/Stack';
+import Title from './Title';
+import {Tree as Tree_2} from 'rsuite';
+import { CustomProvider } from 'rsuite';
+import './Tree.css';
+import zhCN from 'rsuite/locales/zh_CN';
+import FormControl from '@mui/material/FormControl';
+import InputLabel from '@mui/material/InputLabel';
+
+import Grid from '@mui/material/Grid';
+import Container from '@mui/material/Container';
+import Paper from '@mui/material/Paper';
+import LinearProgress from '@mui/material/LinearProgress';
+import Chart from './Chart';
+import Chart2 from './Chart2';
+import Select from '@mui/material/Select';
+import MenuItem from '@mui/material/MenuItem';
+
+export default function AI() {
+  const drawerWidth = 240;
+  const theme = useTheme();
+
+  
+
+
+  function RenderDate(props) {
+    const { hasFocus, value } = props;
+    const buttonElement = React.useRef(null);
+    const rippleRef = React.useRef(null);
+  
+    React.useLayoutEffect(() => {
+      if (hasFocus) {
+        const input = buttonElement.current.querySelector('input');
+        input?.focus();
+      } else if (rippleRef.current) {
+        // Only available in @mui/material v5.4.1 or later
+        rippleRef.current.stop({});
+      }
+    }, [hasFocus]);
+  
+    return (
+      <strong>
+        
+        <Button
+          ref={buttonElement}
+          touchRippleRef={rippleRef}
+          variant="contained"
+          size="small"
+          style={{ marginLeft: 4 }}
+          
+          
+          onClick={(event) => {
+            console.log(value);
+            event.stopPropagation();
+            handleZujia();
+            AIService.qingdan(value).then(x=>{
+              setQddetail(x);
+              setZjloading(true);
+              AIService.zujia(x[0]['清单编码']).then(y=>{
+                setZjdetail(y);
+                setZjloading(false);
+               });
+             });
+            
+          }}
+        >
+          组价AI
+        </Button>
+      </strong>
+    );
+  }
+
+  function RenderDe(props) {
+    const { hasFocus, value } = props;
+    const buttonElement = React.useRef(null);
+    const rippleRef = React.useRef(null);
+  
+    
+  
+    return (
+      
+        <Box>
+
+          {value.split('@').map(x=><Button
+          variant="contained"
+          size="small"
+          style={{ marginLeft: 4 }}
+          >{x}</Button>)}
+      
+       </Box>
+     
+    );
+  }
+
+  function RenderDemc(props) {
+    const { hasFocus, value } = props;
+    const buttonElement = React.useRef(null);
+    const rippleRef = React.useRef(null);
+  
+    
+  
+    return (
+      
+        <Box>
+
+          {value.split('@@').map(x=><Button
+          variant="contained"
+          size="small"
+          style={{ marginLeft: 4, marginTop: 2 }}
+          >{x}</Button>)}
+      
+       </Box>
+     
+    );
+  }
+
+
+  
+ 
+  const columns = React.useMemo(
+    () => [
+      { field: '清单编码', headerName: '清单编码', width: 250 },
+      { field: '名称', headerName: '名称', width: 400 },
+      { field: '数量', headerName: '数量', width: 200 },
+      { field: '操作', headerName: '操作', width: 100 , sortable: false, renderCell: RenderDate},
+ 
+      
+    ],
+    
+  );
+
+  const qdcolumns = React.useMemo(
+    () => [
+      { field: '清单编码', headerName: '清单编码',  },
+      { field: '项目名称', headerName: '项目名称',  },
+      { field: '项目特征', headerName: '项目特征', width : 150 },
+      { field: '计量单位', headerName: '计量单位',  },
+      { field: '工程量计算规则', headerName: '工程量计算规则', width: 300 },
+      { field: '工作内容', headerName: '工作内容', width: 150 },
+ 
+      
+    ],
+    
+  );
+
+  const zjcolumns = React.useMemo(
+    () => [
+      { field: '单位', headerName: '单位',  },
+      { field: '组价定额', headerName: '组价定额', width : 250 , sortable: false, renderCell: RenderDe},
+      { field: '定额名称', headerName: '定额名称', width : 250, sortable: false, renderCell: RenderDemc },
+      { field: '数量', headerName: '数量',  },
+      { field: '综合单价', headerName: '综合单价',  },
+      
+ 
+      
+    ],
+    
+  );
+  const Style = {
+    position: "absolute",
+    bottom: 128,
+    right: 64,
+  };
+  const VisuallyHiddenInput = styled('input')({
+    display: 'none',
+    accept: ".13jz, .13jt, .jszf"
+    });
+
+    const itemRef = React.useRef(null);
+    const navigate = useNavigate();
+    const [detail, setDetail] = React.useState( [
+      {label: "", value : 0}
+     
+     
+    ]);
+    const [detail2, setDetail2] = React.useState( [
+      ["工程类别", "清单数"],
+      ["", 0],
+     
+    ]);
+    const [expanded, setExpanded] = React.useState([]);
+    const [frequency, setFrequency] = React.useState([]);
+    const [qddetail, setQddetail] = React.useState([]);
+    const [zjdetail, setZjdetail] = React.useState([]);
+    const [open, setOpen] = React.useState(false);
+    const [dopen, setDopen] = React.useState(false);
+    const [bopen, setBopen] = React.useState(false);
+    const [type, setType] = React.useState();
+    const [zjloading, setZjloading] = React.useState(false);
+    const [cjloading, setCjloading] = React.useState(false);
+    const [gploading, setGploading] = React.useState(false);
+    const [zhuanye, setZhuanye] = React.useState(10);
+
+    const [tab, setTab] = React.useState(false);
+    const [qingdanshu, setQingdanshu] = React.useState([]);
+
+
+    React.useEffect(
+      () => {
+         AIService.statistics("").then(x=>{
+          let t = [];
+          for (let i = 1; i < x.length; i++) {
+            t.push({label: x[i][0], value: x[i][1]});
+          }
+          setDetail(t);
+         });
+         AIService.qingdanshu(10).then(x=>{
+          setQingdanshu(x);
+         });
+
+      }, []
+    );
+
+
+    const handleRowClick = (params) => {
+      //setMessage(`Movie "${params.row.title}" clicked`);
+      navigate('/editor/qingdan/'.concat(params.row.id));
+    };
+
+    const AppBar = styled(MuiAppBar, {
+      shouldForwardProp: (prop) => prop !== 'open',
+    })(({ theme }) => ({
+      transition: theme.transitions.create(['margin', 'width'], {
+        easing: theme.transitions.easing.sharp,
+        duration: theme.transitions.duration.leavingScreen,
+      }),
+      variants: [
+        {
+          props: ({ open }) => open,
+          style: {
+            width: `calc(100% - ${drawerWidth}px)`,
+            marginLeft: `${drawerWidth}px`,
+            transition: theme.transitions.create(['margin', 'width'], {
+              easing: theme.transitions.easing.easeOut,
+              duration: theme.transitions.duration.enteringScreen,
+            }),
+          },
+        },
+      ],
+    }));
+
+    const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(
+      ({ theme }) => ({
+        flexGrow: 1,
+        padding: theme.spacing(3),
+        transition: theme.transitions.create('margin', {
+          easing: theme.transitions.easing.sharp,
+          duration: theme.transitions.duration.leavingScreen,
+        }),
+        marginLeft: `-${drawerWidth}px`,
+        variants: [
+          {
+            props: ({ open }) => open,
+            style: {
+              transition: theme.transitions.create('margin', {
+                easing: theme.transitions.easing.easeOut,
+                duration: theme.transitions.duration.enteringScreen,
+              }),
+              marginLeft: 0,
+            },
+          },
+        ],
+      }),
+    );
+
+    const DrawerHeader = styled('div')(({ theme }) => ({
+      display: 'flex',
+      alignItems: 'center',
+      padding: theme.spacing(0, 1),
+      // necessary for content to be below app bar
+      ...theme.mixins.toolbar,
+      justifyContent: 'flex-end',
+    }));
+
+    const handleDrawerClose = () => {
+      setOpen(false);
+    };
+    const handleTongji = () => {
+      setOpen(false);
+      setTab(false);
+    };
+    const handleZujia = () => {
+      setOpen(false);
+      setTab(true);
+    };
+
+
+  const handleDrawerOpen = () => {
+    setOpen(true);
+  };
+
+  const handleClose = () => {
+    setDopen(false);
+  };
+
+
+  const handleDelete = () => {
+    Service.deleteFiles(itemRef.current).then(x=> {
+      Service.generateFiles2().then(x=>{
+        setDetail(x.map(y=>{return {'id': y[0], 'ID': y[0], '名称': y[1], '创建时间': y[2], '操作': y[0]};}));
+      });
+    });
+    setDopen(false);
+  };
+
+  const onSelect = (selectedKeys, info) => {
+    setZjloading(true);
+    if (!selectedKeys.hasOwnProperty("children") || selectedKeys['children'].length == 0) {
+         console.log(selectedKeys['id']);
+         AIService.qingdan(selectedKeys['title']).then(x=>{
+          setQddetail(x);
+          AIService.zujia(x[0]['清单编码']).then(y=>{
+            setZjdetail(y);
+            setZjloading(false);
+           });
+         });
+         
+    
+      }
+  };
+
+  const onExpand = (expandItemValues, node) => {
+    //console.log(expandItemValues);
+    //console.log(node);
+    setExpanded(expandItemValues);
+  };
+
+  const clickLB = (lb) => {
+    console.log(lb);
+    setType(lb);
+    if (lb == null) {
+  
+    } else if (lb == '建筑与装饰工程') {
+      //console.log('建筑与装饰工程');
+      setCjloading(true);
+      AIService.statistics("建筑与装饰工程").then(x=>{
+        let t = [];
+          for (let i = 1; i < x.length; i++) {
+            t.push({label: x[i][0], value: x[i][1]});
+          }
+          setDetail2(t);
+          setCjloading(false);
+       
+      });
+   
+    } else if (lb == '仿古建筑') {
+      setCjloading(true);
+      AIService.statistics("仿古建筑").then(x=>{
+        let t = [];
+          for (let i = 1; i < x.length; i++) {
+            t.push({label: x[i][0], value: x[i][1]});
+          }
+          setDetail2(t);
+          setCjloading(false);
+      });
+    
+    } else if (lb == '安装工程') {
+      setCjloading(true);
+      AIService.statistics("安装工程").then(x=>{
+        let t = [];
+          for (let i = 1; i < x.length; i++) {
+            t.push({label: x[i][0], value: x[i][1]});
+          }
+          setDetail2(t);
+          setCjloading(false);
+        
+      });
+   
+    } else if (lb == '市政工程') {
+      setCjloading(true);
+      AIService.statistics("市政工程").then(x=>{
+        let t = [];
+          for (let i = 1; i < x.length; i++) {
+            t.push({label: x[i][0], value: x[i][1]});
+          }
+          setDetail2(t);
+          setCjloading(false);
+        
+      });
+    
+    } else if (lb == '园林绿化工程') {
+      setCjloading(true);
+      AIService.statistics("园林绿化工程").then(x=>{
+        let t = [];
+          for (let i = 1; i < x.length; i++) {
+            t.push({label: x[i][0], value: x[i][1]});
+          }
+          setDetail2(t);
+          setCjloading(false);
+        
+      });
+    
+    }
+  };
+
+  const clickLB2 = (type, lb) => {
+    console.log(type);
+    console.log(lb);
+    setGploading(true);
+    AIService.frequency(type, lb).then(x=>{
+      console.log(x);
+      setFrequency(x.map(y=>{y['操作'] = y['清单编码'];return y;}));
+      setGploading(false);
+    });
+  };
+
+  const handleChangeZhuanye = (event) => {
+    //console.log(event.target.value);
+    AIService.qingdanshu(event.target.value).then(x=>{
+        setQingdanshu(x);
+    });
+    setZhuanye(event.target.value);
+};
+
+
+    return (
+      <Box sx={{ display: 'flex' }}>
+       
+        <CssBaseline />
+        <AppBar position="fixed" open={open}>
+        <Toolbar>
+        
+          <IconButton
+            color="inherit"
+            aria-label="open drawer"
+            onClick={handleDrawerOpen}
+            edge="start"
+            sx={[
+              {
+                mr: 2,
+              },
+              open && { display: 'none' },
+            ]}
+          >
+            <MenuIcon />
+          </IconButton>
+          <Typography variant="h6" noWrap component="div">
+          清单AI
+          </Typography>
+          <Box sx={{ flexGrow: 1 }} >
+          <Stack direction="row" spacing={2} sx={{ml: 4}}>
+          <Button
+                key={'统计AI'}
+                onClick={handleTongji}
+                sx={{ my: 2, color: 'white', display: 'block' }}
+              >
+                统计AI
+              </Button>
+            <Button
+                key={'组价AI'}
+                onClick={handleZujia}
+                sx={{ my: 2, color: 'white', display: 'block' }}
+              >
+                组价AI
+              </Button>
+            </Stack>
+         
+          </Box>
+          <Box sx={{ display: { xs: 'none', md: 'flex' } }}>
+           
+            <IconButton
+              size="large"
+              edge="end"
+              aria-label="account of current user"
+              
+              aria-haspopup="true"
+              
+              color="inherit"
+            >
+              <AccountCircle />
+            </IconButton>
+          </Box>
+          </Toolbar>
+        </AppBar>
+        <Drawer
+        sx={{
+          width: drawerWidth,
+          flexShrink: 0,
+          '& .MuiDrawer-paper': {
+            width: drawerWidth,
+            boxSizing: 'border-box',
+          },
+        }}
+        variant="persistent"
+        anchor="left"
+        open={open}
+      >
+         <DrawerHeader>
+          <IconButton onClick={handleDrawerClose}>
+            {theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
+          </IconButton>
+        </DrawerHeader>
+        <Divider />
+        <List>
+          {['工作台', '清单AI'].map((text, index) => (
+            <ListItem key={text} disablePadding>
+              <ListItemButton onClick={(item)=>{
+                if (index == 0) {
+                  navigate('/editor/index.html');
+                }
+              }}>
+                <ListItemIcon>
+                  {index % 2 === 0 ? <HomeIcon /> : <TipsAndUpdatesIcon />}
+                </ListItemIcon>
+                <ListItemText primary={text} />
+              </ListItemButton>
+            </ListItem>
+          ))}
+        </List>
+
+      </Drawer>
+      <Main open={open}>
+      <DrawerHeader />
+
+      <div style={{  width: '100%' }}>
+      { !tab && <Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>
+            <Grid container spacing={3}>
+              {/* Chart */}
+              <Grid item xs={12} md={5} lg={6}>
+                <Paper
+                  sx={{
+                    p: 2,
+                    display: 'flex',
+                    flexDirection: 'column',
+                    height: 400,
+                  }}
+                >
+                  <Title>清单分类</Title>
+                 <Chart  data={detail} click={clickLB}></Chart>
+                </Paper>
+              </Grid>
+              {/* Recent Deposits */}
+              <Grid item xs={12} md={7} lg={6}>
+                <Paper
+                  sx={{
+                    p: 2,
+                    display: 'flex',
+                    flexDirection: 'column',
+                    height: 400,
+                  }}
+                >
+                   <Title>次级清单分类</Title>
+                 { cjloading && <LinearProgress/>}
+                {  !cjloading && <Chart2 type={type} click={clickLB2} data={detail2}></Chart2> }
+                </Paper>
+              </Grid>
+              {/* Recent Orders */}
+              <Grid item xs={12}>
+                <Paper sx={{ p: 2, display: 'flex', flexDirection: 'column' }}>
+                <Title>高频清单</Title>
+                <DataGrid autoHeight
+                 rows={frequency} columns={columns}
+                 disableColumnMenu={true} loading={gploading}
+                 localeText={zh_CN.components.MuiDataGrid.defaultProps.localeText}
+                />
+                </Paper>
+              </Grid>
+            </Grid>
+           
+          </Container>}
+
+          { tab && <Container maxWidth="xl"  sx={{ mt: 4, mb: 4 }}>
+          <Grid container spacing={3}>
+              {/* Chart */}
+              <Grid item  xs={12} md={2} lg={3}>
+                <Paper
+                  sx={{
+                    
+                    p: 2,
+                    display: 'flex',
+                    flexDirection: 'column',
+                    
+                  }}
+                >
+                  <Title>清单树</Title>
+                  <FormControl  size="small">
+                     <InputLabel id="demo-multiple-name-label">专业</InputLabel>
+                     <Select
+                       labelId="demo-multiple-name-label"
+                       id="demo-multiple-name"
+                       value={zhuanye}
+                       onChange={handleChangeZhuanye}
+                       label="专业" 
+                     >
+                     <MenuItem value={10}>建筑与装饰</MenuItem>
+                     <MenuItem value={20}>安装</MenuItem>
+                     <MenuItem value={30}>市政</MenuItem>
+                     <MenuItem value={40}>园林绿化</MenuItem>
+                     
+                     </Select>
+                   </FormControl> 
+                  <CustomProvider locale={zhCN}>
+               <Tree_2 searchable height={460}
+                     onSelect={onSelect}
+                     onExpand={onExpand}
+                     expandItemValues={expanded}
+                     data={qingdanshu}/>
+                     </CustomProvider>
+                </Paper>
+              </Grid>
+              {/* Recent Deposits */}
+              <Grid item xs={12} md={10} lg={9}>
+                <Paper
+                  sx={{
+                    p: 2,
+                    display: 'flex',
+                    flexDirection: 'column',
+                   
+                  }}
+                >
+                 <DataGrid autoHeight hideFooter={true}
+                 getRowHeight={() => 'auto'}
+                 rows={qddetail} columns={qdcolumns}
+                 disableColumnMenu={true}
+                 localeText={zh_CN.components.MuiDataGrid.defaultProps.localeText}
+                />
+                <Title>历史组价</Title>
+                <DataGrid autoHeight hideFooter={true}
+                 getRowHeight={() => 'auto'}
+                 rows={zjdetail} columns={zjcolumns}
+                 disableColumnMenu={true}
+                 loading={zjloading}
+                 localeText={zh_CN.components.MuiDataGrid.defaultProps.localeText}
+                />
+                </Paper>
+              </Grid>
+             
+             
+            </Grid>
+            </Container>}
+
+       
+    </div>
+   
+    </Main>
+    <Dialog
+        open={dopen}
+        onClose={handleClose}
+        aria-labelledby="alert-dialog-title"
+        aria-describedby="alert-dialog-description"
+      >
+        <DialogTitle id="alert-dialog-title">
+          {"删除该项目?"}
+        </DialogTitle>
+        
+        <DialogActions>
+          <Button onClick={handleClose}>取消</Button>
+          <Button onClick={handleDelete} autoFocus>
+            确定
+          </Button>
+        </DialogActions>
+      </Dialog>
+      <Backdrop
+        sx={(theme) => ({ color: '#fff', zIndex: theme.zIndex.drawer + 1 })}
+        open={bopen}
+        
+      >
+        <CircularProgress color="inherit" />
+      </Backdrop>
+    </Box>
+    );
+  }

+ 158 - 0
src/AIService.js

@@ -0,0 +1,158 @@
+import {copy, danxiangdinge_index, danxiangdinge_index_djcs, renameDingE, extractFuzhu, match_target, azfy, xsazfy, tjazfy, addChildren} from './utils';
+import { v4 as uuidv4 } from 'uuid';
+class AIService{
+    
+    constructor() {
+        this.footprint = [];
+        this.cache = [];
+        this.memory = [];
+        this.mem_pointer = -1;
+        this.cache_djcs = [];
+        this.memory_djcs = [];
+        this.mem_pointer_djcs = -1;
+        this.qufei = [];
+        this.qufeiEntry = null;
+        this.jiagongcai = [];
+        this.mapper={
+            '99090513' : ['汽车式起重机 50t', 2838.92],
+            '99090509' : ['汽车式起重机 25t', 1174.12],
+            '99090111': ['履带式起重机 提升质量50t', 1755.71],
+            '99090108': ['履带式起重机 提升质量25t', 904.68],
+            '99030306': ['静力压桩机 压力1200kN', 1508.23],
+            '99030124': ['轨道式柴油打桩机 冲击质量2.5t', 1143.91],
+            '99030106' : ['履带式柴油打桩机 冲击质量7t', 2545.82],
+            '10450518' : ['纯铝箔 140×140', 0.35],
+            '10450508' : ['纯银箔 93.3×93.3', 4.5],
+            '04050101' : ['道碴 40~80mm', 47],
+            '04034103' : ['石屑(米砂)', 40]
+        };
+        
+    }
+    
+    ip() {
+        //return "/api"//return "http://127.0.0.1:8000"
+        return "http://127.0.0.1:1313/api"
+    }
+
+    clearCache() {//set qufei will clear cache
+        this.cache = [];
+        this.footprint= [];
+        this.memory = [];
+        this.mem_pointer = -1;
+        this.cache_djcs = [];
+        this.memory_djcs = [];
+        this.mem_pointer_djcs = -1;
+    }
+
+   
+
+    async statistics(query) {
+        const response = await fetch(this.ip().concat( "/statistics/"), {
+            method : "POST",
+            headers: {
+                "Content-type": "application/json"
+            },
+            body: JSON.stringify(
+                {
+                    "name": query,
+                    
+                }
+            )
+        });
+        if (!response.ok) {
+            //const error = await response.json();
+            console.error('error');
+        } else {
+            const data = await response.json();
+            return data;
+        }
+    }
+
+    async qingdanshu(zhuanye) {
+        const response = await fetch(this.ip().concat( "/qingdanshu/").concat(zhuanye.toString()), {
+            method : "GET",
+           
+        });
+        if (!response.ok) {
+            //const error = await response.json();
+            console.error('error');
+        } else {
+            const data = await response.json();
+            return data;
+        }
+    }
+    async qingdan(bm) {
+        const response = await fetch(this.ip().concat( "/qingdan/").concat(bm), {
+            method : "GET",
+           
+        });
+        if (!response.ok) {
+            //const error = await response.json();
+            console.error('error');
+        } else {
+            const data = await response.json();
+            return data;
+        }
+    }
+
+    async frequency(type, query) {
+        const response = await fetch(this.ip().concat( "/frequency/"), {
+            method : "POST",
+            headers: {
+                "Content-type": "application/json"
+            },
+            body: JSON.stringify(
+                {
+                    "name": type,
+                    "id" : query
+                    
+                }
+            )
+        });
+        if (!response.ok) {
+            //const error = await response.json();
+            console.error('error');
+        } else {
+            const data = await response.json();
+            return data;
+        }
+    }
+
+    async zujia(query) {
+        const response = await fetch(this.ip().concat( "/zujia/"), {
+            method : "POST",
+            headers: {
+                "Content-type": "application/json"
+            },
+            body: JSON.stringify(
+                {
+                    "name": query,
+                    
+                    
+                }
+            )
+        });
+        if (!response.ok) {
+            //const error = await response.json();
+            console.error('error');
+        } else {
+            const data = await response.json();
+            return data;
+        }
+    }
+
+    
+
+
+
+
+    
+}
+
+
+
+
+
+
+
+export default new AIService();

+ 52 - 0
src/Chart.js

@@ -0,0 +1,52 @@
+import * as React from 'react';
+import { useTheme } from '@mui/material/styles';
+import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer';
+import Title from './Title';
+import { PiePlot } from '@mui/x-charts/PieChart';
+
+import AIService from './AIService';
+import { ChartsTooltip } from '@mui/x-charts/ChartsTooltip';
+
+import { ChartsLegend } from '@mui/x-charts/ChartsLegend';
+
+
+import Box from '@mui/material/Box';
+
+
+
+
+
+
+
+export default function Chart({data, click}) {
+
+
+
+  
+
+  const onClick = (event, identifier, item) => {
+    //setMessage(`Movie "${params.row.title}" clicked`);
+    
+    click(item['label']);
+  };
+
+ 
+
+  return (
+    
+    <ResponsiveChartContainer series={[
+      {
+        type: 'pie',
+        data: data,
+        highlightScope: { faded: 'global', highlighted: 'item' },
+          faded: { innerRadius: 30, additionalRadius: -30, color: 'gray' },
+      },
+    
+    ]}>
+      <PiePlot onClick={onClick}/>
+      <ChartsLegend direction="row" />
+      <ChartsTooltip trigger={'item'}/>
+    </ResponsiveChartContainer>
+    
+  );
+}

+ 64 - 0
src/Chart2.js

@@ -0,0 +1,64 @@
+import * as React from 'react';
+import { useTheme } from '@mui/material/styles';
+import { ResponsiveContainer } from 'recharts';
+import Title from './Title';
+import { Chart as GChart } from "react-google-charts";
+import Box from '@mui/material/Box';
+import Skeleton from '@mui/material/Skeleton';
+
+import CircularProgress from '@mui/material/CircularProgress';
+
+import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer';
+import { createTheme, ThemeProvider } from '@mui/material/styles';
+
+
+import { PiePlot } from '@mui/x-charts/PieChart';
+
+import { ChartsTooltip } from '@mui/x-charts/ChartsTooltip';
+
+import { ChartsLegend } from '@mui/x-charts/ChartsLegend';
+
+
+
+
+
+
+
+
+export default function Chart2({type, click, data}) {
+
+
+
+  const onClick = (event, identifier, item) => {
+    //setMessage(`Movie "${params.row.title}" clicked`);
+    
+    click(type, item['label']);
+  };
+
+  let theme = createTheme();
+  theme.typography.subtitle1 = {
+    fontSize: '0.5rem'
+    };
+
+  return (
+    
+    <ThemeProvider theme={theme}>
+      <ResponsiveChartContainer  series={[
+        {
+          type: 'pie',
+          data: data,
+          highlightScope: { faded: 'global', highlighted: 'item' },
+            faded: { innerRadius: 30, additionalRadius: -30, color: 'gray' },
+        },
+      
+      ]}>
+      
+      <PiePlot onClick={onClick}/>
+      <ChartsLegend direction="column" 
+                 position= {{ vertical: 'top', horizontal: 'left' }}
+                    />
+      <ChartsTooltip trigger={'item'}/>
+      </ResponsiveChartContainer>
+      </ThemeProvider>
+  );
+}

+ 23 - 0
src/Home2.js

@@ -27,6 +27,13 @@ import DialogTitle from '@mui/material/DialogTitle';
 import DialogActions from '@mui/material/DialogActions';
 import CircularProgress from '@mui/material/CircularProgress';
 import Backdrop from '@mui/material/Backdrop';
+import List from '@mui/material/List';
+import ListItem from '@mui/material/ListItem';
+import ListItemButton from '@mui/material/ListItemButton';
+import ListItemIcon from '@mui/material/ListItemIcon';
+import ListItemText from '@mui/material/ListItemText';
+import HomeIcon from '@mui/icons-material/Home';
+import TipsAndUpdatesIcon from '@mui/icons-material/TipsAndUpdates';
 
 export default function Home2() {
   const drawerWidth = 240;
@@ -317,6 +324,22 @@ export default function Home2() {
           </IconButton>
         </DrawerHeader>
         <Divider />
+        <List>
+          {['工作台', '清单AI'].map((text, index) => (
+            <ListItem key={text} disablePadding>
+              <ListItemButton onClick={(item)=>{
+                if (index == 1) {
+                  navigate('/editor/ai');
+                }
+              }}>
+                <ListItemIcon>
+                  {index % 2 === 0 ? <HomeIcon /> : <TipsAndUpdatesIcon />}
+                </ListItemIcon>
+                <ListItemText primary={text} />
+              </ListItemButton>
+            </ListItem>
+          ))}
+        </List>
 
       </Drawer>
       <Main open={open}>

+ 0 - 50
src/Test.js

@@ -1,50 +0,0 @@
-import * as React from 'react';
-import {Tree} from 'rsuite';
-import 'rsuite/Tree/styles/index.css';
-
-export default function Test() {
-  const MUI_X_PRODUCTS = [
-    {
-      value: 'grid',
-      label: 'Data Grid',
-      children: [
-        { value: 'grid-community', label: '@mui/x-data-grid' },
-        { value: 'grid-pro', label: '@mui/x-data-grid-pro' },
-        { value: 'grid-premium', label: '@mui/x-data-grid-premium' },
-      ],
-    },
-    {
-      value: 'pickers',
-      label: 'Date and Time Pickers',
-      children: [
-        { value: 'pickers-community', label: '@mui/x-date-pickers' },
-        { value: 'pickers-pro', label: '@mui/x-date-pickers-pro' },
-      ],
-    },
-    {
-      value: 'charts',
-      label: 'Charts',
-      children: [
-        { value: 'charts-community', label: '@mui/x-charts' },
-        { value: 'charts-pro', label: '@mui/charts-pro' },
-      ],
-    },
-    {
-      value: 'tree-view',
-      label: 'Tree View',
-      children: [
-        { value: 'tree-view-community', label: '@mui/x-tree-view' },
-        { value: 'tree-view-pro', label: '@mui/x-tree-view-pro' },
-      ],
-    },
-  ];
-   
-    return (
-      <Tree data ={ MUI_X_PRODUCTS} height="20vh">
-
-      </Tree>
-
-
-
-    );
-}

+ 17 - 0
src/Title.js

@@ -0,0 +1,17 @@
+import * as React from 'react';
+import PropTypes from 'prop-types';
+import Typography from '@mui/material/Typography';
+
+function Title(props) {
+  return (
+    <Typography component="h2" variant="h6" color="primary" gutterBottom>
+      {props.children}
+    </Typography>
+  );
+}
+
+Title.propTypes = {
+  children: PropTypes.node,
+};
+
+export default Title;

+ 1 - 1
src/Tree.css

@@ -204,7 +204,7 @@
   color: var(--rs-text-secondary);
 }
 .rs-tree-view {
-  max-height: 360px;
+  max-height: 500px;
   overflow-y: auto;
 }
 .rs-tree.rs-tree-virtualized .rs-tree-view {

+ 350 - 0
src/Tree2.css

@@ -0,0 +1,350 @@
+:root,
+.rs-theme-light {
+  --rs-gray-0: #fff;
+  --rs-gray-50: #f7f7fa;
+  --rs-gray-200: #e5e5ea;
+  --rs-gray-400: #b6b7b8;
+  --rs-gray-500: #939393;
+  --rs-gray-600: #717273;
+  --rs-gray-700: #575757;
+  --rs-gray-800: #343434;
+  --rs-primary-50: #f2faff;
+  --rs-primary-100: #cce9ff;
+  --rs-primary-200: #a6d7ff;
+  --rs-primary-500: #3498ff;
+  --rs-primary-700: #1976D2;
+  --rs-primary-900: #004299;
+  --rs-text-link: var(--rs-primary-700);
+  --rs-text-primary: var(--rs-gray-800);
+  --rs-text-secondary: var(--rs-gray-600);
+  --rs-bg-overlay: var(--rs-gray-0);
+  --rs-color-focus-ring: rgb(from var(--rs-primary-500) r g b / 25%);
+  --rs-listbox-option-hover-bg: rgb(from var(--rs-primary-100) r g b / 50%);
+  --rs-listbox-option-hover-text: var(--rs-primary-700);
+  --rs-listbox-option-selected-bg: var(--rs-primary-50);
+  --rs-listbox-option-disabled-text: var(--rs-gray-400);
+  --rs-listbox-option-disabled-selected-text: var(--rs-primary-200);
+  --rs-tree-indent-line-color: rgba(0, 0, 0, 0.1);
+}
+@supports not (color: rgb(from white r g b)) {
+  :root,
+  .rs-theme-light {
+    --rs-color-focus-ring: rgba(52, 152, 255, 0.25);
+    --rs-listbox-option-hover-bg: rgba(204, 233, 255, 0.5);
+  }
+}
+.rs-theme-dark {
+  --rs-gray-0: #fff;
+  --rs-gray-50: #e9ebf0;
+  --rs-gray-200: #a4a9b3;
+  --rs-gray-400: #6a6f76;
+  --rs-gray-500: #5c6066;
+  --rs-gray-600: #3c3f43;
+  --rs-gray-700: #292d33;
+  --rs-gray-800: #1a1d24;
+  --rs-primary-50: #f2fcff;
+  --rs-primary-100: #ccf3ff;
+  --rs-primary-200: #a6e9ff;
+  --rs-primary-500: #34c3ff;
+  --rs-primary-700: #169de0;
+  --rs-primary-900: #006199;
+  --rs-text-link: var(--rs-primary-500);
+  --rs-text-primary: var(--rs-gray-50);
+  --rs-text-secondary: var(--rs-gray-200);
+  --rs-bg-overlay: var(--rs-gray-700);
+  --rs-color-focus-ring: 0 0 0 3px rgb(from var(--rs-gray-500) r g b / 25%);
+  --rs-listbox-option-hover-bg: var(--rs-gray-600);
+  --rs-listbox-option-hover-text: currentColor;
+  --rs-listbox-option-selected-bg: rgb(from var(--rs-primary-900) r g b / 20%);
+  --rs-listbox-option-disabled-text: var(--rs-gray-500);
+  --rs-listbox-option-disabled-selected-text: var(--rs-primary-200);
+  --rs-tree-indent-line-color: rgba(255, 255, 255, 0.1);
+}
+@supports not (color: rgb(from white r g b)) {
+  .rs-theme-dark {
+    --rs-color-focus-ring: 0 0 0 3px rgba(52, 195, 255, 0.25);
+    --rs-listbox-option-selected-bg: rgba(0, 97, 153, 0.2);
+  }
+}
+.rs-theme-high-contrast {
+  --rs-gray-0: #fff;
+  --rs-gray-50: #e9ebf0;
+  --rs-gray-200: #a4a9b3;
+  --rs-gray-400: #6a6f76;
+  --rs-gray-500: #5c6066;
+  --rs-gray-600: #3c3f43;
+  --rs-gray-700: #292d33;
+  --rs-gray-800: #1a1d24;
+  --rs-primary-50: #fffef2;
+  --rs-primary-100: #fffbc2;
+  --rs-primary-200: #fffa91;
+  --rs-primary-500: #ffff00;
+  --rs-primary-700: #d9e000;
+  --rs-primary-900: #8f9900;
+  --rs-text-link: var(--rs-primary-500);
+  --rs-text-primary: var(--rs-gray-50);
+  --rs-text-secondary: var(--rs-gray-200);
+  --rs-bg-overlay: var(--rs-gray-800);
+  --rs-color-focus-ring: var(--rs-gray-0);
+  --rs-listbox-option-hover-bg: transparent;
+  --rs-listbox-option-hover-text: var(--rs-primary-500);
+  --rs-listbox-option-selected-bg: transparent;
+  --rs-listbox-option-disabled-text: var(--rs-gray-500);
+  --rs-listbox-option-disabled-selected-text: var(--rs-primary-200);
+  --rs-tree-indent-line-color: rgba(255, 255, 255, 0.1);
+}
+/* stylelint-disable */
+*[class*='rs-'] {
+  -webkit-box-sizing: border-box;
+          box-sizing: border-box;
+}
+*[class*='rs-']::before,
+*[class*='rs-']::after {
+  -webkit-box-sizing: border-box;
+          box-sizing: border-box;
+}
+.rs-picker-subtle .picker-subtle-toggle {
+  position: relative;
+  z-index: 5;
+  padding-right: 32px;
+  display: inline-block;
+}
+.rs-picker-subtle.rs-picker-disabled .picker-subtle-toggle {
+  cursor: not-allowed;
+}
+.rs-picker-subtle.rs-picker-disabled .picker-subtle-toggle:hover,
+.rs-picker-subtle.rs-picker-disabled .picker-subtle-toggle:focus,
+.rs-picker-subtle.rs-picker-disabled .picker-subtle-toggle:active {
+  background: none;
+}
+.rs-picker-subtle.rs-picker-disabled .picker-subtle-toggle:hover::after,
+.rs-picker-subtle.rs-picker-disabled .picker-subtle-toggle:focus::after,
+.rs-picker-subtle.rs-picker-disabled .picker-subtle-toggle:active::after {
+  display: none;
+}
+.rs-tree-node-toggle {
+  cursor: pointer;
+  width: 24px;
+  padding: 4px;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+      -ms-flex-pack: center;
+          justify-content: center;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+}
+.rs-tree-node-toggle > .rs-tree-node-toggle-icon {
+  display: inline-block;
+  height: 16px;
+  width: 16px;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+.rs-tree-node-toggle > .rs-tree-node-custom-icon,
+.rs-tree-node-toggle > .rs-tree-node-loading-icon {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+  font-size: 14px;
+}
+.rs-tree-node-toggle-placeholder {
+  width: 24px;
+  height: 24px;
+}
+/* rtl:begin:ignore */
+[dir='rtl'] .rs-tree-node-toggle {
+  right: 0;
+  padding-right: inherit;
+}
+/* rtl:end:ignore */
+.rs-tree-indent-line {
+  border-left: 1px solid rgba(0, 0, 0, 0.1);
+  border-left: 1px solid var(--rs-tree-indent-line-color);
+  position: absolute;
+  width: 1px;
+  top: -10px;
+  left: 12px;
+  bottom: -4px;
+}
+.rs-tree {
+  height: 100%;
+  -webkit-box-flex: 1;
+      -ms-flex: 1 1 auto;
+          flex: 1 1 auto;
+}
+.rs-tree-drag-preview {
+  position: absolute;
+  top: 0;
+  color: #343434;
+  color: var(--rs-text-primary);
+  background-color: #fff;
+  background-color: var(--rs-bg-overlay);
+  display: inline-block;
+  margin: 0;
+  padding: 8px 12px;
+  border-radius: 6px;
+  -webkit-box-shadow: 0 1px 8px rgba(0, 0, 0, 0.12);
+          box-shadow: 0 1px 8px rgba(0, 0, 0, 0.12);
+  z-index: -1;
+}
+.rs-tree .rs-search-box {
+  padding: 6px;
+}
+.rs-tree .rs-tree-empty {
+  padding: 6px 12px;
+  color: #717273;
+  color: var(--rs-text-secondary);
+}
+.rs-tree-view {
+  max-height: 500px;
+  overflow-y: auto;
+}
+.rs-tree.rs-tree-virtualized .rs-tree-view {
+  overflow: hidden;
+}
+.rs-tree .rs-highlight-mark {
+  padding: 0;
+}
+.rs-tree-group {
+  padding-left: 18px;
+}
+.rs-tree-node {
+  position: relative;
+  font-size: 0;
+  text-align: left;
+  margin: 0 0 4px 0;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+}
+.rs-tree-node:focus-visible .rs-tree-node-label {
+  outline: 3px solid rgb(from #3498ff r g b / 25%);
+  outline: 3px solid var(--rs-color-focus-ring);
+}
+.rs-theme-high-contrast .rs-tree-node:focus-visible .rs-tree-node-label {
+  outline-offset: 2px;
+}
+.rs-tree-node-label {
+  position: relative;
+  margin: 1px;
+  cursor: pointer;
+  font-size: 14px;
+  line-height: 22px;
+  padding: 6px;
+  border-radius: 6px;
+}
+.rs-tree-node-label:hover,
+.rs-tree-node-label:focus,
+.rs-tree-node-label.rs-tree-node-label-focus {
+  background-color: rgb(from #cce9ff r g b / 50%);
+  background-color: var(--rs-listbox-option-hover-bg);
+  color: #1976D2;
+  color: var(--rs-listbox-option-hover-text);
+}
+.rs-theme-high-contrast .rs-tree-node-label:hover,
+.rs-theme-high-contrast .rs-tree-node-label:focus,
+.rs-theme-high-contrast .rs-tree-node-label.rs-tree-node-label-focus {
+  outline: 3px solid rgb(from #3498ff r g b / 25%);
+  outline: 3px solid var(--rs-color-focus-ring);
+  outline-offset: -3px;
+  outline-width: 2px;
+  color: #1976D2;
+  color: var(--rs-listbox-option-hover-text);
+  text-decoration: underline;
+}
+.rs-theme-high-contrast .rs-theme-high-contrast .rs-tree-node-label:hover,
+.rs-theme-high-contrast .rs-theme-high-contrast .rs-tree-node-label:focus,
+.rs-theme-high-contrast .rs-theme-high-contrast .rs-tree-node-label.rs-tree-node-label-focus {
+  outline-offset: 2px;
+}
+.rs-tree-node-label::after {
+  content: '';
+  position: absolute;
+  width: 0;
+  height: 0;
+  left: -8px;
+  border-left: 6px solid #1976D2;
+  border-left: 6px solid var(--rs-text-link);
+  border-top: 3px solid transparent;
+  border-bottom: 3px solid transparent;
+  display: none;
+}
+.rs-tree-node-active .rs-tree-node-label {
+  font-weight: bold;
+  color: #1976D2;
+  color: var(--rs-text-link);
+  background-color: #f2faff;
+  background-color: var(--rs-listbox-option-selected-bg);
+}
+.rs-tree-node-drag-over {
+  background-color: rgb(from #cce9ff r g b / 50%);
+  background-color: var(--rs-listbox-option-hover-bg);
+}
+.rs-tree-node-dragging {
+  outline: 1px dashed #3498ff;
+  outline: 1px dashed var(--rs-primary-500);
+}
+.rs-tree-node-drag-over-top.rs-tree-node-label {
+  border-top: 2px solid #1976D2 !important;
+  border-top: 2px solid var(--rs-text-link) !important;
+  border-radius: 0;
+}
+.rs-tree-node-drag-over-top.rs-tree-node-label::after {
+  display: block;
+  top: -4px;
+}
+.rs-tree-node-drag-over-bottom.rs-tree-node-label {
+  border-bottom: 2px solid #1976D2 !important;
+  border-bottom: 2px solid var(--rs-text-link) !important;
+  border-radius: 0;
+}
+.rs-tree-node-drag-over-bottom.rs-tree-node-label::after {
+  display: block;
+  bottom: -4px;
+}
+.rs-tree-node-disabled .rs-tree-node-label {
+  background: none;
+  color: #b6b7b8;
+  color: var(--rs-listbox-option-disabled-text);
+  cursor: not-allowed;
+}
+.rs-tree-node-disabled > .rs-tree-node-label {
+  cursor: not-allowed;
+  color: #b6b7b8;
+  color: var(--rs-listbox-option-disabled-text);
+}
+.rs-tree-node-disabled > .rs-tree-node-label,
+.rs-tree-node-disabled > .rs-tree-node-label:hover {
+  background-color: transparent;
+}
+.rs-tree-node-disabled.rs-tree-node-active > .rs-tree-node-label,
+.rs-tree-node-disabled.rs-tree-node-active > .rs-tree-node-label:hover {
+  color: #a6d7ff;
+  color: var(--rs-listbox-option-disabled-selected-text);
+}
+.rs-tree-node:not(.rs-tree-node-disabled):focus > .rs-tree-node-label {
+  background-color: #f2faff;
+  background-color: var(--rs-listbox-option-selected-bg);
+}
+.rs-tree-node:not(.rs-tree-node-disabled) > .rs-tree-node-label:focus {
+  background-color: #f2faff;
+  background-color: var(--rs-listbox-option-selected-bg);
+}
+.rs-tree-node-children > .rs-tree-group {
+  position: relative;
+  display: none;
+}
+.rs-tree-node-expanded.rs-tree-node-children > .rs-tree-group {
+  display: block;
+}

+ 5 - 2
src/index.js

@@ -1,11 +1,11 @@
 import React, {Suspense} from 'react';
 import ReactDOM from 'react-dom';
 import './index.css';
-import Test from './Test';
 import reportWebVitals from './reportWebVitals';
 import {createBrowserRouter, RouterProvider, Route, Link} from "react-router-dom";
 const Home2 = React.lazy(() => import('./Home2'));
 const App2 = React.lazy(() => import('./App2'));
+const AI = React.lazy(() => import('./AI'));
 
 const router = createBrowserRouter([
    {path: "/editor/qingdan/:id",
@@ -13,7 +13,10 @@ const router = createBrowserRouter([
    },
    {path: "/editor/index.html",
    element: (<Suspense fallback={<div></div>}><Home2></Home2></Suspense>)
- },
+   },
+   {path: "/editor/ai",
+   element: (<Suspense fallback={<div></div>}><AI></AI></Suspense>)
+   },
 
 ]);
 const root = ReactDOM.render(<RouterProvider router={router}/>, document.getElementById('root'));