main
Kurt92 3 years ago
parent 6c6a561992
commit c57e0dc7dc

@ -0,0 +1,17 @@
//----------------------------------------------------------------------------
// Board : 유저관리
//----------------------
import axios from 'utils/axios';
import { GET_USER_BOARD_LIST, SAVE_USER_BOARD, DELETE_USER_BOARD } from 'commons/ApiUrl';
import { setRowId } from './common';
// eslint-disable-next-line import/prefer-default-export
export async function getBoardList(params) {
const res = await axios.get(GET_USER_BOARD_LIST, { params });
if (res.success) {
res.data = res.data.map((d, idx) => ({ ...d, rowId: setRowId(params, idx) }));
return res;
}
return res;
}

@ -24,3 +24,8 @@ export const SAVE_PARKING_SIMSA_TARGET = '/api/v1/ctgy/parking/target';
export const GET_RESIDENT_DATA_LIST = '/api/v1/ctgy/resident/data';
export const SAVE_RESIDENT_DATA = '/api/v1/ctgy/resident/data';
export const GET_RESIDENT = '/api/v1/ctgy/resident/';
// 사용자 관리
export const GET_USER_BOARD_LIST = '/api/v1/ctgy/pboard/';
export const SAVE_USER_BOARD = '/api/v1/ctgy/file/pboard';
export const DELETE_USER_BOARD = '/api/v1/ctgy/file/pboard/';

@ -5,6 +5,7 @@ import MainLayout from 'layout/MainLayout';
import Loadable from 'ui-component/Loadable';
import AuthGuard from 'utils/route-guard/AuthGuard';
import ResidentDataReview from '../views/biz/resident/Review';
import Juror from '../views/biz/user/Juror';
// sample page routing
const SamplePage = Loadable(lazy(() => import('views/sample-page')));
@ -17,6 +18,7 @@ const Board = Loadable(lazy(() => import('views/biz/board/Board')));
const ParkingReview = Loadable(lazy(() => import('views/biz/parking/Review')));
const ParkingDetails = Loadable(lazy(() => import('views/biz/parking/ModalDetails')));
const ParkingRegister = Loadable(lazy(() => import('views/biz/parking/Regist')));
const UserManager = Loadable(lazy(() => import('views/biz/user/UserManager')));
// component
const ModalForm = Loadable(lazy(() => import('views/form/Modal')));
@ -83,7 +85,11 @@ const MainRoutes = {
/* 사용자 */
{
path: '/user/management',
element: <Board />
element: <UserManager />
},
{
path: '/user/juror',
element: <Juror />
},
/* SMS */
{
@ -95,11 +101,6 @@ const MainRoutes = {
path: '/board',
element: <Board />
},
{
path: '/user/juror',
element: <Board />
},
{
path: '/parking/details',
element: <ParkingDetails />

@ -0,0 +1,152 @@
import { useEffect, useState } from 'react';
// material-ui
import { Button, Divider, Grid, InputAdornment, MenuItem, OutlinedInput, Select } from '@mui/material';
// assets
import { IconSearch } from '@tabler/icons';
// berry ui
import MainCard from 'ui-component/cards/MainCard';
// project imports
import MuiDataGrid from 'views/form/MuiDataGrid';
import { getBoardList } from '../../../apis/user';
import CmmModal from '../../form/Modal/CmmModal';
import UserManagerForm from './UserManagerForm';
import { deletePublicBoard, savePublicBoard } from '../../../apis/public';
const UserManager = () => {
const [category, setCategory] = useState('ciTitle');
const [searchTxt, setSearchTxt] = useState('');
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 10,
rows: []
// loading: false
});
// state
const [open, setOpen] = useState(false);
const [selectedRow, setSelectedRow] = useState({});
const [create, setCreate] = useState(false);
const [title, setTitle] = useState();
const columns = [
{ headerName: '게시판코드', field: 'ciCode' },
{ headerName: '글번호', field: 'ciContentno' },
{ headerName: '제목', field: 'ciTitle', editable: true },
{ headerName: '사용자ID', field: 'ciId' },
{ headerName: '사용자 비번', field: 'ciPwd' }
];
const handleSearch = async (event) => {
if (event.type === 'keydown' && event.key === 'Enter') {
const newString = event?.target.value;
setSearchTxt(newString);
}
};
const search = () => {
const params = {
page: rowsState.page,
size: rowsState.pageSize
};
getBoardList(params).then((response) => {
if (response && response.data) {
setTotalCount(response.count);
setRowsState((prevState) => ({ ...prevState, rows: response.data }));
}
});
};
useEffect(() => {
search();
}, [rowsState.page, rowsState.pageSize, category, searchTxt]); // rowsState.page, rowsState.pageSize, rowsState.rows]);
// model
const submitPublicBoard = (type, payload) => {
switch (type) {
case 'SAVE':
savePublicBoard(payload).then(() => {
search();
setOpen(false);
}); // .then((res) => {
break;
case 'DELETE':
deletePublicBoard(payload).then(() => {
search();
setOpen(false);
}); // .then((res) => {
break;
default:
}
};
//
const handleCreate = () => {
setSelectedRow({});
setTitle('공지사항 등록');
setCreate(true);
setOpen(true);
};
return (
<MainCard>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} lg={6}>
<Grid container spacing={1}>
<Grid item>
<Select id="category" name="reviewYear" defaultValue="ciTitle" onChange={(e) => setCategory(e.target.value)}>
<MenuItem key="1" value="ciTitle">
이름
</MenuItem>
<MenuItem key="2" value="ciName">
아이디
</MenuItem>
</Select>
</Grid>
<Grid item>
<OutlinedInput
placeholder="Search"
onKeyDown={handleSearch}
size="small"
autoFocus
endAdornment={
<InputAdornment position="end">
<IconSearch stroke={1.5} size="1rem" />
</InputAdornment>
}
/>
{/* <IconSearch stroke={1.5} size="1rem" /> */}
</Grid>
<Grid item>
<Button variant="contained" size="small" onClick={handleCreate}>
평가 등록
</Button>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<MuiDataGrid
columns={columns}
rowsState={rowsState}
totalCount={totalCount}
setRowsState={setRowsState}
// handleCellClick={handleOnCellClick}
/>
<CmmModal isBackdrop title={title} open={open} setOpen={setOpen}>
<UserManagerForm create={create} setOpen={setOpen} handleModalSave={submitPublicBoard} {...selectedRow} />
</CmmModal>
</MainCard>
);
};
export default UserManager;

@ -0,0 +1,226 @@
import { useMemo, useRef, useState } from 'react';
import { useAlert } from 'react-alert';
// material-ui
import { Button, FormControl, Grid, MenuItem, Select, TextField } from '@mui/material';
// assets
// berry ui
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
// project imports
import InputLabel from 'ui-component/extended/Form/InputLabel';
import { Delete, List, Save } from '@mui/icons-material';
import { fileDownload } from '../../../apis/common';
import FileForm from 'views/form/FileForm';
const PublicBoardForm = (props) => {
// eslint-disable-next-line react/prop-types
const { create, inCode, inDept, inTitle, inHit, inName, inNalja, inFilename, inContents, setOpen, handleModalSave } = props;
const alert = useAlert();
const quillRef = useRef();
const [dept, setDept] = useState(inDept || '주정차위반');
const [subject, setSubject] = useState(inTitle || '');
const [contents, setContents] = useState(inContents || '');
const [filesInfo, setFilesInfo] = useState();
const [selectedFile, setSelectedFile] = useState(inFilename || ''); //
// const [fileData, setFileData] = useState();
const onList = () => {
setOpen(false);
};
const onSave = () => {
// TODO : validation check
const formData = new FormData();
formData.append('inCode', inCode ?? '');
formData.append('inTitle', subject);
formData.append('inDept', dept);
formData.append('inContents', contents);
formData.append('inFilename', selectedFile ?? '');
if (filesInfo && filesInfo.length > 0) {
// eslint-disable-next-line no-plusplus
for (let i = 0; i < filesInfo.length; i++) formData.append('files', filesInfo[i]);
}
handleModalSave('SAVE', formData);
};
const onDelete = () => {
handleModalSave('DELETE', inCode);
};
const imageHandler = () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
if (input.files) {
const file = input.files[0];
const formData = new FormData();
formData.append('image', file);
console.log(formData);
// input
input.onchange = async () => {
const file = input.files;
if (file !== null) {
formData.append('image', file[0]);
try {
//
// const res = await axios.get('/');
//
const url = '/Users/minuk/Pictures/test.png';
//
const range = quillRef.current?.getEditor().getSelection()?.index;
if (range !== null && range !== undefined) {
const quill = quillRef.current?.getEditor();
quill?.setSelection(range, 1);
quill?.clipboard.dangerouslyPasteHTML(range, `<img src=${url} alt="이미지 태그가 삽입됩니다." />`);
}
} catch (error) {
console.log(error);
}
}
};
}
};
};
const modules = useMemo(
() => ({
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[{ size: ['small', false, 'large', 'huge'] }, { color: [] }],
[{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }, { align: [] }]
// ['image', 'video']
],
handlers: {
image: imageHandler
}
}
}),
[]
);
const onChangeFile = (file) => {
setSelectedFile(file.name);
setFilesInfo([file]);
};
// const onChangeFile = (e) => {
// setSelectedFile(e.target.files[0].name);
// setFilesInfo(e.target.files);
// };
const handleFileDownload = () => {
if (!inFilename) {
alert.show('등록된 파일이 없습니다.');
return;
}
fileDownload(inCode, inFilename, alert).then(() => {});
};
return (
<>
<Grid mt={2}>
<Grid container spacing={1} item xs={12} mb={1}>
<Grid item xs={12} sm={6}>
<TextField size="small" required label="제목" value={subject} onChange={(e) => setSubject(e.target.value)} fullWidth />
</Grid>
<Grid item xs={12} sm={3}>
<FormControl fullWidth>
<InputLabel required>진술유형</InputLabel>
<Select
size="small"
disabled={!create}
label="업무구분"
required
value={dept}
onChange={(e) => setDept(e.target.value)}
fullWidth
>
<MenuItem value="주정차위반">주정차위반</MenuItem>
<MenuItem value="장애인위반">장애인위반</MenuItem>
<MenuItem value="기타">기타</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12} sm={3}>
<TextField size="small" disabled label="작성자" value={inName} fullWidth />
</Grid>
</Grid>
<Grid container spacing={1} item xs={12} mb={1}>
<Grid item xs={12} sm={3}>
<TextField size="small" disabled label="등록일" value={inNalja} fullWidth />
</Grid>
<Grid item xs={12} sm={3}>
<TextField size="small" disabled label="번호" value={inCode} fullWidth />
</Grid>
<Grid item xs={12} sm={3}>
<TextField size="small" disabled label="조회수" value={inHit} fullWidth />
</Grid>
</Grid>
<Grid container spacing={1} item xs={12} mb={1}>
<Grid item xs={12}>
<ReactQuill
ref={(element) => {
if (element !== null) {
quillRef.current = element;
}
}}
value={contents}
onChange={setContents}
modules={modules}
theme="snow"
placeholder="내용을 입력해주세요."
/>
</Grid>
</Grid>
<Grid container spacing={1} item xs={12} mb={1}>
<Grid item sm={6}>
<FileForm
isDownload={inFilename}
labelName="첨부파일"
savedFilename={inFilename}
selectedFile={selectedFile}
handleChangeFile={onChangeFile}
handleFileDownload={handleFileDownload}
alert={alert}
/>
</Grid>
</Grid>
</Grid>
<Grid item container spacing={0.5} xs={12}>
<Grid item>
<Button variant="contained" size="small" startIcon={<List />} onClick={onList}>
목록
</Button>
</Grid>
<Grid item>
<Button variant="contained" size="small" startIcon={<Save />} onClick={onSave}>
저장
</Button>
</Grid>
{!create && (
<Grid item>
<Button variant="contained" size="small" startIcon={<Delete />} onClick={onDelete}>
삭제
</Button>
</Grid>
)}
</Grid>
</>
);
};
export default PublicBoardForm;

@ -0,0 +1,182 @@
import { useEffect, useState } from 'react';
// material-ui
import { Button, Divider, Grid, InputAdornment, MenuItem, OutlinedInput, Select, Link } from '@mui/material';
// assets
import { IconSearch, IconFileText } from '@tabler/icons';
// berry ui
import MainCard from 'ui-component/cards/MainCard';
// project imports
import MuiDataGrid from 'views/form/MuiDataGrid';
import { getBoardList } from '../../../apis/user';
import CmmModal from '../../form/Modal/CmmModal';
import UserManagerForm from './UserManagerForm';
import { deletePublicBoard, getPublicBoardList, modifyPublicBoardHitCount, savePublicBoard } from '../../../apis/public';
const UserManager = () => {
const [category, setCategory] = useState('ciTitle');
const [searchTxt, setSearchTxt] = useState('');
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 10,
rows: []
// loading: false
});
// state
const [open, setOpen] = useState(false);
const [selectedRow, setSelectedRow] = useState({});
const [create, setCreate] = useState(false);
const [title, setTitle] = useState();
const columns = [
{ headerName: 'No.', headerAlign: 'center', field: 'rowId', align: 'center', width: 70 },
{
headerName: '사용자아이디',
headerAlign: 'center',
field: 'inDept',
align: 'center'
},
{
headerName: '이름',
headerAlign: 'center',
field: 'inTitle',
minWidth: 200,
renderCell: (params) => (
<Link underline="hover" href="#">
{params.value}
</Link>
)
},
{ headerName: '전화번호', headerAlign: 'center', field: 'a', align: 'center' },
{ headerName: '이메일', headerAlign: 'center', field: 'b', align: 'center' },
{ headerName: '사용여부', headerAlign: 'center', field: 'c', align: 'center' },
{ headerName: '사용구분', headerAlign: 'center', field: 'd', align: 'center' },
{ headerName: '생성일시', headerAlign: 'center', field: 'e', align: 'right' }
];
//
const search = () => {
const params = {
page: rowsState.page,
size: rowsState.pageSize
};
getPublicBoardList(params).then((response) => {
// console.log(response);
if (response && response.data) {
setTotalCount(response.count);
setRowsState((prevState) => ({ ...prevState, rows: response.data }));
}
});
};
const handleSearch = async (event) => {
if (event.type === 'keydown' && event.key === 'Enter') {
const newString = event?.target.value;
setSearchTxt(newString);
}
};
useEffect(() => {
search();
}, [rowsState.page, rowsState.pageSize, category, searchTxt]);
//
const handleCreate = () => {
setSelectedRow({});
setTitle('사용자 등록');
setCreate(true);
setOpen(true);
};
const handleOnCellClick = (e) => {
if (e?.field === 'inTitle') {
setCreate(false);
setTitle('사용자 정보 변경');
setSelectedRow(e?.row);
modifyPublicBoardHitCount(e?.row?.inCode);
setOpen(true);
}
};
// model
const submitPublicBoard = (type, payload) => {
switch (type) {
case 'SAVE':
savePublicBoard(payload).then(() => {
search();
setOpen(false);
}); // .then((res) => {
break;
case 'DELETE':
deletePublicBoard(payload).then(() => {
search();
setOpen(false);
}); // .then((res) => {
break;
default:
}
};
return (
<MainCard>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} lg={6}>
<Grid container spacing={1}>
<Grid item>
<Select id="category" name="reviewYear" defaultValue="ciTitle" onChange={(e) => setCategory(e.target.value)}>
<MenuItem key="1" value="ciTitle">
이름
</MenuItem>
<MenuItem key="2" value="ciName">
아이디
</MenuItem>
</Select>
</Grid>
<Grid item>
<OutlinedInput
placeholder="Search"
onKeyDown={handleSearch}
size="small"
autoFocus
endAdornment={
<InputAdornment position="end">
<IconSearch stroke={1.5} size="1rem" />
</InputAdornment>
}
/>
{/* <IconSearch stroke={1.5} size="1rem" /> */}
</Grid>
<Grid item>
<Button variant="contained" size="small" onClick={handleCreate}>
사용자 등록
</Button>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<MuiDataGrid
columns={columns}
rowsState={rowsState}
totalCount={totalCount}
setRowsState={setRowsState}
handleCellClick={handleOnCellClick}
/>
<CmmModal isBackdrop title={title} open={open} setOpen={setOpen}>
<UserManagerForm create={create} setOpen={setOpen} handleModalSave={submitPublicBoard} {...selectedRow} />
</CmmModal>
</MainCard>
);
};
export default UserManager;

@ -0,0 +1,185 @@
import { useMemo, useRef, useState } from 'react';
import { useAlert } from 'react-alert';
// material-ui
import { Button, FormControl, Grid, MenuItem, Select, TextField } from '@mui/material';
// assets
// berry ui
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
// project imports
import InputLabel from 'ui-component/extended/Form/InputLabel';
import { Delete, List, Save } from '@mui/icons-material';
import { fileDownload } from '../../../apis/common';
import FileForm from 'views/form/FileForm';
const PublicBoardForm = (props) => {
// eslint-disable-next-line react/prop-types
const { create, inCode, inDept, inTitle, inHit, inName, inNalja, inFilename, inContents, setOpen, handleModalSave } = props;
const alert = useAlert();
const quillRef = useRef();
const [dept, setDept] = useState(inDept || '주정차위반');
const [subject, setSubject] = useState(inTitle || '');
const [contents, setContents] = useState(inContents || '');
const [filesInfo, setFilesInfo] = useState();
const [selectedFile, setSelectedFile] = useState(inFilename || ''); //
// const [fileData, setFileData] = useState();
const onList = () => {
setOpen(false);
};
const onSave = () => {
// TODO : validation check
const formData = new FormData();
formData.append('inCode', inCode ?? '');
formData.append('inTitle', subject);
formData.append('inDept', dept);
formData.append('inContents', contents);
formData.append('inFilename', selectedFile ?? '');
if (filesInfo && filesInfo.length > 0) {
// eslint-disable-next-line no-plusplus
for (let i = 0; i < filesInfo.length; i++) formData.append('files', filesInfo[i]);
}
handleModalSave('SAVE', formData);
};
const onDelete = () => {
handleModalSave('DELETE', inCode);
};
const imageHandler = () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
if (input.files) {
const file = input.files[0];
const formData = new FormData();
formData.append('image', file);
console.log(formData);
// input
input.onchange = async () => {
const file = input.files;
if (file !== null) {
formData.append('image', file[0]);
try {
//
// const res = await axios.get('/');
//
const url = '/Users/minuk/Pictures/test.png';
//
const range = quillRef.current?.getEditor().getSelection()?.index;
if (range !== null && range !== undefined) {
const quill = quillRef.current?.getEditor();
quill?.setSelection(range, 1);
quill?.clipboard.dangerouslyPasteHTML(range, `<img src=${url} alt="이미지 태그가 삽입됩니다." />`);
}
} catch (error) {
console.log(error);
}
}
};
}
};
};
const modules = useMemo(
() => ({
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[{ size: ['small', false, 'large', 'huge'] }, { color: [] }],
[{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }, { align: [] }]
// ['image', 'video']
],
handlers: {
image: imageHandler
}
}
}),
[]
);
const onChangeFile = (file) => {
setSelectedFile(file.name);
setFilesInfo([file]);
};
// const onChangeFile = (e) => {
// setSelectedFile(e.target.files[0].name);
// setFilesInfo(e.target.files);
// };
const handleFileDownload = () => {
if (!inFilename) {
alert.show('등록된 파일이 없습니다.');
return;
}
fileDownload(inCode, inFilename, alert).then(() => {});
};
return (
<>
<Grid mt={2}>
<Grid container spacing={1} item xs={12} mb={1}>
<Grid item xs={12} sm={6}>
<TextField size="small" required label="USERID" value={subject} onChange={(e) => setSubject(e.target.value)} fullWidth />
</Grid>
<Grid item xs={12} sm={3}>
<TextField size="small" required label="PASSWD" value={subject} onChange={(e) => setSubject(e.target.value)} fullWidth />
</Grid>
<Grid item xs={12} sm={3}>
<TextField size="small" required label="PASSWD Check" value={subject} onChange={(e) => setSubject(e.target.value)} fullWidth />
</Grid>
<Grid item xs={12} sm={3}>
<TextField size="small" disabled label="이름" value={inName} fullWidth />
</Grid>
</Grid>
<Grid container spacing={1} item xs={12} mb={1}>
<Grid item xs={12} sm={3}>
<TextField size="small" disabled label="등록일" value={inNalja} fullWidth />
</Grid>
<Grid item xs={12} sm={3}>
<TextField size="small" disabled label="전화번호" value={inCode} fullWidth />
</Grid>
<Grid item xs={12} sm={3}>
<TextField size="small" disabled label="이메일" value={inHit} fullWidth />
</Grid>
</Grid>
</Grid>
<Grid item container spacing={0.5} xs={12}>
{!create && (
<Grid item>
<Button variant="contained" size="small" startIcon={<Delete />} onClick={onDelete}>
삭제
</Button>
</Grid>
)}
<Grid item style={{ marginLeft: 'auto' }}>
<Button variant="contained" size="small" startIcon={<List />} onClick={onList}>
목록
</Button>
</Grid>
<Grid item>
<Button variant="contained" size="small" startIcon={<Save />} onClick={onSave}>
저장
</Button>
</Grid>
</Grid>
</>
);
};
export default PublicBoardForm;
Loading…
Cancel
Save