feat
This commit is contained in:
133
src/api/ops/goview.ts
Normal file
133
src/api/ops/goview.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { request } from '@/api/request'
|
||||
|
||||
// GoView 项目类型定义
|
||||
export interface GoViewProject {
|
||||
id: string
|
||||
identity: string
|
||||
projectName: string
|
||||
state: -1 | 1 // -1 未发布, 1 已发布
|
||||
createTime: string
|
||||
updateTime?: string
|
||||
createUserId: number
|
||||
isDelete: 0 | 1
|
||||
indexImage: string
|
||||
backgroundImage: string
|
||||
remarks: string
|
||||
}
|
||||
|
||||
export interface GoViewProjectData {
|
||||
id: string
|
||||
projectId: string
|
||||
content: string // JSON 格式字符串
|
||||
createTime: string
|
||||
createUserId: string
|
||||
}
|
||||
|
||||
export interface GoViewFile {
|
||||
id: string
|
||||
fileName: string
|
||||
fileSize: number
|
||||
fileSuffix: string
|
||||
virtualKey: string
|
||||
relativePath: string
|
||||
absolutePath: string
|
||||
createTime: string
|
||||
}
|
||||
|
||||
export interface OssInfo {
|
||||
BucketName: string
|
||||
bucketURL: string
|
||||
}
|
||||
|
||||
// API 响应类型
|
||||
export interface GoViewResponse<T = any> {
|
||||
code: number
|
||||
msg: string
|
||||
data: T
|
||||
}
|
||||
|
||||
// 项目列表响应
|
||||
export interface ProjectListResponse {
|
||||
list: GoViewProject[]
|
||||
total: number
|
||||
}
|
||||
|
||||
/** 获取 OSS 信息 */
|
||||
export const fetchOssInfo = () => {
|
||||
return request.get<GoViewResponse<OssInfo>>('/Visual/v1/sys/getOssInfo')
|
||||
}
|
||||
|
||||
/** 获取项目列表 */
|
||||
export const fetchProjectList = (params?: {
|
||||
page?: number
|
||||
size?: number
|
||||
state?: -1 | 1
|
||||
}) => {
|
||||
return request.get<GoViewResponse<ProjectListResponse>>('/Visual/v1/project/list', { params })
|
||||
}
|
||||
|
||||
/** 获取项目数据(公开接口,无需认证) */
|
||||
export const fetchProjectData = (projectId: string) => {
|
||||
return request.get<GoViewResponse<GoViewProjectData>>('/Visual/v1/project/getData', {
|
||||
params: { projectId },
|
||||
})
|
||||
}
|
||||
|
||||
/** 创建项目 */
|
||||
export const createProject = (data: {
|
||||
projectName: string
|
||||
state: -1 | 1
|
||||
createTime?: string
|
||||
createUserId?: number
|
||||
isDelete?: 0 | 1
|
||||
indexImage?: string
|
||||
backgroundImage?: string
|
||||
remarks?: string
|
||||
}) => {
|
||||
return request.post<GoViewResponse<GoViewProject>>('/Visual/v1/project/create', data)
|
||||
}
|
||||
|
||||
/** 编辑项目 */
|
||||
export const updateProject = (data: {
|
||||
identity: string
|
||||
projectName?: string
|
||||
indexImage?: string
|
||||
backgroundImage?: string
|
||||
remarks?: string
|
||||
}) => {
|
||||
return request.post<GoViewResponse<GoViewProject>>('/Visual/v1/project/edit', data)
|
||||
}
|
||||
|
||||
/** 删除项目 */
|
||||
export const deleteProject = (ids: string) => {
|
||||
return request.delete<GoViewResponse<Record<string, never>>>('/Visual/v1/project/delete', {
|
||||
params: { ids },
|
||||
})
|
||||
}
|
||||
|
||||
/** 发布/取消发布项目 */
|
||||
export const publishProject = (data: { identity: string; state: -1 | 1 }) => {
|
||||
return request.put<GoViewResponse<{ identity: string; state: number }>>(
|
||||
'/Visual/v1/project/publish',
|
||||
data
|
||||
)
|
||||
}
|
||||
|
||||
/** 上传文件 */
|
||||
export const uploadFile = (file: File) => {
|
||||
const formData = new FormData()
|
||||
formData.append('object', file)
|
||||
return request.post<GoViewResponse<GoViewFile>>('/Visual/v1/project/upload', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
}
|
||||
|
||||
/** 保存项目数据 */
|
||||
export const saveProjectData = (projectId: string, content: string) => {
|
||||
const formData = new URLSearchParams()
|
||||
formData.append('projectId', projectId)
|
||||
formData.append('content', content)
|
||||
return request.post<GoViewResponse<GoViewProjectData>>('/Visual/v1/project/save/data', formData, {
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
396
src/views/ops/pages/feedback/template/index.vue
Normal file
396
src/views/ops/pages/feedback/template/index.vue
Normal file
@@ -0,0 +1,396 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<search-table
|
||||
:form-model="formModel"
|
||||
:form-items="formItems"
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
title="工单模板管理"
|
||||
@update:form-model="handleFormModelUpdate"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
@page-change="handlePageChange"
|
||||
@refresh="handleRefresh"
|
||||
>
|
||||
<!-- 工具栏额外按钮 -->
|
||||
<template #toolbar-extra>
|
||||
<a-button type="primary" @click="handleCreate">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
新建模板
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<!-- 状态 -->
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="record.status === 'active' ? 'green' : 'gray'">
|
||||
{{ record.status === 'active' ? '启用' : '禁用' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 类型 -->
|
||||
<template #type="{ record }">
|
||||
<a-tag>{{ getTypeText(record.type) }}</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 优先级 -->
|
||||
<template #priority="{ record }">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ getPriorityText(record.priority) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 触发类型 -->
|
||||
<template #trigger_type="{ record }">
|
||||
<a-tag>{{ getTriggerTypeText(record.trigger_type) }}</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 自动分配 -->
|
||||
<template #auto_assign="{ record }">
|
||||
<a-tag :color="record.auto_assign ? 'green' : 'gray'">
|
||||
{{ record.auto_assign ? '是' : '否' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 自动接单 -->
|
||||
<template #auto_accept="{ record }">
|
||||
<a-tag :color="record.auto_accept ? 'green' : 'gray'">
|
||||
{{ record.auto_accept ? '是' : '否' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 创建时间 -->
|
||||
<template #created_at="{ record }">
|
||||
{{ formatDate(record.created_at) }}
|
||||
</template>
|
||||
|
||||
<!-- 最后修改 -->
|
||||
<template #updated_at="{ record }">
|
||||
{{ formatDate(record.updated_at) }}
|
||||
</template>
|
||||
|
||||
<!-- 操作 -->
|
||||
<template #actions="{ record }">
|
||||
<a-space size="small">
|
||||
<a-button type="text" size="small" @click="handleEdit(record)">
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="record.status === 'inactive'"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleActivate(record)"
|
||||
>
|
||||
启用
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="record.status === 'active'"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleDeactivate(record)"
|
||||
>
|
||||
禁用
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="record.status === 'active'"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleCreateTicket(record)"
|
||||
>
|
||||
按模板创建
|
||||
</a-button>
|
||||
<a-button type="text" size="small" status="danger" @click="handleDelete(record)">
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</search-table>
|
||||
|
||||
<!-- 新建/编辑模板弹窗 -->
|
||||
<template-form-dialog
|
||||
v-model:visible="formDialogVisible"
|
||||
:template-id="currentTemplateId"
|
||||
@success="handleFormSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import { IconPlus } from '@arco-design/web-vue/es/icon'
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
import SearchTable from '@/components/search-table/index.vue'
|
||||
import { searchFormConfig } from './config/search-form'
|
||||
import { columns as columnsConfig } from './config/columns'
|
||||
import {
|
||||
fetchTemplates,
|
||||
deleteTemplate,
|
||||
activateTemplate,
|
||||
deactivateTemplate,
|
||||
createTicketByTemplate,
|
||||
} from '@/api/ops/template'
|
||||
import TemplateFormDialog from './components/TemplateFormDialog.vue'
|
||||
|
||||
// 状态管理
|
||||
const loading = ref(false)
|
||||
const tableData = ref<any[]>([])
|
||||
|
||||
// 表单模型
|
||||
const formModel = ref({
|
||||
status: '',
|
||||
})
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
// 表单项配置
|
||||
const formItems = computed<FormItem[]>(() => searchFormConfig)
|
||||
|
||||
// 表格列配置
|
||||
const columns = computed(() => columnsConfig)
|
||||
|
||||
// 表单弹窗
|
||||
const formDialogVisible = ref(false)
|
||||
const currentTemplateId = ref<number | undefined>(undefined)
|
||||
|
||||
// 获取模板列表
|
||||
const fetchTemplatesData = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const params: any = {
|
||||
page: pagination.current,
|
||||
page_size: pagination.pageSize,
|
||||
}
|
||||
|
||||
if (formModel.value.status) {
|
||||
params.status = formModel.value.status
|
||||
}
|
||||
|
||||
const res = await fetchTemplates(params)
|
||||
|
||||
tableData.value = res.details?.items || []
|
||||
pagination.total = res.details?.total || 0
|
||||
} catch (error) {
|
||||
console.error('获取模板列表失败:', error)
|
||||
Message.error('获取模板列表失败')
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr: string) => {
|
||||
if (!dateStr) return '-'
|
||||
const date = new Date(dateStr)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
// 获取类型文本
|
||||
const getTypeText = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
incident: '事件',
|
||||
request: '请求',
|
||||
change: '变更',
|
||||
problem: '问题',
|
||||
consultation: '咨询',
|
||||
}
|
||||
return typeMap[type] || '-'
|
||||
}
|
||||
|
||||
// 获取优先级文本
|
||||
const getPriorityText = (priority: string) => {
|
||||
const priorityMap: Record<string, string> = {
|
||||
low: '低',
|
||||
medium: '中',
|
||||
high: '高',
|
||||
critical: '紧急',
|
||||
}
|
||||
return priorityMap[priority] || '-'
|
||||
}
|
||||
|
||||
// 获取优先级颜色
|
||||
const getPriorityColor = (priority: string) => {
|
||||
const colorMap: Record<string, string> = {
|
||||
low: 'blue',
|
||||
medium: 'green',
|
||||
high: 'orange',
|
||||
critical: 'red',
|
||||
}
|
||||
return colorMap[priority] || 'gray'
|
||||
}
|
||||
|
||||
// 获取触发类型文本
|
||||
const getTriggerTypeText = (triggerType: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
manual: '手动',
|
||||
scheduled: '定时',
|
||||
event: '事件',
|
||||
api: 'API',
|
||||
}
|
||||
return typeMap[triggerType] || '-'
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
fetchTemplatesData()
|
||||
}
|
||||
|
||||
// 处理表单模型更新
|
||||
const handleFormModelUpdate = (value: any) => {
|
||||
formModel.value = {
|
||||
...formModel.value,
|
||||
...value,
|
||||
}
|
||||
}
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
formModel.value = {
|
||||
status: '',
|
||||
}
|
||||
pagination.current = 1
|
||||
fetchTemplatesData()
|
||||
}
|
||||
|
||||
// 分页变化
|
||||
const handlePageChange = (current: number) => {
|
||||
pagination.current = current
|
||||
fetchTemplatesData()
|
||||
}
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
fetchTemplatesData()
|
||||
Message.success('数据已刷新')
|
||||
}
|
||||
|
||||
// 新建模板
|
||||
const handleCreate = () => {
|
||||
currentTemplateId.value = undefined
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 编辑模板
|
||||
const handleEdit = (record: any) => {
|
||||
currentTemplateId.value = record.id
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 启用模板
|
||||
const handleActivate = async (record: any) => {
|
||||
try {
|
||||
const res = await activateTemplate(record.id)
|
||||
if (res.code === 0) {
|
||||
Message.success('启用成功')
|
||||
fetchTemplatesData()
|
||||
} else {
|
||||
Message.error(res.msg || '启用失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('启用模板失败:', error)
|
||||
Message.error('启用模板失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 禁用模板
|
||||
const handleDeactivate = async (record: any) => {
|
||||
try {
|
||||
const res = await deactivateTemplate(record.id)
|
||||
if (res.code === 0) {
|
||||
Message.success('禁用成功')
|
||||
fetchTemplatesData()
|
||||
} else {
|
||||
Message.error(res.msg || '禁用失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('禁用模板失败:', error)
|
||||
Message.error('禁用模板失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除模板
|
||||
const handleDelete = (record: any) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除模板「${record.template_name}」吗?删除后将无法恢复,且可能影响历史工单的引用。`,
|
||||
okText: '删除',
|
||||
okButtonProps: { status: 'danger' },
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
const res = await deleteTemplate(record.id)
|
||||
if (res.code === 0) {
|
||||
Message.success('删除成功')
|
||||
fetchTemplatesData()
|
||||
} else {
|
||||
Message.error(res.msg || '删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除模板失败:', error)
|
||||
Message.error('删除模板失败')
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 按模板创建工单
|
||||
const handleCreateTicket = async (record: any) => {
|
||||
try {
|
||||
const res = await createTicketByTemplate(record.id)
|
||||
if (res.code === 0) {
|
||||
Message.success('工单创建成功')
|
||||
// 可以在这里跳转到工单详情页
|
||||
// if (res.details?.id) {
|
||||
// router.push(`/feedback/ticket/${res.details.id}`)
|
||||
// }
|
||||
fetchTemplatesData()
|
||||
} else {
|
||||
Message.error(res.msg || '工单创建失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('按模板创建工单失败:', error)
|
||||
Message.error(error.msg || '工单创建失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 表单提交成功
|
||||
const handleFormSuccess = () => {
|
||||
fetchTemplatesData()
|
||||
}
|
||||
|
||||
// 初始化加载数据
|
||||
onMounted(() => {
|
||||
fetchTemplatesData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'TemplateList',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user