case 管理页面静态布局以及功能实现

在做 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>
                                          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                          <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>
                                          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                          <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;
    }
  }
}

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值