feat(事故管理): 实现事故类型、账号和消息的完整功能
- 新增事故类型管理页面及表单
- 重构账号管理功能,支持关联委托方/项目
- 实现事故消息的增删改查及富文本编辑
- 添加城市级联选择组件
- 优化表单数据处理和错误提示
- 更新API接口调用逻辑
| New file |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | export function getAccountList(data, params) { |
| | | return request({ |
| | | url: `/admin/screen-account/list`, |
| | | method: 'post', |
| | | data, |
| | | params |
| | | }) |
| | | } |
| | | |
| | | export function addAccount(data) { |
| | | return request({ |
| | | url: '/admin/screen-account/add', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export function editAccount(data) { |
| | | return request({ |
| | | url: '/admin/screen-account/edit', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export function deleteAccount(id) { |
| | | return request({ |
| | | url: `/admin/screen-account/delete/${id}`, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | export function resetPassword(data) { |
| | | return request({ |
| | | url: '/admin/screen-account/resetPassword', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export function enableAccount(id) { |
| | | return request({ |
| | | url: `/admin/screen-account/enable/${id}`, |
| | | method: 'post' |
| | | }) |
| | | } |
| | | |
| | | export function getClientOptions() { |
| | | return request({ |
| | | url: '/admin/entity/clients', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | export function getAccidentClassificationList(data, params) { |
| | | return request({ |
| | | url: '/admin/accident-classification/list', |
| | | method: 'post', |
| | | data, |
| | | params |
| | | }) |
| | | } |
| | | |
| | | export function addAccidentClassification(data) { |
| | | return request({ |
| | | url: '/admin/accident-classification/add', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export function editAccidentClassification(data) { |
| | | return request({ |
| | | url: '/admin/accident-classification/edit', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export function deleteAccidentClassification(id) { |
| | | return request({ |
| | | url: `/admin/accident-classification/delete/${id}`, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | export function getAccidentBriefList(data, params) { |
| | | return request({ |
| | | url: '/admin/accident-brief/list', |
| | | method: 'post', |
| | | data, |
| | | params |
| | | }) |
| | | } |
| | | |
| | | export function addAccidentBrief(data) { |
| | | return request({ |
| | | url: '/admin/accident-brief/add', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export function editAccidentBrief(data) { |
| | | return request({ |
| | | url: '/admin/accident-brief/edit', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export function deleteAccidentBrief(id) { |
| | | return request({ |
| | | url: `/admin/accident-brief/delete/${id}`, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | export function getAccidentBriefDetail(id) { |
| | | return request({ |
| | | url: `/admin/accident-brief/detail/${id}`, |
| | | method: 'get' |
| | | }) |
| | | } |
| | |
| | | // 定义全局变量 |
| | | |
| | | // export const BASE_URL = 'http://47.93.28.249:8181' |
| | | export const BASE_URL = 'http://221.182.45.100:8080'; |
| | | export const BASE_URL = 'http://221.182.45.100:8180'; |
| | | export const SCREEN_URL = 'http://221.182.45.100:8087/#/ '; |
| | | |
| | | // export const BASE_URL = 'http://localhost:8080'; |
| | |
| | | .then(res => { |
| | | if (res.data.status === 'OK') { |
| | | this.setState({ |
| | | entities: res.data.data.list, |
| | | totalElements: res.data.data.totalElements |
| | | entities: res.data.data.list || res.data.data.records, |
| | | totalElements: res.data.data.totalElements || res.data.data.total |
| | | }) |
| | | } else { |
| | | message.error("获取数据失败" + res.data.errorMsg) |
| | |
| | | |
| | | // 处理删除实体 |
| | | handleDeleteEntity = (rows) => { |
| | | |
| | | // 如果传入的是单个对象,将其放入数组中 |
| | | if (!Array.isArray(rows)) { |
| | | rows = [rows]; |
| | | } |
| | | |
| | | const ids = rows.map(row => row.id); |
| | | console.log(ids); |
| | | |
| | | this.deleteEntities({ |
| | | ids: ids |
| | | }).then(res => { |
| | |
| | | if (err) { |
| | | return; |
| | | } |
| | | console.log(this.editFormRef.state) |
| | | |
| | | this.setState({ editModalLoading: true, }); |
| | | let formState = this.editFormRef.state |
| | | let action = formState.isEdit ? '编辑' : '添加'; |
| | | this.editEntity(formState.editableData, formState.isEdit).then((response) => { |
| | | console.log(response); |
| | | |
| | | if (response.data.status === 'OK') { |
| | | form.resetFields(); |
| | | message.success(action + "成功!") |
| | |
| | | this.reloadEntities() |
| | | } else { |
| | | this.setState({ editModalLoading: false }); |
| | | message.success(response.data.errorMsg || action + "失败,请重试") |
| | | message.error(response.data.errorMsg || action + "失败,请重试") |
| | | } |
| | | }).catch(e => { |
| | | message.success(action + "失败,请重试! " + e.message) |
| | | message.error(action + "失败,请重试! " + e.message) |
| | | }) |
| | | }); |
| | | }; |
| | |
| | | import React from "react"; |
| | | import { Card, Divider, Icon, Input, Select, Table } from "antd"; |
| | | import { deleteDevice, editDevice, getDevicesList } from "@/api/devices"; |
| | | import EditAccountMessageForm from "./forms/edit-accidentMessage-form"; |
| | | import { |
| | | getAccidentBriefList, |
| | | addAccidentBrief, |
| | | editAccidentBrief, |
| | | deleteAccidentBrief |
| | | } from "@/api/screenSetting"; |
| | | import EditAccidentMessageForm from "./forms/edit-accidentMessage-form"; |
| | | import "./style/index.less"; |
| | | import CrudViewBase from "../common/crud-view-base"; |
| | | |
| | | const { Column } = Table; |
| | | const { Option } = Select; |
| | | const inputLongStyle = { |
| | | padding: "1px 10px", |
| | | width: "250px", |
| | | }; |
| | | |
| | | class AccidentMessageSetting extends CrudViewBase { |
| | | state = { |
| | | ...this.state, |
| | | entities: [], |
| | | }; |
| | | |
| | | initListBody() { |
| | | return { |
| | | name: "", |
| | | keyword: "", |
| | | deviceType: "", |
| | | title: "", |
| | | }; |
| | | } |
| | | |
| | | // 重写处理listBody变化的方法,当deviceType变化时加载表单 |
| | | handleListBodyChange = (key, value) => { |
| | | this.setState( |
| | | (prevState) => { |
| | | const newListBody = { ...prevState.listBody, [key]: value }; |
| | | |
| | | return { listBody: newListBody }; |
| | | }, |
| | | () => { |
| | | this.reloadEntities(); |
| | | } |
| | | ); |
| | | }; |
| | | |
| | | getEntityList(pageInfo, listBody) { |
| | | return getDevicesList(pageInfo, listBody); |
| | | const params = { |
| | | title: listBody.title |
| | | }; |
| | | return getAccidentBriefList(pageInfo, params); |
| | | } |
| | | |
| | | editEntity(data, isEdit) { |
| | | return editDevice(data, isEdit); |
| | | // 数据清洗与转换 |
| | | // 1. 处理级联选择结果 |
| | | if (data.cascaderValue && data.cascaderValue.length > 0) { |
| | | data.province = data.cascaderValue[0] || ''; |
| | | data.city = data.cascaderValue[1] || ''; |
| | | data.area = data.cascaderValue[2] || ''; |
| | | } |
| | | |
| | | if (this.editFormRef && this.editFormRef.state.editableData) { |
| | | data.img = this.editFormRef.state.editableData.img.name; |
| | | } |
| | | |
| | | // 3. 删除多余字段 |
| | | const allowedFields = [ |
| | | 'title', 'classifyId', 'area', 'city', 'province', |
| | | 'casualties', 'time', 'content', 'detail', 'img', 'state' |
| | | ]; |
| | | |
| | | // 如果是编辑,保留 id |
| | | if (isEdit) { |
| | | data.id = this.state.currentRowData.id; |
| | | allowedFields.push('id'); |
| | | } |
| | | |
| | | const payload = {}; |
| | | allowedFields.forEach(field => { |
| | | if (data[field] !== undefined) { |
| | | payload[field] = data[field]; |
| | | } |
| | | }); |
| | | |
| | | if (isEdit) { |
| | | return editAccidentBrief(payload); |
| | | } else { |
| | | return addAccidentBrief(payload); |
| | | } |
| | | } |
| | | |
| | | deleteEntities(ids) { |
| | | return deleteDevice(ids); |
| | | return deleteAccidentBrief(ids.ids[0]); |
| | | } |
| | | |
| | | // 处理重置密码实体确认 |
| | | handleResetPasswordEntityOk = (_) => { |
| | | const { form } = this.resetPasswordFormRef.props; |
| | | form.validateFields((err) => { |
| | | if (err) { |
| | | return; |
| | | } |
| | | |
| | | this.setState({ resetPasswordModalLoading: true }); |
| | | let formState = this.resetPasswordFormRef.state; |
| | | console.log(formState.editableData); |
| | | // this.editEntity(formState.editableData, formState.isEdit) |
| | | // .then((response) => { |
| | | // if (response.data.status === "OK") { |
| | | // form.resetFields(); |
| | | // message.success(action + "成功!"); |
| | | // this.setState({ |
| | | // resetPasswordModalVisible: false, |
| | | // resetPasswordModalLoading: false, |
| | | // }); |
| | | // this.onResetPasswordSuccess(); |
| | | // this.reloadEntities(); |
| | | // } else { |
| | | // this.setState({ resetPasswordModalLoading: false }); |
| | | // message.success(response.data.errorMsg || action + "失败,请重试"); |
| | | // } |
| | | // }) |
| | | // .catch((e) => { |
| | | // message.success(action + "失败,请重试! " + e.message); |
| | | // }); |
| | | }); |
| | | }; |
| | | |
| | | render() { |
| | | const { entities } = this.state; |
| | |
| | | <div> |
| | | <Input |
| | | style={inputLongStyle} |
| | | placeholder="请输入姓名" |
| | | placeholder="请输入标题" |
| | | value={this.state.listBody ? this.state.listBody.title : ""} |
| | | onChange={(e) => { |
| | | this.handleListBodyChange("keyword", e.target.value); |
| | | this.handleListBodyChange("title", e.target.value); |
| | | }} |
| | | prefix={<Icon style={{ marginLeft: "4px" }} type="search" />} |
| | | allowClear |
| | | /> |
| | | |
| | | <Select |
| | | style={{ |
| | | width: 250, |
| | | }} |
| | | placeholder="账号级别" |
| | | onChange={(value) => { |
| | | this.handleListBodyChange("deviceType", value); |
| | | }} |
| | | > |
| | | <Option value={1}>委托方</Option> |
| | | <Option value={2}>项目</Option> |
| | | </Select> |
| | | |
| | | </div> |
| | | |
| | | <div className="right">{this.renderAddButton("device:add")}</div> |
| | |
| | | > |
| | | <Column |
| | | title="序号" |
| | | width={125} |
| | | width={80} |
| | | dataIndex="id" |
| | | key="id" |
| | | align="center" |
| | | /> |
| | | <Column |
| | | title="标题" |
| | | width={125} |
| | | dataIndex="name" |
| | | key="name" |
| | | width={150} |
| | | dataIndex="title" |
| | | key="title" |
| | | align="center" |
| | | /> |
| | | <Column |
| | | title="事故类型" |
| | | width={125} |
| | | dataIndex="account" |
| | | key="account" |
| | | width={120} |
| | | dataIndex="classifyId" // 暂时显示ID,或者需要后端返回名称 |
| | | key="classifyId" |
| | | align="center" |
| | | /> |
| | | <Column |
| | | title="发生地点" |
| | | width={125} |
| | | dataIndex="accountType" |
| | | key="accountType" |
| | | width={200} |
| | | key="location" |
| | | align="center" |
| | | render={(text, row) => ( |
| | | <span>{`${row.province || ''}${row.city || ''}${row.area || ''}`}</span> |
| | | )} |
| | | /> |
| | | <Column |
| | | title="伤亡情况" |
| | | width={125} |
| | | width={150} |
| | | dataIndex="casualties" |
| | | key="casualties" |
| | | align="center" |
| | | /> |
| | | <Column |
| | | title="发生时间" |
| | | dataIndex="date" |
| | | key="date" |
| | | dataIndex="time" |
| | | key="time" |
| | | align="center" |
| | | width={150} |
| | | /> |
| | | <Column |
| | | title="状态" |
| | | key="state" |
| | | align="center" |
| | | width={100} |
| | | render={(text, row) => ( |
| | | <span style={{ color: row.state ? "yellow" : "inherit" }}> |
| | | {row.state ? "启用" : "禁用"} |
| | | <span style={{ color: row.state === 1 ? "green" : "red" }}> |
| | | {row.state === 1 ? "启用" : "禁用"} |
| | | </span> |
| | | )} |
| | | /> |
| | | <Column |
| | | title="操作" |
| | | key="action" |
| | | width={260} |
| | | fixed="right" |
| | | width={200} |
| | | align="center" |
| | | render={(text, row) => ( |
| | | <span> |
| | | {this.renderEnableDisableButton(row, "device:edit")} |
| | | <Divider type="vertical" /> |
| | | {this.renderEditButton(row, "device:edit")} |
| | | <Divider type="vertical" /> |
| | | {this.renderDeleteButton(row, "device:delete")} |
| | |
| | | <br /> |
| | | {this.renderPagination()} |
| | | </Card> |
| | | <EditAccountMessageForm |
| | | <EditAccidentMessageForm |
| | | currentRowData={this.state.currentRowData} |
| | | wrappedComponentRef={(formRef) => (this.editFormRef = formRef)} |
| | | visible={this.state.editModalVisible} |
| | |
| | | import React from "react"; |
| | | import { Card, Divider, Icon, Input, Table } from "antd"; |
| | | import { deleteDevice, editDevice, getDevicesList } from "@/api/devices"; |
| | | import { |
| | | getAccidentClassificationList, |
| | | addAccidentClassification, |
| | | editAccidentClassification, |
| | | deleteAccidentClassification |
| | | } from "@/api/screenSetting"; |
| | | import EditAccidentTypeForm from "./forms/edit-accidentType-form"; |
| | | import "./style/index.less"; |
| | | import CrudViewBase from "../common/crud-view-base"; |
| | |
| | | }; |
| | | |
| | | class AccidentTypeSetting extends CrudViewBase { |
| | | state = { |
| | | ...this.state, |
| | | entities: [], |
| | | }; |
| | | |
| | | initListBody() { |
| | | return { |
| | | keyword: "", |
| | | deviceType: "", |
| | | name: "", |
| | | }; |
| | | } |
| | | |
| | | // 重写处理listBody变化的方法,当deviceType变化时加载表单 |
| | | handleListBodyChange = (key, value) => { |
| | | this.setState( |
| | | (prevState) => { |
| | | const newListBody = { ...prevState.listBody, [key]: value }; |
| | | |
| | | return { listBody: newListBody }; |
| | | }, |
| | | () => { |
| | | this.reloadEntities(); |
| | | } |
| | | ); |
| | | }; |
| | | |
| | | getEntityList(pageInfo, listBody) { |
| | | return getDevicesList(pageInfo, listBody); |
| | | // 接口要求:body传分页(page, size),query传name |
| | | // listBody 包含 name |
| | | const params = { |
| | | name: listBody.name |
| | | }; |
| | | return getAccidentClassificationList(pageInfo, params); |
| | | } |
| | | |
| | | editEntity(data, isEdit) { |
| | | return editDevice(data, isEdit); |
| | | if (isEdit) { |
| | | return editAccidentClassification(data); |
| | | } else { |
| | | return addAccidentClassification(data); |
| | | } |
| | | } |
| | | |
| | | deleteEntities(ids) { |
| | | return deleteDevice(ids); |
| | | return deleteAccidentClassification(ids.ids[0]); |
| | | } |
| | | |
| | | render() { |
| | |
| | | <Input |
| | | style={inputLongStyle} |
| | | placeholder="请输入分类" |
| | | value={this.state.listBody ? this.state.listBody.name : ""} |
| | | onChange={(e) => { |
| | | this.handleListBodyChange("keyword", e.target.value); |
| | | this.handleListBodyChange("name", e.target.value); |
| | | }} |
| | | prefix={<Icon style={{ marginLeft: "4px" }} type="search" />} |
| | | allowClear |
| | |
| | | dataIndex="id" |
| | | key="id" |
| | | align="center" |
| | | width={80} |
| | | /> |
| | | <Column |
| | | title="事故类型" |
| | |
| | | <Column |
| | | title="操作" |
| | | key="action" |
| | | width={360} |
| | | width={200} |
| | | align="center" |
| | | render={(text, row) => ( |
| | | <span> |
| | | {this.renderEditButton(row, "device:edit")} |
| | | {this.renderEditButton(row, "device:add")} |
| | | <Divider type="vertical" /> |
| | | {this.renderDeleteButton(row, "device:delete")} |
| | | {this.renderDeleteButton(row, "device:add")} |
| | | </span> |
| | | )} |
| | | /> |
| | |
| | | import React from "react"; |
| | | import { Card, Divider, Icon, Input, Select, Table } from "antd"; |
| | | import { Card, Divider, Icon, Input, Select, Table, message, Modal } from "antd"; |
| | | import { |
| | | deleteDevice, |
| | | editDevice, |
| | | getDevicesList, |
| | | loadDeviceForm, |
| | | } from "@/api/devices"; |
| | | deleteAccount, |
| | | editAccount, |
| | | addAccount, |
| | | getAccountList, |
| | | resetPassword, |
| | | enableAccount |
| | | } from "@/api/screenSetting"; |
| | | import EditAccountForm from "./forms/edit-account-form"; |
| | | import ResetPasswordForm from "./forms/reset-password-form"; |
| | | |
| | |
| | | }; |
| | | |
| | | class AccountSetting extends CrudViewBase { |
| | | state = { |
| | | ...this.state, |
| | | entities: [], |
| | | }; |
| | | |
| | | // initListBody 实现基类抽象方法 |
| | | initListBody() { |
| | | return { |
| | | name: "", |
| | | keyword: "", |
| | | deviceType: "", |
| | | type: undefined, |
| | | }; |
| | | } |
| | | |
| | | // 重写处理listBody变化的方法,当deviceType变化时加载表单 |
| | | handleListBodyChange = (key, value) => { |
| | | this.setState( |
| | | (prevState) => { |
| | | const newListBody = { ...prevState.listBody, [key]: value }; |
| | | |
| | | return { listBody: newListBody }; |
| | | }, |
| | | () => { |
| | | this.reloadEntities(); |
| | | } |
| | | ); |
| | | }; |
| | | |
| | | // getEntityList 实现基类抽象方法 |
| | | getEntityList(pageInfo, listBody) { |
| | | return getDevicesList(pageInfo, listBody); |
| | | return getAccountList(pageInfo, listBody); |
| | | } |
| | | |
| | | // editEntity 实现基类抽象方法 |
| | | editEntity(data, isEdit) { |
| | | return editDevice(data, isEdit); |
| | | const submitData = { ...data }; |
| | | |
| | | if (Array.isArray(submitData.clientProjectIds)) { |
| | | submitData.clientProjectIds = submitData.clientProjectIds.join(','); |
| | | } |
| | | |
| | | submitData.state = submitData.state ? 1 : 2; |
| | | |
| | | if (isEdit) { |
| | | return editAccount(submitData); |
| | | } else { |
| | | return addAccount(submitData); |
| | | } |
| | | } |
| | | |
| | | |
| | | deleteEntities(ids) { |
| | | return deleteDevice(ids); |
| | | return deleteAccount(ids.ids[0]) |
| | | } |
| | | |
| | | // 处理重置密码实体确认 |
| | | |
| | | // 自定义方法:启用/禁用 |
| | | handleEnableDisableEntity = (row) => { |
| | | // 增加一个二次确认 |
| | | Modal.confirm({ |
| | | title: "确认操作", |
| | | content: `确定${row.state === 1 ? "禁用" : "启用"}账号 ${row.name}吗?`, |
| | | okText: "确认", |
| | | onOk: () => { |
| | | enableAccount(row.id).then(res => { |
| | | if (res && res.data && (res.data.status === 'OK' || res.data.code === 200)) { |
| | | message.success("操作成功"); |
| | | this.reloadEntities(); |
| | | } else { |
| | | message.error((res && res.data && res.data.errorMsg) || "操作失败"); |
| | | } |
| | | }).catch(e => { |
| | | message.error("操作失败: " + e.message); |
| | | }) |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 自定义方法:重置密码确认 |
| | | handleResetPasswordEntityOk = (_) => { |
| | | const { form } = this.resetPasswordFormRef.props; |
| | | form.validateFields((err) => { |
| | | form.validateFields((err, values) => { |
| | | if (err) { |
| | | return; |
| | | } |
| | | |
| | | this.setState({ resetPasswordModalLoading: true }); |
| | | let formState = this.resetPasswordFormRef.state; |
| | | console.log(formState.editableData); |
| | | // this.editEntity(formState.editableData, formState.isEdit) |
| | | // .then((response) => { |
| | | // if (response.data.status === "OK") { |
| | | // form.resetFields(); |
| | | // message.success(action + "成功!"); |
| | | // this.setState({ |
| | | // resetPasswordModalVisible: false, |
| | | // resetPasswordModalLoading: false, |
| | | // }); |
| | | // this.onResetPasswordSuccess(); |
| | | // this.reloadEntities(); |
| | | // } else { |
| | | // this.setState({ resetPasswordModalLoading: false }); |
| | | // message.success(response.data.errorMsg || action + "失败,请重试"); |
| | | // } |
| | | // }) |
| | | // .catch((e) => { |
| | | // message.success(action + "失败,请重试! " + e.message); |
| | | // }); |
| | | |
| | | const payload = { |
| | | id: this.state.currentRowData.id, |
| | | password: values.password |
| | | }; |
| | | |
| | | resetPassword(payload) |
| | | .then((response) => { |
| | | if (response && response.data && (response.data.status === 'OK' || response.data.code === 200)) { |
| | | form.resetFields(); |
| | | message.success("重置密码成功!"); |
| | | this.setState({ |
| | | resetPasswordModalVisible: false, |
| | | resetPasswordModalLoading: false, |
| | | }); |
| | | } else { |
| | | this.setState({ resetPasswordModalLoading: false }); |
| | | message.error((response && response.data && response.data.errorMsg) || "重置密码失败"); |
| | | } |
| | | }) |
| | | .catch((e) => { |
| | | this.setState({ resetPasswordModalLoading: false }); |
| | | message.error("重置密码失败: " + e.message); |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | |
| | | style={inputLongStyle} |
| | | placeholder="请输入姓名" |
| | | onChange={(e) => { |
| | | this.handleListBodyChange("keyword", e.target.value); |
| | | this.handleListBodyChange("name", e.target.value); |
| | | }} |
| | | prefix={<Icon style={{ marginLeft: "4px" }} type="search" />} |
| | | allowClear |
| | |
| | | |
| | | <Input |
| | | style={inputLongStyle} |
| | | placeholder="请输入委托方/项目名称" |
| | | placeholder="请输入关键字" |
| | | onChange={(e) => { |
| | | this.handleListBodyChange("keyword", e.target.value); |
| | | }} |
| | |
| | | }} |
| | | placeholder="账号级别" |
| | | onChange={(value) => { |
| | | this.handleListBodyChange("deviceType", value); |
| | | this.handleListBodyChange("type", value); |
| | | }} |
| | | allowClear |
| | | > |
| | | <Option value={1}>委托方</Option> |
| | | <Option value={2}>项目</Option> |
| | |
| | | > |
| | | <Column |
| | | title="序号" |
| | | width={125} |
| | | width={80} |
| | | dataIndex="id" |
| | | key="id" |
| | | align="center" |
| | |
| | | <Column |
| | | title="账号级别" |
| | | width={125} |
| | | dataIndex="accountType" |
| | | key="accountType" |
| | | dataIndex="type" |
| | | key="type" |
| | | align="center" |
| | | render={(text, row) => ( |
| | | <span>{row.accountType === 1 ? "委托方" : "项目"}</span> |
| | | <span>{row.type === 1 ? "委托方" : "项目"}</span> |
| | | )} |
| | | /> |
| | | <Column title="关联委托方/项目" key="type" align="center" /> |
| | | <Column |
| | | title="关联委托方/项目" |
| | | key="clientOrProject" |
| | | dataIndex="clientOrProject" |
| | | align="center" |
| | | /> |
| | | <Column |
| | | title="状态" |
| | | key="state" |
| | | align="center" |
| | | render={(text, row) => ( |
| | | <span style={{ color: row.state ? "yellow" : "inherit" }}> |
| | | {row.state ? "启用" : "禁用"} |
| | | <span style={{ color: row.state === 1 ? "green" : "red" }}> |
| | | {row.state === 1 ? "启用" : "禁用"} |
| | | </span> |
| | | )} |
| | | /> |
| | |
| | | align="center" |
| | | render={(text, row) => ( |
| | | <span> |
| | | {this.renderEnableDisableButton(row, "device:edit")} |
| | | {this.renderEnableDisableButton(row)} |
| | | <Divider type="vertical" /> |
| | | {this.renderResetPasswordButton(row, "device:delete")} |
| | | {this.renderResetPasswordButton(row, "device:edit")} |
| | | <Divider type="vertical" /> |
| | | {this.renderEditButton(row, "device:edit")} |
| | | <Divider type="vertical" /> |
| | |
| | | } |
| | | } |
| | | |
| | | export default AccountSetting; |
| | | export default AccountSetting; |
| | |
| | | Switch, |
| | | Upload, |
| | | Icon, |
| | | message |
| | | } from "antd"; |
| | | import "../style/index.less"; |
| | | import moment from "moment/moment"; |
| | | import moment from "moment"; |
| | | import { checkFileSize } from "../../common/common_func"; |
| | | import EditFormBase from "../../common/edit-form-base"; |
| | | import { inputLongStyle } from "../../common/constants"; |
| | | import { EditorState, convertToRaw } from "draft-js"; |
| | | import { EditorState, convertToRaw, ContentState } from "draft-js"; |
| | | import { Editor } from "react-draft-wysiwyg"; |
| | | import draftToHtml from "draftjs-to-html"; |
| | | import htmlToDraft from "html-to-draftjs"; |
| | | import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css"; |
| | | import { getAccidentClassificationList } from "@/api/screenSetting"; |
| | | |
| | | const { Option } = Select; |
| | | |
| | |
| | | }); |
| | | } |
| | | |
| | | class EditAccountMessageForm extends EditFormBase { |
| | | class EditAccidentMessageForm extends EditFormBase { |
| | | state = { |
| | | ...this.state, |
| | | classificationOptions: [], |
| | | cityOptions: [], |
| | | }; |
| | | |
| | | initView() { |
| | | this.loadClassificationOptions(); |
| | | this.loadCityOptions(); |
| | | } |
| | | |
| | | loadClassificationOptions = () => { |
| | | getAccidentClassificationList({ page: 0, size: 9999 }, {}).then(res => { |
| | | if (res && res.data && res.data.data && res.data.data.records) { |
| | | this.setState({ classificationOptions: res.data.data.records }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | loadCityOptions = async () => { |
| | | // 初始加载全国省份 |
| | | const provinces = await this.getDistrictData(); |
| | | this.setState({ cityOptions: provinces }); |
| | | }; |
| | | |
| | | getDistrictData = (keyword = "", level = 0) => { |
| | | // subdistrict=1 只获取下一级 |
| | | const url = `https://restapi.amap.com/v3/config/district?keywords=${keyword}&subdistrict=1&key=aeec2a4b436322753dd2458fde89506b`; |
| | | return fetch(url) |
| | | .then(response => response.json()) |
| | | .then(data => { |
| | | if (data.status === "1" && data.districts && data.districts.length > 0) { |
| | | // 如果是空关键词查询全国,districts[0] 是中国,取其 children |
| | | // 如果是具体关键词(如山东省),districts[0] 是山东省,取其 children |
| | | const root = data.districts[0]; |
| | | const list = root.districts || []; |
| | | |
| | | return list.map(node => ({ |
| | | value: node.name, |
| | | label: node.name, |
| | | isLeaf: node.level === "district", // 区县级为叶子节点 |
| | | level: node.level |
| | | })); |
| | | } |
| | | return []; |
| | | }) |
| | | .catch(e => { |
| | | console.error("加载城市数据失败", e); |
| | | return []; |
| | | }); |
| | | }; |
| | | |
| | | onLoadData = async (selectedOptions) => { |
| | | const targetOption = selectedOptions[selectedOptions.length - 1]; |
| | | targetOption.loading = true; |
| | | |
| | | const children = await this.getDistrictData(targetOption.label); |
| | | |
| | | targetOption.loading = false; |
| | | if (children && children.length > 0) { |
| | | targetOption.children = children; |
| | | } else { |
| | | targetOption.isLeaf = true; |
| | | } |
| | | |
| | | this.setState({ |
| | | cityOptions: [...this.state.cityOptions], |
| | | }); |
| | | }; |
| | | |
| | | initEditableData() { |
| | | return { |
| | | title: "", |
| | | type: 1, |
| | | casualties: "", |
| | | description: "", |
| | | date: null, |
| | | detail: "", |
| | | status: 1, |
| | | classifyId: undefined, |
| | | area: "", |
| | | city: "", |
| | | province: "", |
| | | casualties: "", |
| | | time: null, |
| | | content: "", |
| | | detail: "", |
| | | img: "", |
| | | state: 1, |
| | | cascaderValue: [], |
| | | file: [], |
| | | editorState: EditorState.createEmpty(), |
| | | previewVisible: false, |
| | | previewImage: "", |
| | | editorState: EditorState.createEmpty(), |
| | | }; |
| | | } |
| | | |
| | | async ensureCascaderData(province, city) { |
| | | let provinces = this.state.cityOptions; |
| | | // 如果省份数据还没加载,先加载 |
| | | if (!provinces || provinces.length === 0) { |
| | | provinces = await this.getDistrictData(); |
| | | } |
| | | |
| | | // 找到对应省份节点 |
| | | const provinceNode = provinces.find(p => p.value === province); |
| | | if (provinceNode) { |
| | | // 如果该省份没有子节点(市),加载市 |
| | | if (!provinceNode.children || provinceNode.children.length === 0) { |
| | | const cities = await this.getDistrictData(province); |
| | | provinceNode.children = cities; |
| | | } |
| | | |
| | | // 如果有市,继续加载区 |
| | | if (city) { |
| | | const cityNode = provinceNode.children.find(c => c.value === city); |
| | | if (cityNode) { |
| | | if (!cityNode.children || cityNode.children.length === 0) { |
| | | const areas = await this.getDistrictData(city); |
| | | cityNode.children = areas; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | this.setState({ cityOptions: [...provinces] }); |
| | | } |
| | | |
| | | viewWillAppear() { |
| | | const { detail, img, province, city, area } = this.state.editableData; |
| | | |
| | | let editorState = EditorState.createEmpty(); |
| | | if (detail) { |
| | | const contentBlock = htmlToDraft(detail); |
| | | if (contentBlock) { |
| | | const contentState = ContentState.createFromBlockArray(contentBlock.contentBlocks); |
| | | editorState = EditorState.createWithContent(contentState); |
| | | } |
| | | } |
| | | |
| | | let file = []; |
| | | if (img) { |
| | | file = [{ |
| | | uid: '-1', |
| | | name: 'image.png', |
| | | status: 'done', |
| | | url: img |
| | | }]; |
| | | } |
| | | |
| | | let cascaderValue = []; |
| | | if (province) cascaderValue.push(province); |
| | | if (city) cascaderValue.push(city); |
| | | if (area) cascaderValue.push(area); |
| | | |
| | | // 预加载级联数据以支持回显 |
| | | if (province) { |
| | | this.ensureCascaderData(province, city); |
| | | } |
| | | |
| | | this.setState({ |
| | | editableData: { |
| | | ...this.state.editableData, |
| | | editorState, |
| | | file, |
| | | cascaderValue |
| | | } |
| | | }); |
| | | } |
| | | |
| | | onEditorStateChange = (editorState) => { |
| | |
| | | }); |
| | | }; |
| | | |
| | | handleCascaderChange = (value) => { |
| | | this.setFieldValue("cascaderValue", value); |
| | | this.setFieldValue("province", value[0] || ""); |
| | | this.setFieldValue("city", value[1] || ""); |
| | | this.setFieldValue("area", value[2] || ""); |
| | | } |
| | | |
| | | handleUploadChange = ({ file, fileList }) => { |
| | | this.setFieldValue("file", fileList); |
| | | // 如果没有文件了,清空 img |
| | | if (fileList.length === 0) { |
| | | this.setFieldValue("img", ""); |
| | | } |
| | | } |
| | | |
| | | render() { |
| | | const { visible, onCancel, onOk, form } = this.props; |
| | | const { getFieldDecorator } = form; |
| | | const { |
| | | title, |
| | | type, |
| | | classifyId, |
| | | casualties, |
| | | description, |
| | | date, |
| | | content, |
| | | time, |
| | | detail, |
| | | status, |
| | | city, |
| | | state, |
| | | file, |
| | | editorState, |
| | | previewVisible, |
| | | previewImage, |
| | | cascaderValue |
| | | } = this.state.editableData; |
| | | |
| | | const uploadButton = ( |
| | | <div> |
| | | <Icon type="plus" /> |
| | | <div className="ant-upload-text">上传</div> |
| | | </div> |
| | | ); |
| | | |
| | |
| | | onChange: this.handleFieldChange("title"), |
| | | })(<Input style={inputLongStyle} placeholder="请输入标题" />)} |
| | | </Form.Item> |
| | | |
| | | <Form.Item label="类别"> |
| | | {getFieldDecorator("type", { |
| | | {getFieldDecorator("classifyId", { |
| | | rules: [{ required: true, message: "请选择类别!" }], |
| | | initialValue: type, |
| | | onChange: this.handleFieldChange("type"), |
| | | initialValue: classifyId, |
| | | onChange: this.handleFieldChange("classifyId"), |
| | | })( |
| | | <Select style={inputLongStyle} placeholder="请选择关联委托方"> |
| | | <Option value={1}>委托方1</Option> |
| | | <Option value={2}>委托方2</Option> |
| | | <Select style={inputLongStyle} placeholder="请选择类别"> |
| | | {this.state.classificationOptions.map(option => ( |
| | | <Option key={option.id} value={option.id}>{option.name}</Option> |
| | | ))} |
| | | </Select>, |
| | | )} |
| | | </Form.Item> |
| | | |
| | | <Form.Item label="发生城市"> |
| | | {getFieldDecorator("city", { |
| | | initialValue: city, |
| | | onChange: this.handleFieldChange("city"), |
| | | {getFieldDecorator("cascaderValue", { |
| | | initialValue: cascaderValue, |
| | | onChange: this.handleCascaderChange, |
| | | rules: [{ required: true, message: "请选择发生城市!" }], |
| | | })( |
| | | <Cascader |
| | | style={{ width: "250px" }} |
| | | options={[ |
| | | { |
| | | value: "1", |
| | | label: "北京", |
| | | }, |
| | | { |
| | | value: "2", |
| | | label: "上海", |
| | | }, |
| | | ]} |
| | | expandTrigger="hover" |
| | | />, |
| | | options={this.state.cityOptions} |
| | | loadData={this.onLoadData} |
| | | changeOnSelect |
| | | placeholder="请选择发生城市" |
| | | style={inputLongStyle} |
| | | /> |
| | | )} |
| | | </Form.Item> |
| | | <Form.Item label="造成伤亡情况"> |
| | | |
| | | <Form.Item label="伤亡情况"> |
| | | {getFieldDecorator("casualties", { |
| | | rules: [{ required: true, message: "请选择启用状态!" }], |
| | | rules: [{ required: true, message: "请输入伤亡情况!" }], |
| | | initialValue: casualties, |
| | | onChange: this.handleFieldChange("casualties"), |
| | | })( |
| | | <Input |
| | | style={inputLongStyle} |
| | | placeholder="请输入造成伤亡情况" |
| | | placeholder="请输入伤亡情况" |
| | | />, |
| | | )} |
| | | </Form.Item> |
| | | <Form.Item label="封面图"> |
| | | |
| | | <Form.Item label="图片"> |
| | | {getFieldDecorator("file", { |
| | | valuePropName: "file", |
| | | // getValueFromEvent: singleFile, |
| | | initialValue: file, |
| | | rules: [{ required: true, message: "请上传封面图" }], |
| | | rules: [{ required: true, message: "请上传图片!" }], |
| | | })( |
| | | <div> |
| | | <Upload |
| | | disabled={this.uploading} |
| | | name={"file"} |
| | | name="file" |
| | | maxCount={1} |
| | | listType="picture-card" |
| | | multiple={false} |
| | | customRequest={this.uploadAttachment.bind(null, "file")} |
| | | onRemove={() => { |
| | | this.setFieldValue("file", []); |
| | | }} |
| | | showUploadList={true} |
| | | customRequest={this.uploadAttachment.bind(this, "img")} |
| | | onChange={this.handleUploadChange} |
| | | onPreview={this.handlePreview} |
| | | beforeUpload={checkFileSize} |
| | | accept="image/*" |
| | | fileList={file} |
| | | > |
| | | {(!file || file.length === 0) ? uploadButton : null} |
| | | </Upload> |
| | |
| | | </div>, |
| | | )} |
| | | </Form.Item> |
| | | |
| | | <Form.Item label="情况简述"> |
| | | {getFieldDecorator("description", { |
| | | {getFieldDecorator("content", { |
| | | rules: [{ required: true, message: "请输入情况简述!" }], |
| | | initialValue: description, |
| | | onChange: this.handleFieldChange("description"), |
| | | initialValue: content, |
| | | onChange: this.handleFieldChange("content"), |
| | | })(<Input style={inputLongStyle} placeholder="请输入情况简述" />)} |
| | | </Form.Item> |
| | | |
| | | <Form.Item label="发生时间"> |
| | | {getFieldDecorator("date", { |
| | | rules: [{ required: true, message: "请输入日期" }], |
| | | initialValue: date ? moment(date) : null, |
| | | onChange: this.handleDateFieldChange("date"), |
| | | {getFieldDecorator("time", { |
| | | rules: [{ required: true, message: "请输入发生时间" }], |
| | | initialValue: time ? moment(time) : null, |
| | | onChange: this.handleDateFieldChange("time"), |
| | | })( |
| | | <DatePicker style={{ width: "250px" }} format={"YYYY-MM-DD"} />, |
| | | <DatePicker showTime style={{ width: "250px" }} format={"YYYY-MM-DD HH:mm:ss"} />, |
| | | )} |
| | | </Form.Item> |
| | | |
| | | <Form.Item label="启用状态"> |
| | | {getFieldDecorator("state", { |
| | | rules: [{ required: true, message: "请选择启用状态!" }], |
| | | initialValue: state, |
| | | onChange: (value) => this.setFieldValue("state", value ? 1 : 2), // Switch返回bool,后端要1/2 |
| | | })(<Switch checked={state === 1} />)} |
| | | </Form.Item> |
| | | </div> |
| | | |
| | | <Form.Item label="详情"> |
| | | {getFieldDecorator("detail", { |
| | | rules: [{ required: true, message: "请输入详情" }], |
| | | initialValue: detail, |
| | | onChange: this.handleFieldChange("detail"), |
| | | // onChange 由 onEditorStateChange 处理 |
| | | })( |
| | | <div style={{ marginTop: 10 }}> |
| | | <Editor |
| | |
| | | </div>, |
| | | )} |
| | | </Form.Item> |
| | | <Form.Item label="启用状态"> |
| | | {getFieldDecorator("status", { |
| | | rules: [{ required: true, message: "请选择启用状态!" }], |
| | | initialValue: status, |
| | | onChange: this.handleFieldChange("status"), |
| | | })(<Switch checked={status === 1} />)} |
| | | </Form.Item> |
| | | </Form> |
| | | </Modal> |
| | | ); |
| | | } |
| | | } |
| | | |
| | | export default Form.create({ name: "EditAccountMessageForm" })( |
| | | EditAccountMessageForm, |
| | | export default Form.create({ name: "EditAccidentMessageForm" })( |
| | | EditAccidentMessageForm, |
| | | ); |
| | |
| | | import EditFormBase from "../../common/edit-form-base"; |
| | | import { inputLongStyle } from "../../common/constants"; |
| | | |
| | | class EditDeviceForm extends EditFormBase { |
| | | class EditAccidentTypeForm extends EditFormBase { |
| | | initEditableData() { |
| | | return { |
| | | name: "", |
| | |
| | | } |
| | | } |
| | | |
| | | export default Form.create({ name: "EditDeviceForm" })(EditDeviceForm); |
| | | export default Form.create({ name: "EditAccidentTypeForm" })(EditAccidentTypeForm); |
| | |
| | | import { Form, Input, Modal, Select, Switch, Radio } from "antd"; |
| | | import "../style/index.less"; |
| | | import EditFormBase from "../../common/edit-form-base"; |
| | | import { inputLongStyle } from "../../common/constants"; |
| | | import { inputLongStyle } from "../../common/constants"; |
| | | import { getClientOptions } from "@/api/screenSetting"; |
| | | |
| | | const { Option } = Select; |
| | | |
| | | class EditDeviceForm extends EditFormBase { |
| | | class EditAccountForm extends EditFormBase { |
| | | state = { |
| | | ...this.state, |
| | | clientOptions: [] |
| | | }; |
| | | |
| | | initView() { |
| | | this.loadClientOptions(); |
| | | } |
| | | |
| | | loadClientOptions = () => { |
| | | getClientOptions().then(response => { |
| | | if (response && response.data) { |
| | | let options = []; |
| | | if (response.data.data && Array.isArray(response.data.data)) { |
| | | options = response.data.data; |
| | | } else if (Array.isArray(response.data)) { |
| | | options = response.data; |
| | | } |
| | | this.setState({ clientOptions: options }); |
| | | } |
| | | }).catch(e => { |
| | | console.error("加载委托方列表失败", e); |
| | | this.setState({ clientOptions: [] }); |
| | | }); |
| | | }; |
| | | |
| | | initEditableData() { |
| | | return { |
| | | name: "", |
| | | type: 1, |
| | | status: 1, |
| | | accountType: 1, |
| | | state: 1, |
| | | clientProjectIds: [], |
| | | account: "", |
| | | password: "", |
| | | }; |
| | |
| | | render() { |
| | | const { visible, onCancel, onOk, form } = this.props; |
| | | const { getFieldDecorator } = form; |
| | | const { name, status, account, password, type, accountType } = this.state.editableData; |
| | | let { name, state, account, password, type, clientProjectIds } = this.state.editableData; |
| | | |
| | | // Handle string to array conversion for Select mode="multiple" |
| | | // If it's a string (from backend), split it. If it's already array (from user interaction), keep it. |
| | | let selectedClientIds = []; |
| | | if (typeof clientProjectIds === 'string' && clientProjectIds) { |
| | | selectedClientIds = clientProjectIds.split(','); |
| | | } else if (Array.isArray(clientProjectIds)) { |
| | | selectedClientIds = clientProjectIds.map(String); // Ensure strings for Select |
| | | } |
| | | |
| | | return ( |
| | | <Modal |
| | | title={this.state.isEdit ? "编辑账号" : "添加账号"} |
| | |
| | | </Radio.Group> |
| | | )} |
| | | </Form.Item> |
| | | <Form.Item label="关联委托方"> |
| | | {getFieldDecorator("accountType", { |
| | | rules: [{ required: true, message: "请选择关联委托方!" }], |
| | | initialValue: accountType, |
| | | onChange: this.handleFieldChange("accountType"), |
| | | <Form.Item label="关联委托方/项目"> |
| | | {getFieldDecorator("clientProjectIds", { |
| | | rules: [{ required: true, message: "请选择关联委托方/项目!" }], |
| | | initialValue: selectedClientIds, |
| | | onChange: this.handleFieldChange("clientProjectIds"), |
| | | })( |
| | | <Select style={inputLongStyle} mode="multiple" placeholder="请选择关联委托方"> |
| | | <Option value={1}>委托方1</Option> |
| | | <Option value={2}>委托方2</Option> |
| | | <Select style={inputLongStyle} mode="multiple" placeholder="请选择关联委托方/项目"> |
| | | {this.state.clientOptions.map(option => ( |
| | | <Option key={option.code} value={option.code.toString()}>{option.name}</Option> |
| | | ))} |
| | | </Select> |
| | | )} |
| | | </Form.Item> |
| | | <Form.Item label="启用状态"> |
| | | {getFieldDecorator("status", { |
| | | rules: [{ required: true, message: "请选择启用状态!" }], |
| | | initialValue: status, |
| | | onChange: this.handleFieldChange("status"), |
| | | })(<Switch checked={status === 1} />)} |
| | | {getFieldDecorator("state", { |
| | | initialValue: state === 1, |
| | | valuePropName: 'checked', |
| | | onChange: (checked) => this.handleFieldChange("state")(checked ? 1 : 2), |
| | | })(<Switch checkedChildren="启用" unCheckedChildren="禁用" />)} |
| | | </Form.Item> |
| | | {!this.state.isEdit && ( |
| | | <Form.Item label="登录账号"> |
| | |
| | | } |
| | | } |
| | | |
| | | export default Form.create({ name: "EditDeviceForm" })(EditDeviceForm); |
| | | export default Form.create({ name: "EditAccountForm" })(EditAccountForm); |