在做 react 项目的时候,项目需求中有一个展示家族成员 case 管理的画面,左侧以列表的形式展示成员以及成员相关的case 列表,左侧则是与列表case 时间相对应的图表展示
实现方式如下:
import { useState, useEffect, useRef } from 'react'
import { Link } from 'react-router-dom'
import { generateClient } from 'aws-amplify/api';
import { Dropdown, Modal, Spin, Form, Input, Row, Col, DatePicker, Select, Button, Popover } from 'antd'
import { LoadingOutlined, ExclamationCircleFilled,CloseOutlined } from '@ant-design/icons'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat';
// 样式
import styles from './index.module.scss'
// aws 接口
import { listNeedyPeople, getIssues } from '../../../../../graphql/queries'
// icon
import { supportDate, menuIcon, downIcon, progressIcon, stopIcon, overIcon, discribetionIcon, rightIcon, dragIcon, deleteIcon, shareBlueIcon, moreIcon,select } from '../../../../../icons'
dayjs.extend(customParseFormat);
const { TextArea } = Input
const { confirm } = Modal
const client = generateClient()
const { RangePicker } = DatePicker
const consultantAndInstitutions = [
{ label: '塚原 優芽/A市福祉課', value: '1/1' },
{ label: '安藤晃/A地区地域包括支援センター', value: '2/2' },
{ label: '佐川栄子 /A病院', value: '3/3' },
{ label: '岩平 幹夫/生活困窮者自立相談支援センター', value: '4/4' },
{ label: '小松原佳枝/基幹相談支援センター', value: '5/5' },
{ label: '宇野 夏美/ハローワーク', value: '6/6' },
{ label: '栗田 政信/みどりクリニック', value: '7/7' },
{ label: '清水祥子/スクールカウンセラー', value: '8/8' },
{ label: '橘義人/多機関協働事業', value: '9/9' },
]
const repetition = [
{ label: 'なし', value: '1' }
]
const CaseManagement = (props) => {
var encodedParams = sessionStorage.getItem('encodedParams');
var decodedParams = decodeURIComponent(encodedParams);
var paramsObj = JSON.parse(decodedParams)
const tableRef = useRef(null)
const { authority, caseManagementList, onFold, onShowIssues, onChangeStatus, onOpenSelectStatus, onEditTask, onShowFlag, onRefresh } = props
const weekDay = ['日', '月', '火', '水', '木', '金', '土']
const [open, setOpen] = useState(false)
const [spinning, setSpinning] = useState(false)
const [familyMembers, setFamilyMembers] = useState([])
const [currentSelectFamilyMember, setCurrentSelectFamilyMember] = useState("")
const [currentPageStatus, setCurrentPageStatus] = useState('create')
const [isTaskCreate, setIsTaskCreate] = useState(true)
const [currentUpdateTask, setCurrentUpdateTask] = useState('')
const [taskForm, setTaskForm] = useState({
taskContent: "",
taskStartDate: "",
taskEndDate: "",
consultantAndInstitutions: "",
repetition: "",
alertNotification: "",
status: "1",
})
const [openActivity, setOpenActivity] = useState(false)
const [isActivityCreate, setIsActivityCreate] = useState(true)
const [currentUpdateActivity, setCurrentUpdateActivity] = useState('')
const [activityForm, setActivityForm] = useState({
activityContent: "",
activityStartDate: "",
activityEndDate: "",
consultantAndInstitutions: "",
repetition: "",
alertNotification: "",
status: "1",
})
const handleTaskCreate = () => {
setIsTaskCreate(true)
setOpen(true)
}
const handleTaskUpdate = () => {
setCurrentPageStatus('update')
setIsTaskCreate(false)
onEditTask()
}
const handleSubmitTask = async () => {
try {
if(!currentSelectFamilyMember) return
setSpinning(true)
// 在此处调用新增/修改 case 接口
setOpen(false)
setSpinning(false)
setTaskForm({
taskContent: "",
taskStartDate: "",
taskEndDate: "",
consultantAndInstitutions: "",
repetition: "",
alertNotification: "",
status: "1",
})
setCurrentSelectFamilyMember("")
setIsTaskCreate(true)
} catch(err) {
setSpinning(false)
console.error(err)
}
}
const handleActivityCreate = () => {
setIsActivityCreate(true)
setOpenActivity(true)
}
const handleActivityUpdate = (caseIndex, activityIndex, activity) => {
const activityform = {
activityContent: activity.activityContent,
activityStartDate: activity.activityStartDate,
activityEndDate: activity.activityEndDate,
consultantAndInstitutions: activity.consultantAndInstitutions,
repetition: activity.repetition,
alertNotification: activity.alertNotification,
status: activity.status,
}
setActivityForm(activityform)
setCurrentSelectFamilyMember(caseManagementList[caseIndex].value)
setCurrentUpdateActivity(activity.id)
setIsActivityCreate(false)
setOpenActivity(true)
}
const handleSubmitActivity = async () => {
try {
if(!currentSelectFamilyMember) return
setSpinning(true)
// 创建小蓝旗活动
setOpenActivity(false)
setSpinning(false)
setActivityForm({
activityContent: "",
activityStartDate: "",
activityEndDate: "",
consultantAndInstitutions: "",
repetition: "",
alertNotification: "",
status: "1",
})
setCurrentSelectFamilyMember("")
} catch(err) {
console.error(err)
}
}
const handleCancel = () => {
setOpen(false)
setTaskForm({
taskContent: "",
taskStartDate: "",
taskEndDate: "",
consultantAndInstitutions: "",
repetition: "",
alertNotification: "",
status: "1",
})
setCurrentSelectFamilyMember("")
setOpenActivity(false)
setActivityForm({
activityContent: "",
activityStartDate: "",
activityEndDate: "",
consultantAndInstitutions: "",
repetition: "",
alertNotification: "",
status: "1",
})
}
const handleFold = (item, index) => {
onFold(index)
}
const handleGoBack = () => {
setCurrentPageStatus('create')
setIsTaskCreate(true)
}
// 课题
const [issues, setIssues] = useState({})
const handleShowIssues = async (item, index) => {
try {
onShowIssues(index)
if(!item.issuesInfoId) return setIssues({})
const res = await client.graphql({ query: getIssues, variables: { id: item.issuesInfoId } });
if(res.data.getIssues) {
setIssues(res.data.getIssues)
}
} catch(err) {
console.error(err)
}
}
const handleEditTask = (i, index, task) => {
const taskform = {
taskContent: task.taskContent,
taskStartDate: task.taskStartDate,
taskEndDate: task.taskEndDate,
consultantAndInstitutions: task.consultantAndInstitutions,
repetition: task.repetition,
alertNotification: task.alertNotification,
status: task.status,
}
setTaskForm(taskform)
setCurrentSelectFamilyMember(caseManagementList[i].value)
setCurrentUpdateTask(task.id)
setIsTaskCreate(false)
setOpen(true)
}
const handleDeleteTask = async (task) => {
try {
confirm({
title: '削除してよろしいですか?',
okText: '確認',
icon: <ExclamationCircleFilled />,
content: null,
async onOk() {
// 调用删除 case 接口
},
onCancel() {
console.log('Cancel');
},
});
} catch(err) {
console.error(err)
}
}
const fetchlistNeedyPeople = async () => {
try {
const ListNeedyPeople = await client.graphql({ query: listNeedyPeople, variables: { filter: { familygroupID: { eq: paramsObj.id } }, limit: 1000 } });
if (ListNeedyPeople.data.listNeedyPeople) {
const data = ListNeedyPeople.data.listNeedyPeople.items
const familyMember = data.filter(item => item.fullName !== null).map(item => ({label: item.fullName, value: item.id}))
setFamilyMembers(familyMember)
}
} catch (err) {
console.error(err)
}
}
useEffect(() => {
fetchlistNeedyPeople()
}, [])
const items = [
{
label: <div style={{display: 'flex', alignItems: 'center', width: 214, height: 41, fontSize: 16}} onClick={handleTaskCreate}>タスクの追加</div>,
key: '0',
},
{
type: 'divider',
},
{
label: <div style={{display: 'flex', alignItems: 'center', width: 214, height: 41, fontSize: 16}} onClick={handleTaskUpdate}>タスクの編集</div>,
key: '1',
},
{
type: 'divider',
},
{
label: <div style={{display: 'flex', alignItems: 'center', width: 214, height: 41, fontSize: 16}} onClick={handleActivityCreate}>イベントの追加</div>,
key: '2',
},
]
const status = [
{ value: '1', label: '順調' },
{ value: '2', label: '遅滞' },
{ value: '3', label: '完了' },
]
const handleOpenChange = (i, index, newOpen) => {
onOpenSelectStatus(i, index, newOpen)
}
const handleChangeStatus = async (i, index, statu, task) => {
try {
onChangeStatus(i, index, statu)
// 调用修改 case 接口修改状态
} catch(err) {
console.error(err)
}
}
const handleMouseEnter = (activity) => {
onShowFlag(activity)
}
// ---
const currentYear = new Date().getFullYear()
const currentMonth = new Date().getMonth() + 1
const currentDate = new Date().getDate()
const currentMonthDateCount = new Date(currentYear, currentMonth, 0).getDate()
const currentMonthDateArr = []
for(let i = 1; i <= currentMonthDateCount; i++) {
currentMonthDateArr.push({date: i, week: weekDay[new Date(`${currentYear}-${currentMonth}-${i}`).getDay()]})
}
const prevMonth = currentMonth - 1 === 0 ? 12 : currentMonth - 1
const prevMonthInYear = prevMonth > currentMonth ? currentYear - 1 : currentYear
const prevMonthDateCount = new Date(prevMonthInYear, prevMonth, 0).getDate()
const prevMonthDateArr = []
for(let i = 1; i <= prevMonthDateCount; i++) {
prevMonthDateArr.push({date: i, week: weekDay[new Date(`${prevMonthInYear}-${prevMonth}-${i}`).getDay()]})
}
const nextMonth = currentMonth + 1 === 13 ? 1 : currentMonth + 1
const nextMonthInYear = nextMonth < currentMonth ? currentYear + 1 : currentYear
const nextMonthDateCount = new Date(nextMonthInYear, nextMonth, 0).getDate()
const nextMonthDateArr = []
for(let i = 1; i <= nextMonthDateCount; i++) {
nextMonthDateArr.push({date: i, week: weekDay[new Date(`${nextMonthInYear}-${nextMonth}-${i}`).getDay()]})
}
const [taskPreviewTimePeriod, setTaskPreviewTimePeriod] = useState({
startTime: `${prevMonthInYear}年${prevMonth > 9 ? prevMonth : '0' + prevMonth}月`,
endTime: `${nextMonthInYear}年${nextMonth > 9 ? nextMonth : '0' + nextMonth}月`
})
const [times, setTimes] = useState([
{
year: prevMonthInYear,
month: prevMonth > 9 ? prevMonth : '0' + prevMonth,
date: prevMonthDateArr
},
{
year: currentYear,
month: currentMonth > 9 ? currentMonth : '0' + currentMonth,
date: currentMonthDateArr
},
{
year: nextMonthInYear,
month: nextMonth > 9 ? nextMonth : '0' + nextMonth,
date: nextMonthDateArr
},
])
useEffect(() => {
let pattern = /(\d{4})年(\d{2})月/;
let replacement = "$1/$2";
const timePeriodMonthCount = new Date(+new Date(taskPreviewTimePeriod.endTime.replace(pattern, replacement)) - (+new Date(taskPreviewTimePeriod.startTime.replace(pattern, replacement)))).getMonth() + 1
const timePeriod = []
let year = Number(taskPreviewTimePeriod.startTime.replace(pattern, replacement).split('/')[0])
let month = Number(taskPreviewTimePeriod.startTime.replace(pattern, replacement).split('/')[1])
for(let i = 0; i < timePeriodMonthCount; i++) {
const date = []
const monthDateCount = new Date(year, month, 0).getDate()
for(let j = 1; j <= monthDateCount; j++) {
date.push({date: j, week: weekDay[new Date(`${year}-${month}-${j}`).getDay()]})
}
timePeriod.push({
year: year,
month: month > 9 ? month : '0' + month,
date: date
})
if(month + 1 > 12) {
year += 1
month = 1
} else {
month += 1
}
}
setTimes(timePeriod)
setTimeout(() => {
const index = timePeriod.findIndex(item => item.month === (new Date().getMonth() > 9 ? new Date().getMonth() : '0' + new Date().getMonth()))
const count = timePeriod.reduce((count, item, i) => {
if(index >= i) {
count += item.date.length
}
return count
}, 0)
tableRef.current.scrollLeft = count * 40 + (new Date(+new Date() - (+new Date(`${new Date().getFullYear()}/${new Date().getMonth() + 1}/01`))).getDate() - 1) * 40 - tableRef.current.offsetWidth / 2 + 40
}, 100)
}, [taskPreviewTimePeriod])
return (
<div className={styles.root}>
{
currentPageStatus === 'create' &&
<div className="header">
<header>
<h2>進捗管理</h2>
<span className="tag">相談受付</span>
</header>
<RangePicker
allowClear={false}
picker="month"
size="large"
suffixIcon={<img src={supportDate} alt="supportDate" />}
format="YYYY年MM月"
value={[dayjs(taskPreviewTimePeriod.startTime, 'YYYY年MM月'), dayjs(taskPreviewTimePeriod.endTime, 'YYYY年MM月')]}
onChange={(dates, dateStings) => { setTaskPreviewTimePeriod({startTime: dateStings[0], endTime: dateStings[1]}) }}
/>
</div>
}
<div className="container">
{/* 左侧列表区域html */}
<div className="case_list">
{
currentPageStatus === 'create'
{/* 创建 case */}
? <>
<div className="case_list_title">
<Dropdown
menu={{
items,
}}
trigger={['click']}
>
<img className="icon" src={menuIcon} alt="" />
</Dropdown>
<h3>タスク一覧</h3>
</div>
<div className="case_list_container">
{
caseManagementList.map((item, index) => (
(item.caseManagementList && item.caseManagementList.length > 0) &&
<div key={item.value} className="case_list_content">
<div className="case_title">
<Link className="family_member_name">{`${item.label}さん`}</Link>
<img className="icon" style={{transform: `rotate(${item.isFold ? '0deg' : '180deg'})`, transition: 'all .2s'}} src={downIcon} alt="" onClick={() => handleFold(item, index)} />
</div>
<div className="case_content" style={{height: item.isFold ? 0 : item.caseManagementList.length * 54, overflow: 'hidden', transition: 'all .2s'}}>
{
item.caseManagementList.map((task, index) => (
<div key={index} className="case_item" style={{backgroundColor: index % 2 !== 0 ? '#F2F2F2' : '#FFFFFF'}}>
<div className="case_item_title">
<h4>{task.taskContent}</h4>
<span>{task.taskStartDate} - {task.taskEndDate}</span>
</div>
<div className="status">
{
task.status === '1' && <div className="tag tag_progress"><img src={progressIcon} alt="" /> <span>順調</span></div>
}
{
task.status === '2' && <div className="tag tag_stop"><img src={stopIcon} alt="" /> <span>遅滞</span></div>
}
{
task.status === '3' && <div className="tag tag_over"><img src={overIcon} alt="" /> <span>完了</span></div>
}
</div>
<div className="consultantAndInstitutions">
{
consultantAndInstitutions.find(e => e.value === task.consultantAndInstitutions)
? <>
<span>{consultantAndInstitutions.find(e => e.value === task.consultantAndInstitutions).label.split('/')[1]}</span>
<span>{consultantAndInstitutions.find(e => e.value === task.consultantAndInstitutions).label.split('/')[0]}</span>
</>
: null
}
</div>
<Button type="primary" style={{fontSize: 14, borderWidth: 2, width: 76, height: 32}} ghost shape="round">
フォロー
</Button>
</div>
))
}
</div>
</div>
))
}
</div>
</>
{/* 更新 case */}
: <>
<header className="editstatus_header">
<h2>タスクの編集</h2>
<Button type="primary" style={{fontSize: 14, width: 130, height: 31, marginLeft: 20}} ghost onClick={handleGoBack}>
進捗管理に戻る
</Button>
</header>
<div className="case_list_container_edit">
{
caseManagementList.map((item, i) => (
(item.caseManagementList && item.caseManagementList.length > 0) &&
<div key={item.value} className="case_list_content">
<div className="case_title" style={{backgroundColor: !item.isShowIssues ? '#EDEDED' : '#E5EBFF'}}>
<Link className="family_member_name">{`${item.label}さん`}</Link>
<div className="title_right">
<img className="icon" src={discribetionIcon} alt="" />
<span>参考情報を見る</span>
<img className="icon" style={{transform: `rotate(${!item.isShowIssues ? '0deg' : '180deg'})`, transition: 'all .2s'}} src={rightIcon} alt="" onClick={() => handleShowIssues(item, i)} />
</div>
{
item.isShowIssues &&
<div className="issue_content_container">
<div className="issue_content">
<div className="title">
<h3>本人の主訴・状況</h3>
<img src={shareBlueIcon} alt="" />
</div>
<div style={{fontSize: 14, fontWeight: 400, whiteSpace: 'pre-line'}} dangerouslySetInnerHTML={{ __html: issues.chiefComplaintSituation ? issues.chiefComplaintSituation : "-" }}></div>
</div>
<div className="issue_content">
<div className="title">
<h3>課題と背景要因</h3>
<img src={shareBlueIcon} alt="" />
</div>
<div style={{fontSize: 14, fontWeight: 400, whiteSpace: 'pre-line'}} dangerouslySetInnerHTML={{ __html: issues.issuesAndBackgroundFactors ? issues.issuesAndBackgroundFactors : "-" }}></div>
</div>
<div className="issue_content">
<div className="title">
<h3>課題のまとめと支援方針</h3>
<img src={shareBlueIcon} alt="" />
</div>
<div style={{fontSize: 14, fontWeight: 400, whiteSpace: 'pre-line'}} dangerouslySetInnerHTML={{ __html: issues.consultationPolicy ? issues.consultationPolicy : "-" }}></div>
</div>
</div>
}
</div>
<div className="case_content">
{
item.caseManagementList.map((task, index) => (
<div key={index} className="case_item" onClick={() => handleEditTask(i, index, task)}>
<img className="drag" src={dragIcon} alt="" />
<div className="case_item_title">
<h4>{task.taskContent}</h4>
<span>{task.taskStartDate} - {task.taskEndDate}</span>
</div>
<Popover
placement="bottom"
trigger="click"
open={task.open}
onOpenChange={(newOpen) => handleOpenChange(i, index, newOpen)}
content={
<>
{
status.map(statu => <div key={statu.value} style={{width: 60, height: 32, paddingTop: 6, fontSize: 14, fontWeight: 500, textAlign: 'center', cursor: 'pointer'}} onClick={(e) => {e.stopPropagation(); e.preventDefault(); handleChangeStatus(i, index, statu, task)}}>{statu.label}</div>)
}
</>
}
>
<div className="status" onClick={(e) => {e.stopPropagation(); e.preventDefault();}}>
<img src={downIcon} alt="" />
<span>{status.find(s => s.value === task.status) ? status.find(s => s.value === task.status).label : ""}</span>
</div>
</Popover>
<div className="consultantAndInstitutions">
{
consultantAndInstitutions.find(e => e.value === task.consultantAndInstitutions)
? <>
<span>{consultantAndInstitutions.find(e => e.value === task.consultantAndInstitutions).label.split('/')[1]}</span>
<span>{consultantAndInstitutions.find(e => e.value === task.consultantAndInstitutions).label.split('/')[0]}</span>
</>
: null
}
</div>
<img className="delete" src={deleteIcon} alt="" onClick={(e) => {e.stopPropagation(); e.preventDefault(); handleDeleteTask(task)}} />
</div>
))
}
</div>
</div>
))
}
</div>
<div style={{padding: '16px 4px 0 0'}}>
<Button type="primary" loading={spinning} size="large" style={{height: 50}} block onClick={handleTaskCreate}>
+タスクの新規作成
</Button>
</div>
</>
}
</div>
{/* 右边图表区域html */}
<div className="table" ref={tableRef}>
<div className="thead">
<div className="thead_date">
{
times.map((time, index) => (
<div key={index} className="time">
<div className="month">{time.year}年{time.month}月</div>
<div className="dates">
{
time.date.map(date => (
<div
key={`${time.month}-${date.date}`}
className={['日', '土'].includes(date.week) ? 'date date_saturday_sunday' : 'date'}
style={{color: date.week === '日' ? "#D30000" : "#000000"}}
>
<span>{date.date}</span>
<span>{date.week}</span>
</div>
))
}
</div>
</div>
))
}
</div>
</div>
<div className="tbody" style={{width: 40 * (times.reduce((count, item) => { count += item.date.length; return count }, 0))}}>
{
times.map((time, index) => (
<div key={index} className="time">
{
time.date.map(date => (
<div
key={`${time.month}-${date.date}`}
className={['日', '土'].includes(date.week) ? 'date date_saturday_sunday' : 'date'}
>
{
currentPageStatus === 'create' && caseManagementList.map((item, j) => (
(item.caseManagementList && item.caseManagementList.length > 0) &&
<div key={item.value} className="case_list_content">
<div className="case_title">
{
item.activityList.map((activity, i) => {
return `${time.year}/${time.month}/${date.date > 9 ? date.date : '0' + date.date}` === activity.activityStartDate
? (
<div key={i} className="case_item_active">
<div className="activity">
<div className="flagstaff"></div>
<p
className="flag"
style={{minWidth: activity.isShow ? activity.activityContent.length * 18 : 38, transition: 'all .2s'}}
onMouseEnter={() => handleMouseEnter(activity)}
onMouseLeave={() => handleMouseEnter(null)}
onClick={() => handleActivityUpdate(j, i, activity)}
>{activity.isShow && activity.activityContent}</p>
</div>
</div>
)
: null
})
}
</div>
<div className="case_content" style={{height: item.isFold ? 0 : item.caseManagementList.length * 54, display: item.isFold ? 'none' : 'block', transition: 'all .2s'}}>
{
item.caseManagementList.map((task, index) => {
return `${time.year}/${time.month}/${date.date > 9 ? date.date : '0' + date.date}` === task.taskStartDate
? (
<div key={index} className="case_item_active">
{
task.status === '1' &&
<div className="task task_progress" style={{width: 40 * (new Date(+new Date(task.taskEndDate) - (+new Date(task.taskStartDate))).getDate()) - 10}}>
<div title={task.taskContent}>{task.taskContent}</div>
<img src={moreIcon} alt="" />
</div>
}
{
(task.status === '2' || task.status === '3') &&
<div className="task task_over" style={{width: 40 * (new Date(+new Date(task.taskEndDate) - (+new Date(task.taskStartDate))).getDate()) - 10}}>
<div title={task.taskContent}>{task.taskContent}</div>
<img src={moreIcon} alt="" />
</div>
}
</div>
)
: <div key={index} className="case_item"></div>
})
}
</div>
</div>
))
}
{
`${currentYear}-${currentMonth > 9 ? currentMonth : '0' + currentMonth}-${currentDate}` === `${time.year}-${time.month}-${date.date}` &&
<div style={{position: 'absolute', top: 0, left: 19, width: 2, height: '100%', backgroundColor: '#FC0000'}}></div>
}
</div>
))
}
</div>
))
}
</div>
</div>
</div>
<Modal
title={isTaskCreate ? "タスク作成" : "タスク編集"}
width="655px"
mask={false}
keyboard={false}
maskClosable={false}
closeIcon={<CloseOutlined style={{fontSize:25}} />}
open={open}
style={{
top: 'calc(100vh - 846px)',
left: 114,
margin: 0
}}
onCancel={() => { handleCancel() }}
footer={
<div style={{padding: '0 20px 20px'}}>
<Button type="primary" loading={spinning} size="large" style={{height: 50}} block onClick={handleSubmitTask}>
{isTaskCreate ? 'タスクを追加する' : 'タスクを編集する'}
</Button>
</div>
}
>
<Spin spinning={spinning} indicator={<LoadingOutlined style={{ fontSize: 48, marginTop: 80 }} spin />}>
<Form
name="編集する"
labelCol={{
span: 10,
}}
style={{padding: '20px 20px 0'}}
size="large"
layout="vertical"
autoComplete="off"
>
<Row>
<Col span={24}>
<Form.Item
label="タスク内容"
>
<TextArea
rows={3}
autoSize={{
minRows: 2,
maxRows: 4,
}}
value={taskForm.taskContent}
placeholder="タスク内容を入力してください"
onChange={(e) => { setTaskForm({...taskForm, taskContent: e.target.value}) }}
/>
</Form.Item>
</Col>
<Col span={12} style={{paddingRight: 10}}>
<Form.Item
label="開始日"
>
<DatePicker
format="YYYY/MM/DD"
disabledDate={(current) => taskForm.taskStartDate === "" ? false : dayjs(taskForm.taskEndDate, "YYYY/MM/DD") < current}
style={{width: '100%', height: 50}}
suffixIcon={<img src={supportDate} alt="supportDate" />}
value={taskForm.taskStartDate === "" ? "" : dayjs(taskForm.taskStartDate, "YYYY/MM/DD")}
onChange={(date, dateString) => {setTaskForm({...taskForm, taskStartDate: dateString})}}
/>
</Form.Item>
</Col>
<Col span={12} style={{paddingLeft: 10}}>
<Form.Item
label="終了日"
>
<DatePicker
format="YYYY/MM/DD"
disabledDate={(current) => taskForm.taskStartDate === "" ? false : dayjs(taskForm.taskStartDate, "YYYY/MM/DD") > current}
style={{width: '100%', height: 50}}
suffixIcon={<img src={supportDate} alt="supportDate" />}
value={taskForm.taskEndDate === "" ? "" : dayjs(taskForm.taskEndDate, "YYYY/MM/DD")}
onChange={(date, dateString) => {setTaskForm({...taskForm, taskEndDate: dateString})}}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="担当者/機関"
>
<Select
style={{
width: 400,
height: 50
}}
suffixIcon={<img src={select} alt="select" />}
value={taskForm.consultantAndInstitutions}
onChange={(val) => {setTaskForm({...taskForm, consultantAndInstitutions: val})}}
options={consultantAndInstitutions}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="対象者"
>
<Select
style={{
width: 400,
height: 50
}}
suffixIcon={<img src={select} alt="select" />}
disabled={!isTaskCreate}
value={currentSelectFamilyMember}
onChange={(val) => {setCurrentSelectFamilyMember(val)}}
options={familyMembers}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="繰り返し"
>
<Select
style={{
width: 400,
height: 50
}}
suffixIcon={<img src={select} alt="select" />}
value={taskForm.repetition}
onChange={(val) => {setTaskForm({...taskForm, repetition: val})}}
options={repetition}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="アラート通知"
>
<Select
style={{
width: 400,
height: 50
}}
suffixIcon={<img src={select} alt="select" />}
value={taskForm.alertNotification}
onChange={(val) => {setTaskForm({...taskForm, alertNotification: val})}}
options={[]}
/>
</Form.Item>
</Col>
</Row>
</Form>
</Spin>
</Modal>
<Modal
title={isActivityCreate ? "イベント作成" : "イベント編集"}
width="800px"
mask={false}
keyboard={false}
maskClosable={false}
closeIcon={<CloseOutlined style={{fontSize:25}} />}
open={openActivity}
style={{
top: 'calc(100vh - 846px)',
left: 'calc(100vw - 820px)',
margin: 0
}}
onCancel={() => { handleCancel() }}
footer={
<div style={{padding: '0 20px 20px'}}>
<Button type="primary" loading={spinning} size="large" style={{height: 50}} block onClick={handleSubmitActivity}>
{isActivityCreate ? 'イベントを追加する' : 'イベントを編集する'}
</Button>
</div>
}
>
<Spin spinning={spinning} indicator={<LoadingOutlined style={{ fontSize: 48, marginTop: 80 }} spin />}>
<Form
name="編集する"
labelCol={{
span: 10,
}}
style={{padding: '20px 20px 0'}}
size="large"
layout="vertical"
autoComplete="off"
>
<Row>
<Col span={24}>
<Form.Item
label="イベント内容"
>
<TextArea
rows={3}
autoSize={{
minRows: 2,
maxRows: 4,
}}
value={activityForm.activityContent}
placeholder="タスク内容を入力してください"
onChange={(e) => { setActivityForm({...activityForm, activityContent: e.target.value}) }}
/>
</Form.Item>
</Col>
<Col span={12} style={{paddingRight: 10}}>
<Form.Item
label="開始日"
>
<DatePicker
format="YYYY/MM/DD"
disabledDate={(current) => activityForm.activityStartDate === "" ? false : dayjs(activityForm.activityEndDate, "YYYY/MM/DD") < current}
style={{width: '100%', height: 50}}
suffixIcon={<img src={supportDate} alt="supportDate" />}
value={activityForm.activityStartDate === "" ? "" : dayjs(activityForm.activityStartDate, "YYYY/MM/DD")}
onChange={(date, dateString) => {setActivityForm({...activityForm, activityStartDate: dateString})}}
/>
</Form.Item>
</Col>
<Col span={12} style={{paddingLeft: 10}}>
<Form.Item
label="終了日"
>
<DatePicker
format="YYYY/MM/DD"
suffixIcon={<img src={supportDate} alt="supportDate" />}
disabledDate={(current) => activityForm.activityStartDate === "" ? false : dayjs(activityForm.activityStartDate, "YYYY/MM/DD") > current}
style={{width: '100%', height: 50}}
value={activityForm.activityEndDate === "" ? "" : dayjs(activityForm.activityEndDate, "YYYY/MM/DD")}
onChange={(date, dateString) => {setActivityForm({...activityForm, activityEndDate: dateString})}}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="担当者/機関"
>
<Select
style={{
width: 400,
height: 50
}}
suffixIcon={<img src={select} alt="select" />}
value={activityForm.consultantAndInstitutions}
onChange={(val) => {setActivityForm({...activityForm, consultantAndInstitutions: val})}}
options={consultantAndInstitutions}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="対象者"
>
<Select
style={{
width: 400,
height: 50
}}
suffixIcon={<img src={select} alt="select" />}
disabled={!isActivityCreate}
value={currentSelectFamilyMember}
onChange={(val) => {setCurrentSelectFamilyMember(val)}}
options={familyMembers}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="繰り返し"
>
<Select
style={{
width: 400,
height: 50
}}
suffixIcon={<img src={select} alt="select" />}
value={activityForm.repetition}
onChange={(val) => {setActivityForm({...activityForm, repetition: val})}}
options={repetition}
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="アラート通知"
>
<Select
style={{
width: 400,
height: 50
}}
suffixIcon={<img src={select} alt="select" />}
value={activityForm.alertNotification}
onChange={(val) => {setActivityForm({...activityForm, alertNotification: val})}}
options={[]}
/>
</Form.Item>
</Col>
</Row>
</Form>
</Spin>
</Modal>
</div>
)
}
export default CaseManagement
样式:
.root {
:global{
display: flex;
flex-direction: column;
height: calc(100vh - 189px);
overflow: auto;
background: #fff;
padding: 0 0 0 10px;
.header {
display: flex;
align-items: center;
width: 100%;
header {
display: flex;
align-items: center;
width: 655px;
margin-top: 10px;
margin-bottom: 20px;
h2 {
font-size: 24px;
}
.tag {
height: 31px;
padding: 0 10px;
border: 2px solid #00bb4b;
border-radius: 16px;
background-color: #dcecd2;
color: #00bb4b;
font-size: 16px;
margin-left: 30px;
}
}
}
.container {
flex: 1;
display: flex;
width: 100%;
.case_list {
display: flex;
flex-direction: column;
width: 655px;
border-right: 1px solid #ECECEC;
.editstatus_header {
display: flex;
align-items: center;
width: 100%;
margin-top: 10px;
margin-bottom: 20px;
h2 {
font-size: 24px;
}
}
&_title {
display: flex;
align-items: center;
width: 100%;
height: 81px;
padding: 0 20px;
background-color: #ededed;
border-bottom: 1px solid #D9D9D9;
.icon {
width: 32px;
height: 32px;
margin-right: 10px;
&:hover {
cursor: pointer;
}
}
h3 {
font-size: 16px;
}
}
&_container {
width: 100%;
.case_list_content {
width: 100%;
min-height: 40px;
overflow: hidden;
.case_title {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 48px;
padding: 0 10px;
background-color: #E5EBFF;
.family_member_name {
font-size: 16px;
text-decoration: underline;
}
.icon {
width: 16px;
height: 16px;
&:hover {
cursor: pointer;
background-color: rgba(0, 0, 0, .02);
}
}
}
.case_content {
.case_item {
display: flex;
align-items: center;
width: 100%;
height: 54px;
padding: 0 12px;
.case_item_title {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
h4 {
font-size: 16px;
font-weight: 700;
margin-top: 6px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
span {
font-size: 12px;
font-weight: 700;
color: #747474;
}
}
.status {
display: flex;
width: 70px;
text-align: center;
img {
width: 8px;
}
.tag {
display: flex;
align-items: center;
height: 26px;
line-height: 26px;
padding: 0 8px;
border-radius: 16px;
font-size: 12px;
}
.tag_progress {
border: 2px solid #00bb4b;
background-color: #dcecd2;
color: #00bb4b;
}
.tag_stop {
border: 2px solid #D30000;
background-color: #FF11041A;
color: #D30000;
}
.tag_over {
border: 2px solid #81CCF2;
background-color: #C9D4FD63;
color: #81CCF2;
}
}
.consultantAndInstitutions {
flex: 1.2;
color: #747474;
font-size: 12px;
font-weight: 700;
}
}
}
}
}
&_container_edit {
flex: 1;
width: 100%;
padding-right: 4px;
.case_list_content {
width: 100%;
min-height: 40px;
.case_title {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 48px;
padding: 0 0 0 10px;
margin-top: 12px;
background-color: #EDEDED;
position: relative;
.family_member_name {
font-size: 16px;
text-decoration: underline;
}
.issue_content_container {
position: absolute;
top: 0;
left: 650px;
width: calc(100vw - 812px);
background-color: #E5EBFF;
padding: 0 20px 22px;
z-index: 999;
.issue_content {
width: 100%;
border-radius: 20px;
border: 1px solid #D8D8D8;
background-color: #FFFFFF;
padding: 16px 16px 24px 24px;
margin-top: 22px;
.title {
display: flex;
justify-content: space-between;
align-items: center;
h3 {
font-size: 16px;
font-weight: 700;
}
img {
width: 43px;
}
}
}
}
.title_right {
display: flex;
align-items: center;
span {
font-size: 12px;
margin-top: 2px;
}
.icon {
width: 32px;
height: 32px;
&:hover {
cursor: pointer;
background-color: rgba(0, 0, 0, .02);
}
}
}
}
.case_content {
.case_item {
display: flex;
align-items: center;
width: 100%;
height: 54px;
padding: 0 4px;
border: 1px solid #E9E9E9;
margin-top: 4px;
transition: .2s all;
&:hover {
cursor: pointer;
background-color: #f3f3f3;
}
.drag {
width: 32px;
height: 32px;
margin-right: 8px;
&:hover {
cursor: pointer;
}
}
.case_item_title {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
h4 {
font-size: 16px;
font-weight: 700;
margin-top: 6px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
span {
font-size: 12px;
font-weight: 700;
color: #747474;
}
}
.status {
display: flex;
align-items: center;
width: 86px;
height: 36px;
border: 2px solid #909090;
border-radius: 18px;
overflow: hidden;
margin: 0 10px;
&:hover {
cursor: pointer;
}
.ant-select-selector {
width: 60px;
border: 0;
padding: 0;
}
img {
width: 20px;
margin: 0 10px;
}
span {
margin-top: 2px;
font-size: 12px;
font-weight: 500;
}
}
.consultantAndInstitutions {
flex: 1.3;
color: #747474;
font-size: 12px;
font-weight: 700;
}
.delete {
&:hover {
cursor: pointer;
}
}
}
}
}
}
}
.table {
flex: 1;
display: flex;
flex-direction: column;
overflow-x: auto;
.thead {
display: flex;
width: 100%;
height: 81px;
&_date {
display: flex;
.time {
width: 100%;
height: 100%;
}
.month {
width: 100%;
height: 30px;
line-height: 30px;
padding: 0 10px;
border: 1px solid #ECECEC;
border-left: 0;
}
.dates {
display: flex;
align-items: center;
.date {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
min-width: 40px;
height: 51px;
font-size: 16px;
font-weight: 700;
border: 1px solid #ECECEC;
border-top: 0;
border-left: 0;
padding-top: 4px;
span:nth-child(1) {
font-size: 16px;
font-weight: 700;
}
span:nth-child(2) {
font-size: 11px;
font-weight: 500;
}
}
.date_saturday_sunday {
background-color: #F5F5F5;
}
}
}
}
.tbody {
flex: 1;
display: flex;
position: relative;
overflow-x: auto;
.time {
display: flex;
.date {
position: relative;
min-width: 40px;
border-right: 1px solid #ECECEC;
.case_list_content {
min-height: 40px;
.case_title {
height: 48px;
.case_item_active {
height: 100%;
position: relative;
.activity {
display: flex;
position: absolute;
top: 0;
left: 0;
z-index: 999;
.flag {
min-width: 38px;
height: 28px;
line-height: 28px;
color: #fff;
font-size: 16px;
font-weight: 700;
background-color: #486EF8;
overflow: hidden;
&:hover {
cursor: pointer;
}
}
.flagstaff {
min-width: 4px;
height: 48px;
background-color: #486EF8;
}
}
}
}
.case_content {
.case_item_active {
height: 54px;
position: relative;
.task {
display: flex;
align-items: center;
justify-content: space-between;
position: absolute;
top: 8px;
left: 4px;
height: 38px;
border-radius: 3px;
padding: 0 10px;
overflow: hidden;
z-index: 99;
div {
flex: 1;
font-size: 16px;
font-weight: 700;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
img {
width: 37px;
&:hover {
cursor: pointer;
}
}
}
.task_progress {
color: #000000;
border: 1px solid #486EF8;
background-color: #486EF866;
}
.task_over {
color: #747474;
border: 1px solid #414141;
background-color: #00000040;
div {
text-decoration: line-through;
}
}
}
.case_item {
height: 54px;
}
}
}
}
.date_saturday_sunday {
background-color: #F5F5F5;
}
}
}
}
}
.menu_item {
width: 214px;
height: 41px;
line-height: 41px;
font-size: 16px;
}
}
}