diff --git a/src/api/common/fts.ts b/src/api/common/fts.ts new file mode 100644 index 0000000..2dd74f3 --- /dev/null +++ b/src/api/common/fts.ts @@ -0,0 +1,26 @@ +import { AxiosProgressEvent } from "axios"; +import { request } from "@/api/request"; + +/** 上传文件 */ +const FtsUpload = (data: FormData, onUploadProgress?: (progress: number) => void) => { + data.append('provider', 'local'); + data.append('bucket', 'visual'); + + return request.post( + `/fts/v1/uploader`, + data, + { + headers: { + 'Content-Type': 'multipart/form-data' + }, + onUploadProgress: onUploadProgress ? (progressEvent: AxiosProgressEvent) => { + if (progressEvent.total) { + const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); + onUploadProgress(percentCompleted); + } + } : undefined + } + ); +}; + +export default FtsUpload; diff --git a/src/api/ops/alertHistory.ts b/src/api/ops/alertHistory.ts new file mode 100644 index 0000000..3366c14 --- /dev/null +++ b/src/api/ops/alertHistory.ts @@ -0,0 +1,36 @@ +import { request } from "@/api/request"; + +/** 获取 告警记录列表 */ +export const fetchHistories = (data: { + page?: number, + page_size?: number, + size?: number, + policy_id?: number, + rule_id?: number, + status?: string, + severity_id?: number, + starts_at?: string, + ends_at?: string, + keyword?: string +}) => { + // 兼容 size 参数,转换为 page_size + const params: any = { ...data }; + if (params.size !== undefined && params.page_size === undefined) { + params.page_size = params.size; + delete params.size; + } + return request.get("/Alert/v1/record/list", { params }); +}; + +/** 获取 告警记录详情 */ +export const fetchHistoryDetail = (id: number) => request.get(`/Alert/v1/record/get/${id}`); + +/** 获取 告警统计 */ +export const fetchAlertStatistics = (data: { + policy_id?: number, + starts_at?: string, + ends_at?: string +}) => request.get("/Alert/v1/record/statistics", { params: data }); + +/** 获取 告警计数 */ +export const fetchAlertCount = () => request.get("/Alert/v1/record/count"); diff --git a/src/api/ops/datacenter.ts b/src/api/ops/datacenter.ts new file mode 100644 index 0000000..caba645 --- /dev/null +++ b/src/api/ops/datacenter.ts @@ -0,0 +1,53 @@ +import { request } from "@/api/request"; + +/** 获取数据中心列表(分页) */ +export const fetchDatacenterList = (data?: { + page?: number; + page_size?: number; + keyword?: string; + province_id?: number; + city_id?: number; + status?: string; +}) => { + return request.post("/Assets/v1/datacenter/list", data || {}); +}; + +/** 获取数据中心详情 */ +export const fetchDatacenterDetail = (id: number) => { + return request.get(`/Assets/v1/datacenter/detail/${id}`); +}; + +/** 创建数据中心 */ +export const createDatacenter = (data: any) => { + return request.post("/Assets/v1/datacenter/create", data); +}; + +/** 更新数据中心 */ +export const updateDatacenter = (data: any) => { + return request.put("/Assets/v1/datacenter/update", data); +}; + +/** 删除数据中心 */ +export const deleteDatacenter = (id: number) => { + return request.delete(`/Assets/v1/datacenter/delete/${id}`); +}; + +/** 获取省份列表(用于下拉选择) */ +export const fetchProvinceList = () => { + return request.get("/Assets/v1/province/all"); +}; + +/** 获取城市列表(用于下拉选择) */ +export const fetchCityList = () => { + return request.get("/Assets/v1/city/all"); +}; + +/** 根据城市获取数据中心列表 */ +export const fetchDatacenterByCity = (cityId: number) => { + return request.get(`/Assets/v1/datacenter/city/${cityId}`); +}; + +/** 获取数据中心树形结构 */ +export const fetchDatacenterTree = () => { + return request.get("/Assets/v1/datacenter/tree"); +}; diff --git a/src/api/ops/dcControl.ts b/src/api/ops/dcControl.ts new file mode 100644 index 0000000..5eea408 --- /dev/null +++ b/src/api/ops/dcControl.ts @@ -0,0 +1,22 @@ +import { request } from "@/api/request"; + +/** 获取 采集器 */ +export const fetchCollectors = (data: { page: number, size: number, keyword?: string }) => request.get("/DC-Control/v1/collectors", { params: data }); + +/** 新增 采集器 */ +export const createCollector = (data: any) => request.post("/DC-Control/v1/collectors", data); + +/** 删除 采集器 */ +export const deleteCollector = (id: number) => request.delete(`/DC-Control/v1/collectors/${id}`); + +/** 获取 采集器详情 */ +export const fetchCollectorDetail = (id: number) => request.get(`/DC-Control/v1/collectors/${id}`); + +/** 更新 采集器 */ +export const updateCollector = (data: any) => request.put(`/DC-Control/v1/collectors/${data.id}`, data); + +/** 获取 采集器统计数据 */ +export const fetchCollectorStatistics = () => request.get("/DC-Control/v1/statistics"); + +/** 获取 许可证信息 */ +export const fetchLicenseInfo = () => request.get("/DC-Control/v1/license"); diff --git a/src/api/ops/feedbackTicket.ts b/src/api/ops/feedbackTicket.ts new file mode 100644 index 0000000..5bc1e15 --- /dev/null +++ b/src/api/ops/feedbackTicket.ts @@ -0,0 +1,76 @@ +import { request } from "@/api/request"; + +/** 获取 工单列表 */ +export const fetchFeedbackTickets = (data?: { + page?: number, + page_size?: number, + size?: number, + keyword?: string, + type?: string, + priority?: string, + status?: string, + creator_id?: number, + assignee_id?: number +}) => { + // 兼容 size 参数,转换为 page_size + const params: any = data ? { ...data } : {}; + if (params.size !== undefined && params.page_size === undefined) { + params.page_size = params.size; + delete params.size; + } + return request.get("/Feedback/v1/tickets", params ? { params } : undefined); +}; + +/** 创建 工单 */ +export const createFeedbackTicket = (data: any) => request.post("/Feedback/v1/tickets", data); + +/** 更新 工单 */ +export const updateFeedbackTicket = (id: number, data: any) => request.put(`/Feedback/v1/tickets/${id}`, data); + +/** 删除 工单 */ +export const deleteFeedbackTicket = (id: number) => request.delete(`/Feedback/v1/tickets/${id}`); + +/** 获取 工单详情 */ +export const fetchFeedbackTicketDetail = (id: number) => request.get(`/Feedback/v1/tickets/${id}`); + +/** 根据工单编号获取详情 */ +export const fetchFeedbackTicketByNo = (no: string) => request.get(`/Feedback/v1/tickets/no/${no}`); + +/** 接单 */ +export const acceptFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/accept`); + +/** 转交 */ +export const transferFeedbackTicket = (id: number, data: { assignee_id: number, assignee_name: string, reason?: string }) => request.post(`/Feedback/v1/tickets/${id}/transfer`, data); + +/** 撤回 */ +export const cancelFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/cancel`); + +/** 挂起 */ +export const suspendFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/suspend`, data); + +/** 重启 */ +export const resumeFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/resume`); + +/** 解决 */ +export const resolveFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/resolve`, data); + +/** 关闭 */ +export const closeFeedbackTicket = (id: number, data: { remarks?: string }) => request.post(`/Feedback/v1/tickets/${id}/close`, data); + +/** 添加评论 */ +export const commentFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/comment`, data); + +/** 获取操作日志 */ +export const fetchFeedbackTicketLogs = (id: number) => request.get(`/Feedback/v1/tickets/${id}/logs`); + +/** 获取关联关系 */ +export const fetchFeedbackTicketRelations = (id: number) => request.get(`/Feedback/v1/tickets/${id}/relations`); + +/** 创建关联 */ +export const createFeedbackTicketRelation = (id: number, data: { target_ticket_id: number, relation_type: string, description?: string }) => request.post(`/Feedback/v1/tickets/${id}/relations`, data); + +/** 删除关联 */ +export const deleteFeedbackTicketRelation = (relationId: number) => request.delete(`/Feedback/v1/tickets/relations/${relationId}`); + +/** 获取 工单统计数据 */ +export const fetchFeedbackTicketStatistics = () => request.get("/Feedback/v1/tickets/statistics"); diff --git a/src/api/ops/floor.ts b/src/api/ops/floor.ts new file mode 100644 index 0000000..a9179ad --- /dev/null +++ b/src/api/ops/floor.ts @@ -0,0 +1,37 @@ +import { request } from "@/api/request"; + +/** 获取楼层列表(分页) */ +export const fetchFloorList = (data?: { + page?: number; + page_size?: number; + keyword?: string; + datacenter_id?: number; + status?: string; +}) => { + return request.post("/Assets/v1/floor/list", data || {}); +}; + +/** 获取楼层详情 */ +export const fetchFloorDetail = (id: number) => { + return request.get(`/Assets/v1/floor/detail/${id}`); +}; + +/** 创建楼层 */ +export const createFloor = (data: any) => { + return request.post("/Assets/v1/floor/create", data); +}; + +/** 更新楼层 */ +export const updateFloor = (data: any) => { + return request.put("/Assets/v1/floor/update", data); +}; + +/** 删除楼层 */ +export const deleteFloor = (id: number) => { + return request.delete(`/Assets/v1/floor/delete/${id}`); +}; + +/** 获取数据中心列表(用于下拉选择) */ +export const fetchDatacenterList = () => { + return request.get("/Assets/v1/datacenter/all"); +}; diff --git a/src/api/ops/rbac2.ts b/src/api/ops/rbac2.ts new file mode 100644 index 0000000..e6a495a --- /dev/null +++ b/src/api/ops/rbac2.ts @@ -0,0 +1,31 @@ +import { request } from '@/api/request' + +/** + * 用户列表项接口 + */ +export interface UserItem { + id: number; + name: string; + username?: string; + email?: string; + phone?: string; + status?: string; + created_at?: string; + updated_at?: string; +} + +/** + * 用户列表响应接口 + */ +export interface UserListResponse { + code: number; + message: string; + data: UserItem[]; +} + +/** + * 获取用户列表 + */ +export const fetchUserList = async (data: any) => { + return request.post('/rbac2/v1/user/list', data); +}; diff --git a/src/components/search-form/index.vue b/src/components/search-form/index.vue index d1df4b9..3fc698e 100644 --- a/src/components/search-form/index.vue +++ b/src/components/search-form/index.vue @@ -57,7 +57,7 @@ + + + + diff --git a/src/views/ops/pages/datacenter/floor/components/FloorFormDialog.vue b/src/views/ops/pages/datacenter/floor/components/FloorFormDialog.vue new file mode 100644 index 0000000..136c712 --- /dev/null +++ b/src/views/ops/pages/datacenter/floor/components/FloorFormDialog.vue @@ -0,0 +1,373 @@ + + + + + + + diff --git a/src/views/ops/pages/datacenter/floor/config/columns.ts b/src/views/ops/pages/datacenter/floor/config/columns.ts new file mode 100644 index 0000000..eec454e --- /dev/null +++ b/src/views/ops/pages/datacenter/floor/config/columns.ts @@ -0,0 +1,46 @@ +import type { TableColumnData } from '@arco-design/web-vue/es/table/interface' + +export const columns: TableColumnData[] = [ + { + title: '序号', + dataIndex: 'index', + slotName: 'index', + width: 80, + }, + { + title: '楼层名称', + dataIndex: 'name', + width: 150, + }, + { + title: '所属中心', + dataIndex: 'datacenter', + slotName: 'datacenter', + width: 180, + }, + { + title: '楼层号', + dataIndex: 'floor_number', + width: 100, + }, + { + title: '描述', + dataIndex: 'description', + ellipsis: true, + tooltip: true, + width: 200, + }, + { + title: '状态', + dataIndex: 'status', + slotName: 'status', + width: 120, + }, + { + title: '操作', + dataIndex: 'actions', + slotName: 'actions', + width: 240, + fixed: 'right' as const, + }, +] diff --git a/src/views/ops/pages/datacenter/floor/config/search-form.ts b/src/views/ops/pages/datacenter/floor/config/search-form.ts new file mode 100644 index 0000000..3257e25 --- /dev/null +++ b/src/views/ops/pages/datacenter/floor/config/search-form.ts @@ -0,0 +1,23 @@ +import type { FormItem } from '@/components/search-form/types' + +export const searchFormConfig: FormItem[] = [ + { + field: 'keyword', + label: '关键词', + type: 'input', + placeholder: '请输入楼层名称或编码', + }, + { + field: 'status', + label: '状态', + type: 'select', + placeholder: '请选择状态', + options: [ + { label: '规划中', value: 'planning' }, + { label: '建设中', value: 'construction' }, + { label: '运营中', value: 'operating' }, + { label: '维护中', value: 'maintenance' }, + { label: '已下线', value: 'offline' }, + ], + }, +] diff --git a/src/views/ops/pages/datacenter/floor/index.vue b/src/views/ops/pages/datacenter/floor/index.vue new file mode 100644 index 0000000..48e5ab6 --- /dev/null +++ b/src/views/ops/pages/datacenter/floor/index.vue @@ -0,0 +1,247 @@ + + + + + + + diff --git a/src/views/ops/pages/datacenter/house/components/DatacenterDetailDialog.vue b/src/views/ops/pages/datacenter/house/components/DatacenterDetailDialog.vue new file mode 100644 index 0000000..d4b3134 --- /dev/null +++ b/src/views/ops/pages/datacenter/house/components/DatacenterDetailDialog.vue @@ -0,0 +1,216 @@ + + + + + + + diff --git a/src/views/ops/pages/datacenter/house/components/DatacenterFormDialog.vue b/src/views/ops/pages/datacenter/house/components/DatacenterFormDialog.vue new file mode 100644 index 0000000..32d6f9a --- /dev/null +++ b/src/views/ops/pages/datacenter/house/components/DatacenterFormDialog.vue @@ -0,0 +1,461 @@ + + + + + diff --git a/src/views/ops/pages/datacenter/house/config/columns.ts b/src/views/ops/pages/datacenter/house/config/columns.ts new file mode 100644 index 0000000..1dc436c --- /dev/null +++ b/src/views/ops/pages/datacenter/house/config/columns.ts @@ -0,0 +1,134 @@ +import type { TableColumnData } from '@arco-design/web-vue/es/table/interface' + +export const columns: TableColumnData[] = [ + { + title: '序号', + dataIndex: 'index', + slotName: 'index', + width: 80, + }, + { + title: '数据中心名称', + dataIndex: 'name', + width: 180, + }, + { + title: '数据中心编码', + dataIndex: 'code', + width: 150, + }, + { + title: '简称', + dataIndex: 'short_name', + width: 120, + }, + { + title: '状态', + dataIndex: 'status', + slotName: 'status', + width: 120, + }, + { + title: '是否启用', + dataIndex: 'enabled', + slotName: 'enabled', + width: 100, + }, + { + title: '所属省份', + dataIndex: 'province', + slotName: 'province', + width: 120, + }, + { + title: '所属城市', + dataIndex: 'city', + slotName: 'city', + width: 120, + }, + { + title: '联系人', + dataIndex: 'contact_person', + width: 120, + }, + { + title: '联系电话', + dataIndex: 'contact_phone', + width: 130, + }, + { + title: '详细地址', + dataIndex: 'address', + ellipsis: true, + tooltip: true, + width: 200, + }, + { + title: '总面积(㎡)', + dataIndex: 'total_area', + width: 120, + }, + { + title: 'IT区域面积(㎡)', + dataIndex: 'it_area', + width: 140, + }, + { + title: '总电力(KW)', + dataIndex: 'total_power', + width: 120, + }, + { + title: '已用电力(KW)', + dataIndex: 'used_power', + width: 120, + }, + { + title: '可容纳机柜数', + dataIndex: 'max_rack_capacity', + width: 120, + }, + { + title: 'ISO27001', + dataIndex: 'iso27001', + slotName: 'iso27001', + width: 100, + }, + { + title: 'ISO9001', + dataIndex: 'iso9001', + slotName: 'iso9001', + width: 100, + }, + { + title: '楼层数', + dataIndex: 'floor_count', + width: 100, + }, + { + title: '机柜数', + dataIndex: 'rack_count', + width: 100, + }, + { + title: '描述', + dataIndex: 'description', + ellipsis: true, + tooltip: true, + width: 200, + }, + { + title: '备注', + dataIndex: 'remarks', + ellipsis: true, + tooltip: true, + width: 200, + }, + { + title: '操作', + dataIndex: 'actions', + slotName: 'actions', + width: 240, + fixed: 'right' as const, + }, +] diff --git a/src/views/ops/pages/datacenter/house/config/search-form.ts b/src/views/ops/pages/datacenter/house/config/search-form.ts new file mode 100644 index 0000000..221a432 --- /dev/null +++ b/src/views/ops/pages/datacenter/house/config/search-form.ts @@ -0,0 +1,37 @@ +import type { FormItem } from '@/components/search-form/types' + +export const searchFormConfig: FormItem[] = [ + { + field: 'keyword', + label: '关键词', + type: 'input', + placeholder: '请输入数据中心名称、编码、简称或地址', + }, + { + field: 'status', + label: '状态', + type: 'select', + placeholder: '请选择状态', + options: [ + { label: '规划中', value: 'planning' }, + { label: '建设中', value: 'construction' }, + { label: '运营中', value: 'operating' }, + { label: '维护中', value: 'maintenance' }, + { label: '已下线', value: 'offline' }, + ], + }, + // { + // field: 'province_id', + // label: '所属省份', + // type: 'select', + // placeholder: '请选择省份', + // options: [], // 将在组件中动态加载 + // }, + // { + // field: 'city_id', + // label: '所属城市', + // type: 'select', + // placeholder: '请选择城市', + // options: [], // 将在组件中动态加载 + // }, +] diff --git a/src/views/ops/pages/datacenter/house/index.vue b/src/views/ops/pages/datacenter/house/index.vue new file mode 100644 index 0000000..00d003f --- /dev/null +++ b/src/views/ops/pages/datacenter/house/index.vue @@ -0,0 +1,328 @@ + + + + + + + diff --git a/src/views/ops/pages/feedback/all/config/columns.ts b/src/views/ops/pages/feedback/all/config/columns.ts new file mode 100644 index 0000000..3b8be39 --- /dev/null +++ b/src/views/ops/pages/feedback/all/config/columns.ts @@ -0,0 +1,62 @@ +import type { TableColumnData } from '@arco-design/web-vue/es/table/interface' + +export const columns: TableColumnData[] = [ + { + title: '序号', + dataIndex: 'index', + slotName: 'index', + width: 80, + }, + { + title: '工单编号', + dataIndex: 'ticket_no', + width: 160, + }, + { + title: '工单标题', + dataIndex: 'title', + width: 200, + ellipsis: true, + tooltip: true, + }, + { + title: '工单类型', + dataIndex: 'type', + slotName: 'type', + width: 100, + }, + { + title: '工单状态', + dataIndex: 'status', + slotName: 'status', + width: 100, + }, + { + title: '优先级', + dataIndex: 'priority', + slotName: 'priority', + width: 100, + }, + { + title: '创建人', + dataIndex: 'creator_name', + width: 120, + }, + { + title: '处理人', + dataIndex: 'assignee_name', + width: 120, + }, + { + title: '创建时间', + dataIndex: 'created_at', + width: 180, + }, + { + title: '操作', + dataIndex: 'actions', + slotName: 'actions', + width: 200, + fixed: 'right' as const, + }, +] diff --git a/src/views/ops/pages/feedback/all/config/search-form.ts b/src/views/ops/pages/feedback/all/config/search-form.ts new file mode 100644 index 0000000..83d29db --- /dev/null +++ b/src/views/ops/pages/feedback/all/config/search-form.ts @@ -0,0 +1,54 @@ +import type { FormItem } from '@/components/search-form/types' + +export const searchFormConfig: FormItem[] = [ + { + field: 'keyword', + label: '关键词', + type: 'input', + placeholder: '请输入工单编号或标题', + }, + { + field: 'type', + label: '工单类型', + type: 'select', + placeholder: '请选择工单类型', + options: [ + { label: '故障', value: 'incident' }, + { label: '请求', value: 'request' }, + { label: '问题', value: 'question' }, + { label: '其他', value: 'other' }, + ], + }, + { + field: 'status', + label: '工单状态', + type: 'select', + placeholder: '请选择工单状态', + options: [ + { label: '待接单', value: 'pending' }, + { label: '已接单', value: 'accepted' }, + { label: '处理中', value: 'processing' }, + { label: '已挂起', value: 'suspended' }, + { label: '已解决', value: 'resolved' }, + { label: '已关闭', value: 'closed' }, + { label: '已撤回', value: 'cancelled' }, + ], + }, + { + field: 'priority', + label: '优先级', + type: 'select', + placeholder: '请选择优先级', + options: [ + { label: '低', value: 'low' }, + { label: '中', value: 'medium' }, + { label: '高', value: 'high' }, + { label: '紧急', value: 'urgent' }, + ], + }, + { + field: 'dateRange', + label: '创建时间', + type: 'dateRange', + }, +] diff --git a/src/views/ops/pages/feedback/all/index.vue b/src/views/ops/pages/feedback/all/index.vue new file mode 100644 index 0000000..cba5fe3 --- /dev/null +++ b/src/views/ops/pages/feedback/all/index.vue @@ -0,0 +1,497 @@ + + + + + + + diff --git a/src/views/ops/pages/feedback/components/CommentDialog.vue b/src/views/ops/pages/feedback/components/CommentDialog.vue new file mode 100644 index 0000000..1fa6d59 --- /dev/null +++ b/src/views/ops/pages/feedback/components/CommentDialog.vue @@ -0,0 +1,165 @@ + + + + + + + diff --git a/src/views/ops/pages/feedback/components/ResolveDialog.vue b/src/views/ops/pages/feedback/components/ResolveDialog.vue new file mode 100644 index 0000000..b50c7a5 --- /dev/null +++ b/src/views/ops/pages/feedback/components/ResolveDialog.vue @@ -0,0 +1,165 @@ + + + + + + + diff --git a/src/views/ops/pages/feedback/components/SuspendDialog.vue b/src/views/ops/pages/feedback/components/SuspendDialog.vue new file mode 100644 index 0000000..3ecb284 --- /dev/null +++ b/src/views/ops/pages/feedback/components/SuspendDialog.vue @@ -0,0 +1,165 @@ + + + + + + + diff --git a/src/views/ops/pages/feedback/components/TicketDetailDialog.vue b/src/views/ops/pages/feedback/components/TicketDetailDialog.vue new file mode 100644 index 0000000..8cb4379 --- /dev/null +++ b/src/views/ops/pages/feedback/components/TicketDetailDialog.vue @@ -0,0 +1,420 @@ + + + + + + + diff --git a/src/views/ops/pages/feedback/components/TicketFormDialog.vue b/src/views/ops/pages/feedback/components/TicketFormDialog.vue new file mode 100644 index 0000000..6f50d3b --- /dev/null +++ b/src/views/ops/pages/feedback/components/TicketFormDialog.vue @@ -0,0 +1,304 @@ + + + + + + + diff --git a/src/views/ops/pages/feedback/components/TransferDialog.vue b/src/views/ops/pages/feedback/components/TransferDialog.vue new file mode 100644 index 0000000..77d9ef2 --- /dev/null +++ b/src/views/ops/pages/feedback/components/TransferDialog.vue @@ -0,0 +1,232 @@ + + + + + + + diff --git a/src/views/ops/pages/feedback/undo/config/columns.ts b/src/views/ops/pages/feedback/undo/config/columns.ts new file mode 100644 index 0000000..5eaf381 --- /dev/null +++ b/src/views/ops/pages/feedback/undo/config/columns.ts @@ -0,0 +1,139 @@ +import { h } from 'vue' +import { Tag, Button, Dropdown, Message } from '@arco-design/web-vue' +import { IconEdit, IconDelete, IconMore, IconCheckCircle } from '@arco-design/web-vue/es/icon' + +// 工单状态映射 +const statusMap = { + pending: { text: '待接单', color: 'blue' }, + accepted: { text: '已接单', color: 'cyan' }, + processing: { text: '处理中', color: 'orange' }, + suspended: { text: '已挂起', color: 'gray' }, + resolved: { text: '已解决', color: 'green' }, + closed: { text: '已关闭', color: 'red' }, + cancelled: { text: '已撤回', color: 'red' }, +} + +// 工单类型映射 +const typeMap = { + incident: { text: '故障', color: 'red' }, + request: { text: '请求', color: 'blue' }, + question: { text: '问题', color: 'orange' }, + other: { text: '其他', color: 'gray' }, +} + +// 优先级映射 +const priorityMap = { + low: { text: '低', color: 'gray' }, + medium: { text: '中', color: 'blue' }, + high: { text: '高', color: 'orange' }, + urgent: { text: '紧急', color: 'red' }, +} + +// 生成表格列配置 +export const generateColumns = ( + tabType: 'my-created' | 'pending', + handlers: { + onDetail: (record: any) => void + onEdit?: (record: any) => void + onTransfer?: (record: any) => void + onSuspend?: (record: any) => void + onResume?: (record: any) => void + onResolve?: (record: any) => void + onCancel?: (record: any) => void + onComment?: (record: any) => void + onDelete?: (record: any) => void + onClose?: (record: any) => void + } +) => { + const baseColumns = [ + { + title: '序号', + dataIndex: 'index', + width: 80, + render: ({ rowIndex }: { rowIndex: number }) => rowIndex + 1, + }, + { + title: '工单编号', + dataIndex: 'ticket_no', + width: 160, + }, + { + title: '工单标题', + dataIndex: 'title', + width: 200, + ellipsis: true, + tooltip: true, + }, + { + title: '工单类型', + dataIndex: 'type', + width: 100, + render: ({ record }: { record: any }) => { + const type = typeMap[record.type as keyof typeof typeMap] || typeMap.other + return h(Tag, { color: type.color }, { default: () => type.text }) + }, + }, + { + title: '工单状态', + dataIndex: 'status', + width: 100, + render: ({ record }: { record: any }) => { + const status = statusMap[record.status as keyof typeof statusMap] || { text: record.status, color: 'gray' } + return h(Tag, { color: status.color }, { default: () => status.text }) + }, + }, + { + title: '优先级', + dataIndex: 'priority', + width: 100, + render: ({ record }: { record: any }) => { + const priority = priorityMap[record.priority as keyof typeof priorityMap] || { text: record.priority, color: 'gray' } + return h(Tag, { color: priority.color }, { default: () => priority.text }) + }, + }, + { + title: '操作', + dataIndex: 'action', + width: tabType === 'pending' ? 280 : 300, + fixed: 'right' as const, + render: ({ record }: { record: any }) => { + if (tabType === 'pending') { + // 待处理选项卡的操作按钮 + return h( + 'div', + { style: { display: 'flex', gap: '8px', flexWrap: 'wrap' } }, + [ + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onDetail(record) }, { default: () => '详情' }), + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onTransfer?.(record) }, { default: () => '转交' }), + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onSuspend?.(record) }, { default: () => '挂起' }), + record.status === 'suspended' && + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onResume?.(record) }, { default: () => '重启' }), + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onResolve?.(record) }, { default: () => '解决' }), + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onClose?.(record) }, { default: () => '关闭' }), + ] + ) + } else { + // 我创建的选项卡的操作按钮 + const isCompleted = ['resolved', 'closed'].includes(record.status) + const isResolved = record.status === 'resolved' + + return h( + 'div', + { style: { display: 'flex', gap: '8px', flexWrap: 'wrap' } }, + [ + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onDetail(record) }, { default: () => '详情' }), + !isCompleted && + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onEdit?.(record) }, { default: () => '编辑' }), + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onCancel?.(record) }, { default: () => '撤回' }), + isResolved && h(Button, { size: 'small', type: 'text', onClick: () => handlers.onComment?.(record) }, { default: () => '评论' }), + h(Button, { size: 'small', type: 'text', status: 'danger', onClick: () => handlers.onDelete?.(record) }, { default: () => '删除' }), + h(Button, { size: 'small', type: 'text', onClick: () => handlers.onClose?.(record) }, { default: () => '关闭' }), + ] + ) + } + }, + }, + ] + + return baseColumns +} diff --git a/src/views/ops/pages/feedback/undo/config/search-form.ts b/src/views/ops/pages/feedback/undo/config/search-form.ts new file mode 100644 index 0000000..18125f9 --- /dev/null +++ b/src/views/ops/pages/feedback/undo/config/search-form.ts @@ -0,0 +1,49 @@ +import type { FormItem } from '@/components/search-form/types' + +export const searchFormConfig: FormItem[] = [ + { + field: 'keyword', + label: '关键词', + type: 'input', + placeholder: '请输入工单编号或标题', + }, + { + field: 'type', + label: '工单类型', + type: 'select', + placeholder: '请选择工单类型', + options: [ + { label: '故障', value: 'incident' }, + { label: '请求', value: 'request' }, + { label: '问题', value: 'question' }, + { label: '其他', value: 'other' }, + ], + }, + { + field: 'status', + label: '工单状态', + type: 'select', + placeholder: '请选择工单状态', + options: [ + { label: '待接单', value: 'pending' }, + { label: '已接单', value: 'accepted' }, + { label: '处理中', value: 'processing' }, + { label: '已挂起', value: 'suspended' }, + { label: '已解决', value: 'resolved' }, + { label: '已关闭', value: 'closed' }, + { label: '已撤回', value: 'cancelled' }, + ], + }, + { + field: 'priority', + label: '优先级', + type: 'select', + placeholder: '请选择优先级', + options: [ + { label: '低', value: 'low' }, + { label: '中', value: 'medium' }, + { label: '高', value: 'high' }, + { label: '紧急', value: 'urgent' }, + ], + }, +] diff --git a/src/views/ops/pages/feedback/undo/index.vue b/src/views/ops/pages/feedback/undo/index.vue new file mode 100644 index 0000000..801cd49 --- /dev/null +++ b/src/views/ops/pages/feedback/undo/index.vue @@ -0,0 +1,639 @@ + + + + + + + diff --git a/src/views/ops/pages/netarch/topo-group/config/columns.ts b/src/views/ops/pages/netarch/topo-group/config/columns.ts index 3723247..94ff91c 100644 --- a/src/views/ops/pages/netarch/topo-group/config/columns.ts +++ b/src/views/ops/pages/netarch/topo-group/config/columns.ts @@ -16,11 +16,12 @@ export const getColumns = (): TableColumnData[] => [ dataIndex: 'index', width: 60, align: 'center', + slotName: 'index', }, { title: '分组名称', dataIndex: 'name', - width: 250, + width: 150, align: 'left', }, { @@ -33,7 +34,7 @@ export const getColumns = (): TableColumnData[] => [ { title: '分组路径', dataIndex: 'path', - width: 300, + width: 150, align: 'left', }, { @@ -75,6 +76,7 @@ export const getColumns = (): TableColumnData[] => [ { title: '描述', dataIndex: 'description', + width: 180, align: 'left', }, { diff --git a/src/views/ops/pages/netarch/topo-group/index.vue b/src/views/ops/pages/netarch/topo-group/index.vue index 6f7dc34..ffe1eee 100644 --- a/src/views/ops/pages/netarch/topo-group/index.vue +++ b/src/views/ops/pages/netarch/topo-group/index.vue @@ -1,11 +1,11 @@ + + + + + + +