基于redux重构TASK OA案例

56 阅读3分钟

思路

image.png

代码

image.png redux版本\store\action-types.js

export const TASK_LIST = 'TASK_LIST'
export const TASK_REMOVE = 'TASK_REMOVE'
export const TASK_UPDATE = 'TASK_UPDATE'

redux版本\store\reducer\taskReducer.js

import { TASK_LIST, TASK_REMOVE, TASK_UPDATE } from '../action-types'
import _ from '@/assets/utils'

let initial = {
  // 记录从服务器获取的全部任务
  list: null,
}
export default function taskReducer(state = initial, action) {
  // 自己封装的深克隆方法
  state = _.clone(true, state)
  let { type, payload, id } = action

  // 同步全部任务
  if (type === TASK_LIST) {
    state.list = payload
  }

  if (Array.isArray(state.list)) {
    // 删除指定任务
    if (type === TASK_REMOVE) {
      state.list = state.list.filter((item) => {
        return +item.id !== +id
      })
    }

    // 修改指定任务
    if (type === TASK_UPDATE) {
      state.list = state.list.map((item) => {
        if (+item.id === +id) {
          item.state = 2
          item.complete = new Date().toLocaleString('zh-CN', { hour12: false })
        }
        return item
      })
    }
  }

  return state
}

redux版本\store\reducer\index.js

import { combineReducers } from 'redux'
import taskReducer from './taskReducer'

const reducer = combineReducers({
  task: taskReducer,
})
export default reducer

redux版本\store\index.js

import { createStore, applyMiddleware } from 'redux'
import reducer from './reducer'
import reduxLogger from 'redux-logger' //在控制台输出派发日志
import reduxThunk from 'redux-thunk' //实现异步派发
import reduxPromise from 'redux-promise' //实现异步派发

// pplyMiddleware:中间键
const store = createStore(
    reducer, 
    applyMiddleware(reduxLogger, reduxThunk, reduxPromise))
export default store

redux版本\store\actions\taskAction.js

import { TASK_LIST, TASK_REMOVE, TASK_UPDATE } from '../action-types'
import api from '@/api'

const taskAction = {
  // redux-promise
  async getTaskList() {
    let result = {}
    try {
      result = await api.queryList()
      if (+result.code !== 0) result.list = []
    } catch (_) {
      result.list = []
    }
    return {
      type: TASK_LIST,
      payload: result.list,
    }
  },
  // redux-thunk
  getTaskListThunk() {
    return async (dispath) => {
      try {
        let { code, list } = await api.queryList()
        if (+code !== 0) list = []
        dispath({
          type: TASK_LIST,
          payload: list,
        })
      } catch (_) {
        dispath({
          type: TASK_LIST,
          payload: [],
        })
      }
    }
  },
  removeTask(id) {
    return {
      type: TASK_REMOVE,
      id,
    }
  },
  completeTask(id) {
    return {
      type: TASK_UPDATE,
      id,
    }
  },
}
export default taskAction

redux版本\store\actions\index.js


const actions = {
  task: taskAction,
}
export default actions

redux版本\index.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import Task from '@/views/Task'
/* ANTD */
import { ConfigProvider } from 'antd'
import zhCN from 'antd/lib/locale/zh_CN'
import './index.less'
/* REDUX */
import store from './store'
import { Provider } from 'react-redux'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <ConfigProvider locale={zhCN}>
    <Provider store={store}>
      <Task />
    </Provider>
  </ConfigProvider>
)

redux版本\views\Task.jsx

import React, { useState, useEffect } from 'react'
import { Button, DatePicker, Form, Input, Modal, Popconfirm, Table, Tag, message } from 'antd'
import api from '@/api'
import './Task.less'
import { connect } from 'react-redux'
import actions from '@/store/actions'

// 时间格式化的方法
const formatTime = (time, template = '{0}-{1}-{2} {3}:{4}:{5}') => {
  let arr = time.match(/\d+/g)
  return template.replace(/\{(\d+)\}/g, (_, $1) => {
    let item = arr[$1] || '00'
    if (item.length < 2) item = '0' + item
    return item
  })
}

const Task = function Task(props) {
  /* 定义表格列 */
  const columns = [
    {
      title: '编号',
      dataIndex: 'id',
      align: 'center',
      width: '8%',
    },
    {
      title: '任务描述',
      dataIndex: 'task',
      width: '50%',
    },
    {
      title: '状态',
      dataIndex: 'state',
      align: 'center',
      width: '10%',
      render: (text) => (+text === 1 ? '未完成' : '已完成'),
    },
    {
      title: '完成时间',
      dataIndex: 'time',
      align: 'center',
      width: '15%',
      render: (_, record) => {
        let { state, time, complete } = record
        time = +state === 1 ? time : complete
        return formatTime(time, '{1}-{2} {3}:{4}')
      },
    },
    {
      title: '操作',
      render: (_, record) => {
        let { state, id } = record
        return (
          <>
            <Popconfirm title="您确定要删除此任务吗?" onConfirm={removeHandler.bind(null, id)}>
              <Button type="link">删除</Button>
            </Popconfirm>

            {+state === 1 ? (
              <Popconfirm title="您确定要把此任务设置为完成吗?" onConfirm={updateHandler.bind(null, id)}>
                <Button type="link">完成</Button>
              </Popconfirm>
            ) : null}
          </>
        )
      },
    },
  ]

  /* 定义状态 */
  let [tableData, setTableData] = useState([]),
    [loading, setLoading] = useState(false),
    [selected, setSelected] = useState(0),
    [visible, setVisible] = useState(false),
    [confirmLoading, setConfirmLoading] = useState(false),
    [formIns] = Form.useForm()
  let { list, getTaskList, getTaskListThunk, removeTask, completeTask } = props

  /* 获取数据 */
  useEffect(() => {
    const init = async () => {
      // 判断公共状态是否存在:不存在则完成异步派发
      // !null=true ![]=false
      if (!list) {
        // list 不存在
        setLoading(true)
        await getTaskList()
        setLoading(false)
        return
      }
      // 存在则筛选想要的数据给TABLE
      let temp = list
      if (selected !== 0) {
        temp = temp.filter((item) => {
          return +item.state === selected
        })
      }
      setTableData(temp)
    }
    init()
    // 除了依赖选中,还依赖于redux的公共状态
  }, [selected, list])

  /* 页卡切换 */
  const changeTabHandler = (index) => {
    if (selected === index) return
    setSelected(index)
  }

  /* 关闭Modal && 提交信息  */
  const cancelHandler = () => {
    setVisible(false)
    setConfirmLoading(false)
    formIns.resetFields()
  }
  const submitHandler = async () => {
    try {
      await formIns.validateFields()
      // 向服务器提交数据
      setConfirmLoading(true)
      let { task, time } = formIns.getFieldsValue()
      time = time.format('YYYY-MM-DD HH:mm:ss')
      let { code } = await api.addTask({
        task,
        time,
      })
      if (+code !== 0) {
        message.error('很遗憾,当前操作失败~')
      } else {
        message.success('恭喜您,当前操作成功~')
        cancelHandler()
        // 异步派发
        setLoading(true)
        await getTaskListThunk()
        setLoading(false)
      }
    } catch (_) {}
    setConfirmLoading(false)
  }

  /* 删除 && 完成 */
  const removeHandler = async (id) => {
    try {
      let { code } = await api.removeTask(id)
      if (+code !== 0) {
        message.error('很遗憾,当前操作失败~')
        return
      }
      message.success('恭喜您,当前操作成功~')
      // 同步派发
      removeTask(id)
    } catch (_) {}
  }
  const updateHandler = async (id) => {
    try {
      let { code } = await api.completeTask(id)
      if (+code !== 0) {
        message.error('很遗憾,当前操作失败~')
        return
      }
      message.success('恭喜您,当前操作成功~')
      // 同步派发
      completeTask(id)
    } catch (_) {}
  }

  return (
    <div className="task-box">
      <header className="head-box">
        <h2 className="title">TASK OA 任务管理系统</h2>
        <Button type="primary" onClick={() => setVisible(true)}>
          新增任务
        </Button>
      </header>

      <section className="tag-box">
        {['全部', '未完成', '已完成'].map((item, index) => {
          return (
            <Tag key={index} color={selected === index ? '#108ee9' : ''} onClick={changeTabHandler.bind(null, index)}>
              {item}
            </Tag>
          )
        })}
      </section>

      <Table pagination={false} rowKey="id" loading={loading} dataSource={tableData} columns={columns} />

      <Modal
        title="新增任务窗口"
        okText="提交信息"
        maskClosable={false}
        keyboard={false}
        visible={visible}
        confirmLoading={confirmLoading}
        onCancel={cancelHandler}
        onOk={submitHandler}
      >
        <Form layout="vertical" form={formIns} initialValues={{ task: '', time: '' }}>
          <Form.Item
            label="任务描述"
            name="task"
            validateTrigger="onBlur"
            rules={[{ required: true, pattern: /^[\w\W]{6,}$/, message: '任务描述必须在6位以上~' }]}
          >
            <Input.TextArea rows={4} />
          </Form.Item>
          <Form.Item
            label="任务预期完成时间"
            name="time"
            validateTrigger="onBlur"
            rules={[{ required: true, message: '任务完成时间是必填项~' }]}
          >
            <DatePicker showTime />
          </Form.Item>
        </Form>
      </Modal>
    </div>
  )
}

export default connect((state) => state.task, actions.task)(Task)

redux版本\api\http.js

import axios from 'axios'
import qs from 'qs'
import { message } from 'antd'
import utils from '@/assets/utils'

const http = axios.create({
  baseURL: '/api',
  timeout: 60000,
})
http.defaults.transformRequest = (data) => {
  if (utils.isPlainObject(data)) data = qs.stringify(data)
  return data
}
http.interceptors.response.use(
  (response) => {
    return response.data
  },
  (reason) => {
    message.error('当前网络繁忙,请您稍后再试~')
    return Promise.reject(reason)
  }
)
export default http

redux版本\api\index.js

import http from './http'

// 获取指定状态的任务列表
const queryList = (state = 0) => {
  return http.get('/getTaskList', {
    params: {
      state,
    },
  })
}

// 新增任务
const addTask = (data) => http.post('/addTask', data)

// 删除任务
const removeTask = (id) => {
  return http.get('/removeTask', {
    params: {
      id,
    },
  })
}

// 完成任务
const completeTask = (id) => {
  return http.get('/completeTask', {
    params: {
      id,
    },
  })
}

export default {
  queryList,
  addTask,
  removeTask,
  completeTask,
}