告警工单初始化
This commit is contained in:
@@ -37,7 +37,7 @@ export function updateWorkOrder(data) {
|
||||
|
||||
export function dispatchWorkOrder(data) {
|
||||
return request({
|
||||
url: `/iot/ioTworkOrder/dispat`,
|
||||
url: `/iot/ioTworkOrder/dispatch`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@@ -45,10 +45,11 @@ export function dispatchWorkOrder(data) {
|
||||
|
||||
//开始执行工单
|
||||
|
||||
export function startWorkOrder(id) {
|
||||
export function startWorkOrder(data) {
|
||||
return request({
|
||||
url: `/iot/ioTworkOrder/start/${id}`,
|
||||
method: 'put',
|
||||
url: `/iot/ioTworkOrder/start`,
|
||||
method: 'post',
|
||||
data
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
} from '../../api/systemSetting';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '@/src/store';
|
||||
import utils, { workOrderTypeOptions, getWorkOrderTypeText, errorLevelOptions, LEVEL_TAG_COLOR } from '@/lib/utils';
|
||||
import utils, { errorSourceOptions, getErrorSourceText } from '@/lib/utils';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@@ -121,6 +121,7 @@ const AlarmModelManagement: React.FC = () => {
|
||||
form.setFieldsValue({
|
||||
errorCode: record.errorCode,
|
||||
modelId: record.modelId,
|
||||
sourceType: record.sourceType,
|
||||
autoGenerateOrder: record.autoGenerateOrder
|
||||
});
|
||||
setModalVisible(true);
|
||||
@@ -183,7 +184,8 @@ const AlarmModelManagement: React.FC = () => {
|
||||
// 后端要求字段
|
||||
errorCode: values.errorCode,
|
||||
modelId: values.modelId,
|
||||
autoGenerateOrder: values.autoGenerateOrder ?? false
|
||||
autoGenerateOrder: values.autoGenerateOrder ?? false,
|
||||
sourceType: values.sourceType ?? 1
|
||||
};
|
||||
const res = await saveAlarmOrderConfigApi(submitParams);
|
||||
if (res.code === 200 && res.data === true) {
|
||||
@@ -200,6 +202,12 @@ const AlarmModelManagement: React.FC = () => {
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{ title: '错误编码', dataIndex: 'errorCode', key: 'errorCode' },
|
||||
{
|
||||
title: '错误来源',
|
||||
dataIndex: 'sourceType',
|
||||
key: 'sourceType',
|
||||
render: (val: string) => getErrorSourceText(val)
|
||||
},
|
||||
{
|
||||
title: '关联工单模型',
|
||||
dataIndex: 'modelId',
|
||||
@@ -284,6 +292,15 @@ const AlarmModelManagement: React.FC = () => {
|
||||
<Select placeholder="请选择工单模型" options={modelOptions} allowClear />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="错误来源"
|
||||
name="sourceType"
|
||||
rules={[{ required: true, message: '请选择错误来源' }]}
|
||||
>
|
||||
<Select placeholder="请选择错误来源" options={errorSourceOptions} allowClear />
|
||||
</Form.Item>
|
||||
|
||||
|
||||
{/* 是否自动生成工单 */}
|
||||
<Form.Item
|
||||
label="自动生成工单"
|
||||
|
||||
@@ -7,10 +7,11 @@ import {
|
||||
BellOutlined, ClockCircleOutlined, CheckCircleOutlined, ExclamationCircleOutlined,
|
||||
SearchOutlined, ReloadOutlined, FileTextOutlined, NotificationOutlined,
|
||||
EyeOutlined,
|
||||
EditOutlined
|
||||
EditOutlined,
|
||||
ExclamationOutlined
|
||||
} from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
import { exportTable } from "../../lib/utils.js"
|
||||
import { errorLevelOptions, exportTable } from "../../lib/utils.js"
|
||||
|
||||
// 接口引入
|
||||
import {
|
||||
@@ -24,14 +25,12 @@ import {
|
||||
import { UniversalBarChart, UniversalLineChart, UniversalPieChart } from '../recharts.jsx';
|
||||
import { addWorkOrder } from '@/api/workOrder.js';
|
||||
import { alarmLevelConfig, alarmTypeMap, handleStatusMap } from '../../constants.js';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '@/src/store/index.js';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
const { Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
// ====================== 你提供的枚举 ======================
|
||||
|
||||
// 颜色映射(从枚举自动生成)
|
||||
const levelColor = Object.keys(alarmLevelConfig).reduce((map, key) => {
|
||||
map[key] = alarmLevelConfig[key].color;
|
||||
return map;
|
||||
@@ -42,6 +41,70 @@ const levelText = Object.keys(alarmLevelConfig).reduce((map, key) => {
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
// 级别颜色配置
|
||||
const levelColorMap = {
|
||||
ERROR: '#FF4D4F', // 错误 红
|
||||
WARNING: '#FA8C16', // 警告 橙
|
||||
INFO: '#1890FF', // 信息 蓝
|
||||
};
|
||||
|
||||
|
||||
// 快速根据 value 获取 label
|
||||
const getLevelLabel = (val) => {
|
||||
const item = errorLevelOptions.find(opt => opt.value === val);
|
||||
return item?.label || '未知';
|
||||
};
|
||||
|
||||
/**
|
||||
* 通用告警级别标签组件
|
||||
* @param level 接口返回字符串 INFO / WARNING / ERROR
|
||||
* @param size small表格 | large详情弹窗
|
||||
*/
|
||||
const AlarmLevelTag = ({ level, size = 'small' }) => {
|
||||
const color = levelColorMap[level] || '#86909C';
|
||||
const text = getLevelLabel(level);
|
||||
|
||||
// 详情大标签
|
||||
if (size === 'large') {
|
||||
return (
|
||||
<Tag
|
||||
color={color}
|
||||
style={{
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
padding: '4px 10px',
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 6
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
|
||||
// 表格小标签
|
||||
return (
|
||||
<Tag
|
||||
color={color}
|
||||
style={{
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
padding: '2px 6px',
|
||||
fontSize: 12,
|
||||
borderRadius: 2,
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 4
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
export default function AlarmCenterPage() {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -55,6 +118,8 @@ export default function AlarmCenterPage() {
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const [todayData, setTodayData] = useState({});
|
||||
const [yesterdayData, setYesterdayData] = useState({});
|
||||
const { stationId } = useSelector((state: RootState) => state.station);
|
||||
const { userInfo } = useSelector((state: RootState) => state.user);
|
||||
|
||||
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||
const [editForm] = Form.useForm();
|
||||
@@ -86,8 +151,8 @@ export default function AlarmCenterPage() {
|
||||
const fetchList = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await getAlarmList({ pageNum, pageSize, ...queryParams });
|
||||
if (res.code === 0) {
|
||||
const res = await getAlarmList({ pageNum, pageSize, ...queryParams, siteId: stationId });
|
||||
if (res.code === 200) {
|
||||
setAlarmList(res.rows || []);
|
||||
setTotal(res.total || 0);
|
||||
}
|
||||
@@ -189,7 +254,7 @@ export default function AlarmCenterPage() {
|
||||
fetchList();
|
||||
|
||||
|
||||
}, [pageNum]);
|
||||
}, [pageNum, stationId]);
|
||||
useEffect(() => {
|
||||
fetchTodayAndYesterdayStat();
|
||||
fetchStat();
|
||||
@@ -201,7 +266,6 @@ export default function AlarmCenterPage() {
|
||||
{
|
||||
title: '告警编号',
|
||||
dataIndex: 'alarmNo',
|
||||
width: 180,
|
||||
render: (text) => <Text style={{ color: '#165DFF', fontWeight: 500 }}>{text}</Text>
|
||||
},
|
||||
{
|
||||
@@ -218,7 +282,7 @@ export default function AlarmCenterPage() {
|
||||
{
|
||||
title: '设备名称',
|
||||
dataIndex: 'deviceName',
|
||||
width: 160
|
||||
width: 280
|
||||
},
|
||||
{
|
||||
title: '告警类型',
|
||||
@@ -230,11 +294,8 @@ export default function AlarmCenterPage() {
|
||||
title: '告警级别',
|
||||
dataIndex: 'alarmLevel',
|
||||
width: 100,
|
||||
render: (l) => (
|
||||
<Tag color={levelColor[l]} style={{ borderRadius: 2, border: 'none', color: '#fff' }}>
|
||||
{levelText[l]}
|
||||
</Tag>
|
||||
)
|
||||
render: (l) => <AlarmLevelTag level={l} size="small" />
|
||||
|
||||
},
|
||||
{
|
||||
title: '告警时间',
|
||||
@@ -379,10 +440,9 @@ export default function AlarmCenterPage() {
|
||||
priorityLevel: currentAlarm.alarmLevel === 1 ? 1 : 2, // 严重=高,其他=中
|
||||
deviceId: currentAlarm.deviceId || '',
|
||||
deviceName: currentAlarm.deviceName || '',
|
||||
deviceType: '', // 没有就传空
|
||||
siteId: '',
|
||||
siteName: '',
|
||||
orgId: '',
|
||||
deviceType: currentAlarm.deviceType, // 没有就传空
|
||||
siteId: stationId,
|
||||
orgId: userInfo?.orgId || '',
|
||||
assigneeId: '',
|
||||
assigneeName: '',
|
||||
collaboratorIds: '',
|
||||
@@ -752,7 +812,7 @@ export default function AlarmCenterPage() {
|
||||
title="告警详情"
|
||||
open={drawerVisible}
|
||||
onClose={() => setDrawerVisible(false)}
|
||||
width={420}
|
||||
width={480}
|
||||
destroyOnClose
|
||||
>
|
||||
<Spin spinning={detailLoading}>
|
||||
@@ -767,9 +827,7 @@ export default function AlarmCenterPage() {
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<span style={{ fontSize: 16, fontWeight: 700 }}>{currentAlarm.alarmTitle}</span>
|
||||
<Tag color={levelColor[currentAlarm.alarmLevel]} style={{ borderRadius: 2, border: 'none', color: '#fff' }}>
|
||||
{levelText[currentAlarm.alarmLevel]}
|
||||
</Tag>
|
||||
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: '#86909C' }}>告警编号:{currentAlarm.alarmNo}</div>
|
||||
</div>
|
||||
@@ -795,7 +853,6 @@ export default function AlarmCenterPage() {
|
||||
return <Tag color={color}>{handleStatusMap[status] || '-'}</Tag>;
|
||||
})()}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="负责人">{currentAlarm.handleUserName || '-'}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
{/* 告警描述 */}
|
||||
@@ -902,11 +959,8 @@ export default function AlarmCenterPage() {
|
||||
name="alarmLevel"
|
||||
rules={[{ required: true, message: '请选择告警级别' }]}
|
||||
>
|
||||
<Select>
|
||||
<Option value={1}>严重</Option>
|
||||
<Option value={2}>重要</Option>
|
||||
<Option value={3}>一般</Option>
|
||||
<Option value={4}>提示</Option>
|
||||
<Select options={errorLevelOptions}>
|
||||
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
@@ -237,12 +237,28 @@ export function dataUrlToFile(dataUrl, fileName) {
|
||||
|
||||
|
||||
// 错误来源
|
||||
export const errorSourceOptions = [
|
||||
{ label: '无人机', value: 'UAV' },
|
||||
{ label: '割草机', value: 'MOWER' },
|
||||
{ label: '其他', value: 'OTHERS' },
|
||||
export const ERROR_SOURCE = {
|
||||
UAV: 'UAV',
|
||||
MOWER: 'MOWER',
|
||||
OTHERS: 'OTHERS'
|
||||
} as const;
|
||||
|
||||
];
|
||||
export type ErrorSourceType = typeof ERROR_SOURCE[keyof typeof ERROR_SOURCE];
|
||||
|
||||
export const errorSourceOptions: Array<{
|
||||
value: ErrorSourceType;
|
||||
label: string;
|
||||
}> = [
|
||||
{ label: '无人机', value: ERROR_SOURCE.UAV },
|
||||
{ label: '割草机', value: ERROR_SOURCE.MOWER },
|
||||
{ label: '其他', value: ERROR_SOURCE.OTHERS },
|
||||
];
|
||||
|
||||
export const getErrorSourceText = (source?: ErrorSourceType | string): string => {
|
||||
if (!source) return "";
|
||||
const target = errorSourceOptions.find((item) => item.value === source);
|
||||
return target?.label ?? String(source);
|
||||
};
|
||||
|
||||
// 错误等级
|
||||
export const errorLevelOptions = [
|
||||
@@ -296,9 +312,9 @@ export const workOrderTypeOptions: Array<{
|
||||
{ value: WORK_ORDER_TYPE.UAV_ERROR, label: "无人机故障" },
|
||||
{ value: WORK_ORDER_TYPE.INSPECTION_TASK, label: "巡检" },
|
||||
{ value: WORK_ORDER_TYPE.COMPONENT_DEFECT, label: "光伏组件故障" },
|
||||
{ value: WORK_ORDER_TYPE.CLEAN_TASK, label: "清洗任务" },
|
||||
{ value: WORK_ORDER_TYPE.MAINTAIN_TASK, label: "维护任务" },
|
||||
{ value: WORK_ORDER_TYPE.REPAIR_TASK, label: "维修任务" },
|
||||
{ value: WORK_ORDER_TYPE.CLEAN_TASK, label: "清洗" },
|
||||
{ value: WORK_ORDER_TYPE.MAINTAIN_TASK, label: "维护" },
|
||||
{ value: WORK_ORDER_TYPE.REPAIR_TASK, label: "维修" },
|
||||
{ value: WORK_ORDER_TYPE.OTHER, label: "其他" },
|
||||
];
|
||||
|
||||
|
||||
@@ -33,18 +33,19 @@ import {
|
||||
getWorkOrderApi,
|
||||
getWorkOrderDetailApi,
|
||||
updateWorkOrder,
|
||||
dispatchWorkOrder,
|
||||
startWorkOrder,
|
||||
completeWorkOrder,
|
||||
suspendWorkOrder,
|
||||
deleteWorkOrder
|
||||
} from '../api/workOrder';
|
||||
import { getSiteDevice } from '../api/device';
|
||||
import { UniversalLineChart } from '../components/recharts';
|
||||
import { orderTypeConfig, sourceTypeMap } from '../constants';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '../store';
|
||||
import { getUser } from '../api/user';
|
||||
import utils from '../lib/utils';
|
||||
import utils, { getWorkOrderTypeText, workOrderTypeOptions } from '../lib/utils';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Search } = Input;
|
||||
@@ -78,6 +79,7 @@ const WorkOrderPage = () => {
|
||||
const [workOrderTotal, setWorkOrderTotal] = useState(0);
|
||||
const [currentDetail, setCurrentDetail] = useState(null);
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [deviceList, setDeviceList] = useState([]);
|
||||
|
||||
// 从 redux 获取用户信息
|
||||
const { userInfo } = useSelector((state: RootState) => state.user);
|
||||
@@ -108,6 +110,23 @@ const WorkOrderPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取设备列表
|
||||
const fetchDeviceList = async () => {
|
||||
try {
|
||||
const res: any = await getSiteDevice({
|
||||
siteId: userInfo?.siteId,
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
orgId: userInfo?.orgId
|
||||
});
|
||||
if (res.code == 200) {
|
||||
setDeviceList(res.rows || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取设备列表失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 统计数据
|
||||
const stats = [
|
||||
{ title: '今日新增工单', value: 18, subValue: '+3', subText: '较昨日', icon: <ClipboardList size={24} color="#1890ff" />, bg: '#e6f7ff' },
|
||||
@@ -241,20 +260,24 @@ const WorkOrderPage = () => {
|
||||
setEditModalVisible(true);
|
||||
};
|
||||
|
||||
// 打开派发工单弹窗
|
||||
const openDispatchModal = () => {
|
||||
if (!selectedRowKey) return messageApi.warning('请先选择一条工单');
|
||||
if (!isAdmin) return messageApi.error('只有管理员才可以派发工单');
|
||||
// 打开执行工单弹窗
|
||||
const openExecuteModal = () => {
|
||||
if (selectedRowKeys.length > 0) {
|
||||
// 批量执行
|
||||
form.setFieldsValue({
|
||||
orderIds: selectedRowKeys
|
||||
});
|
||||
} else if (selectedRowKey) {
|
||||
// 单个执行
|
||||
form.setFieldsValue({
|
||||
orderIds: [selectedRowKey]
|
||||
});
|
||||
} else {
|
||||
return messageApi.warning('请先选择一条工单');
|
||||
}
|
||||
|
||||
fetchUserList();
|
||||
form.setFieldsValue({
|
||||
orderId: selectedRowKey,
|
||||
assigneeId: currentDetail?.assigneeId ?? '',
|
||||
assigneeName: currentDetail?.assigneeName || '',
|
||||
planStartTime: currentDetail?.planStartTime ? dayjs(currentDetail.planStartTime) : null,
|
||||
planEndTime: currentDetail?.planEndTime ? dayjs(currentDetail.planEndTime) : null,
|
||||
deadlineTime: currentDetail?.deadlineTime ? dayjs(currentDetail.deadlineTime) : null,
|
||||
});
|
||||
|
||||
fetchDeviceList();
|
||||
setDispatchModalVisible(true);
|
||||
};
|
||||
|
||||
@@ -280,25 +303,21 @@ const WorkOrderPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 提交派发工单
|
||||
const submitDispatch = async (values) => {
|
||||
// 提交执行工单
|
||||
const submitExecute = async (values) => {
|
||||
try {
|
||||
// 调用 startWorkOrder 接口,传递 ids 和 选择的设备 id
|
||||
const params = {
|
||||
orderId: values.orderId,
|
||||
assigneeId: values.assigneeId ?? '',
|
||||
assigneeName: values.assigneeName,
|
||||
collaboratorIds: '',
|
||||
collaboratorNames: values.collaboratorNames?.join(',') || '',
|
||||
planStartTime: values.planStartTime?.format('YYYY-MM-DDTHH:mm:ss') || '',
|
||||
planEndTime: values.planEndTime?.format('YYYY-MM-DDTHH:mm:ss') || '',
|
||||
deadlineTime: values.deadlineTime?.format('YYYY-MM-DDTHH:mm:ss') || '',
|
||||
ids: values.orderIds,
|
||||
deviceId: values.deviceId
|
||||
};
|
||||
await dispatchWorkOrder(params);
|
||||
messageApi.success('工单派发成功');
|
||||
await startWorkOrder(params);
|
||||
messageApi.success('工单执行成功');
|
||||
setDispatchModalVisible(false);
|
||||
setSelectedRowKeys([]); // 清空选中
|
||||
getWorkOrderList();
|
||||
} catch (error) {
|
||||
messageApi.error('工单派发失败');
|
||||
messageApi.error('工单执行失败');
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
@@ -517,20 +536,32 @@ const WorkOrderPage = () => {
|
||||
<Text strong style={{ fontSize: 15, color: '#1D2129' }}>工单列表</Text>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>(共 {workOrderTotal} 条)</Text>
|
||||
{selectedRowKeys.length > 0 && (
|
||||
<Popconfirm
|
||||
title={`确定删除已选中的 ${selectedRowKeys.length} 条工单吗?`}
|
||||
onConfirm={handleBatchDelete}
|
||||
>
|
||||
<Button size="small" danger icon={<DeleteOutlined />}>
|
||||
批量删除
|
||||
<>
|
||||
<Popconfirm
|
||||
title={`确定删除已选中的 ${selectedRowKeys.length} 条工单吗?`}
|
||||
onConfirm={handleBatchDelete}
|
||||
>
|
||||
<Button size="small" danger icon={<DeleteOutlined />}>
|
||||
批量删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon={<PlayCircleFilled />}
|
||||
onClick={openExecuteModal}
|
||||
>
|
||||
批量执行 ({selectedRowKeys.length})
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</>
|
||||
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
bodyStyle={{ padding: 0 }}
|
||||
style={{ ...cardStyle, marginBottom: 8 }}
|
||||
>
|
||||
|
||||
<Table
|
||||
dataSource={workOrderList}
|
||||
columns={columns}
|
||||
@@ -565,7 +596,7 @@ const WorkOrderPage = () => {
|
||||
legend={{ position: 'right', layout: 'vertical' }}
|
||||
height={260}
|
||||
/>
|
||||
<div style={{ position: 'absolute', top: '42%', left: '30%', textAlign: 'center', transform: 'translate(-50%, -50%)' }}>
|
||||
<div style={{ position: 'absolute', top: '45%', left: '50%', textAlign: 'center', transform: 'translate(-50%, -50%)' }}>
|
||||
<div style={{ fontSize: 12, color: '#86909C' }}>总数</div>
|
||||
<div style={{ fontSize: 28, fontWeight: 800, color: '#1D2129' }}>{workOrderTotal}</div>
|
||||
</div>
|
||||
@@ -623,14 +654,14 @@ const WorkOrderPage = () => {
|
||||
<span level={4} style={{ margin: 0, fontSize: 18, fontWeight: 800 }}>{currentDetail?.orderTitle || '暂无标题'}</span>
|
||||
{currentDetail?.priorityLevel && (
|
||||
<Tag style={{ color: priorityConfig[currentDetail.priorityLevel]?.color, background: priorityConfig[currentDetail.priorityLevel]?.bg, border: `1px solid ${priorityConfig[currentDetail.priorityLevel]?.color}`, borderRadius: 4, fontWeight: 600 }}>
|
||||
{priorityConfig[currentDetail.priorityLevel].label}优先级
|
||||
{currentDetail.priorityLevel}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Row gutter={[16, 16]} style={{ background: '#f8fafc', padding: 16, borderRadius: 12, marginBottom: 20 }}>
|
||||
<Col span={12}><span style={{ fontSize: 12 }}>来源:</span><span style={{ fontSize: 12, fontWeight: 500 }}>{sourceTypeMap[currentDetail?.sourceType] || '--'}</span></Col>
|
||||
<Col span={12}><span style={{ fontSize: 12 }}>工单类型:</span><span style={{ fontSize: 12, fontWeight: 500 }}>{orderTypeConfig[currentDetail?.orderType] || '--'}</span></Col>
|
||||
<Col span={12}><span style={{ fontSize: 12 }}>工单类型:</span><span style={{ fontSize: 12, fontWeight: 500 }}>{getWorkOrderTypeText(currentDetail?.orderType)}</span></Col>
|
||||
<Col span={12}><span style={{ fontSize: 12 }}>负责人:</span><span style={{ fontSize: 12, fontWeight: 500 }}>{currentDetail?.assigneeName || '--'}</span></Col>
|
||||
<Col span={12}><span style={{ fontSize: 12 }}>协同人员:</span><span style={{ fontSize: 12, fontWeight: 500 }}>{currentDetail?.collaboratorNames || '无'}</span></Col>
|
||||
<Col span={24}><span style={{ fontSize: 12 }}>计划时间:</span>
|
||||
@@ -672,7 +703,7 @@ const WorkOrderPage = () => {
|
||||
}}>
|
||||
{item.active ? <CheckOutlined /> : item.step}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, marginTop: 8, color: item.active ? '#1D2129' : '#86909C', fontWeight: item.active ? 600 : 400 }}>{item.label}</div>
|
||||
<div style={{ fontSize: 11, marginTop: 8, color: item.active ? '#1D2129' : '#86909C', fontWeight: item.active ? 600 : 400 }}>{item?.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -683,12 +714,11 @@ const WorkOrderPage = () => {
|
||||
<Button
|
||||
type="primary"
|
||||
block
|
||||
onClick={openDispatchModal}
|
||||
onClick={openExecuteModal}
|
||||
style={{ borderRadius: 8, height: 36, fontWeight: 600 }}
|
||||
disabled={!isAdmin}
|
||||
title={!isAdmin ? "只有管理员可以派发工单" : ""}
|
||||
icon={<PlayCircleFilled />}
|
||||
>
|
||||
派发工单
|
||||
执行工单
|
||||
</Button>
|
||||
</Col>
|
||||
<Col span={6}><Button icon={<EditOutlined />} block onClick={openEditModal} style={{ borderRadius: 8, height: 36, fontWeight: 600 }}>修改</Button></Col>
|
||||
@@ -781,9 +811,8 @@ const WorkOrderPage = () => {
|
||||
<Input placeholder="请输入工单标题" />
|
||||
</FormItem>
|
||||
<FormItem name="orderType" label="工单类型" rules={[{ required: true, message: '请选择工单类型' }]}>
|
||||
<Select placeholder="请选择工单类型">
|
||||
<Option value={1}>巡检</Option><Option value={2}>检修</Option><Option value={3}>清洗</Option><Option value={4}>维修</Option><Option value={5}>运维</Option>
|
||||
</Select>
|
||||
<Select placeholder="请选择工单类型" options={workOrderTypeOptions} />
|
||||
|
||||
</FormItem>
|
||||
<FormItem name="priorityLevel" label="优先级" rules={[{ required: true, message: '请选择优先级' }]}>
|
||||
<Select placeholder="请选择优先级">
|
||||
@@ -800,30 +829,49 @@ const WorkOrderPage = () => {
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
{/* 派发工单弹窗 */}
|
||||
<Modal title="派发工单" open={dispatchModalVisible} onCancel={() => setDispatchModalVisible(false)} footer={null} width={550}>
|
||||
<Form form={form} layout="vertical" onFinish={submitDispatch}>
|
||||
<FormItem name="orderId" label="工单ID" hidden><Input /></FormItem>
|
||||
<FormItem name="assigneeName" label="负责人姓名" hidden><Input /></FormItem>
|
||||
<FormItem name="assigneeId" label="负责人" rules={[{ required: true, message: '请选择负责人' }]}>
|
||||
{/* 执行工单弹窗 */}
|
||||
<Modal title="执行工单" open={dispatchModalVisible} onCancel={() => setDispatchModalVisible(false)} footer={null} width={550}>
|
||||
<Form form={form} layout="vertical" onFinish={submitExecute}>
|
||||
<FormItem name="orderIds" label="工单ID" hidden><Input /></FormItem>
|
||||
<FormItem label="选中工单">
|
||||
<div style={{
|
||||
padding: 8,
|
||||
background: '#f8fafc',
|
||||
borderRadius: 6,
|
||||
maxHeight: 150,
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
{selectedRowKeys.length > 0 ? (
|
||||
workOrderList
|
||||
.filter(item => selectedRowKeys.includes(item.id))
|
||||
.map(item => (
|
||||
<div key={item.id} style={{ marginBottom: 4 }}>
|
||||
• {item.orderTitle} ({item.orderNo})
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div>• {currentDetail?.orderTitle} ({currentDetail?.orderNo})</div>
|
||||
)}
|
||||
</div>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="deviceId"
|
||||
label="选择执行设备"
|
||||
rules={[{ required: true, message: '请选择执行设备' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择负责人"
|
||||
onChange={(val, option: any) => {
|
||||
form.setFieldsValue({ assigneeName: option.children });
|
||||
}}
|
||||
placeholder="请选择执行设备"
|
||||
>
|
||||
{userList.map(user => (
|
||||
<Option key={user.userId} value={user.userId}>{user.nickName || user.userName}</Option>
|
||||
{deviceList.map(device => (
|
||||
<Option key={device.id} value={device.id}>
|
||||
{device.deviceName} ({device.serialNumber})
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem name="collaboratorNames" label="协同人员"><Select mode="tags" placeholder="可输入多个,回车确认" /></FormItem>
|
||||
<FormItem name="planStartTime" label="计划开始时间"><DatePicker showTime style={{ width: '100%' }} /></FormItem>
|
||||
<FormItem name="planEndTime" label="计划结束时间"><DatePicker showTime style={{ width: '100%' }} /></FormItem>
|
||||
<FormItem name="deadlineTime" label="截止时间"><DatePicker showTime style={{ width: '100%' }} /></FormItem>
|
||||
<FormItem style={{ textAlign: 'right', marginBottom: 0 }}>
|
||||
<Button onClick={() => setDispatchModalVisible(false)}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" style={{ marginLeft: 8 }}>确认派发</Button>
|
||||
<Button type="primary" htmlType="submit" style={{ marginLeft: 8 }}>确认执行</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@@ -18,9 +18,7 @@ const ENV = { DEV: 'development', PROD: 'production' };
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, '.', '');
|
||||
return {
|
||||
define: {
|
||||
'process.env': {}
|
||||
},
|
||||
|
||||
plugins: [
|
||||
react(),
|
||||
tailwindcss(),
|
||||
@@ -83,6 +81,7 @@ export default defineConfig(({ mode }) => {
|
||||
),
|
||||
// 环境变量注入
|
||||
'import.meta.env.VITE_API_BASE_URL': JSON.stringify(env.VITE_API_BASE_URL ?? env.baseURL ?? ''),
|
||||
'process.env': {}
|
||||
},
|
||||
|
||||
server: {
|
||||
|
||||
Reference in New Issue
Block a user