18 changed files with 1176 additions and 51 deletions
@ -0,0 +1,179 @@ |
|||
<template> |
|||
<div class="knowledge-base"> |
|||
<a-row :gutter="16"> |
|||
<!-- 左侧目录树 --> |
|||
<a-col :span="6"> |
|||
<a-card class="tree-card"> |
|||
<template #title> |
|||
<span class="card-title"> |
|||
<folder-outlined /> 知识库目录 |
|||
</span> |
|||
</template> |
|||
<a-tree |
|||
v-model:selectedKeys="selectedKeys" |
|||
v-model:expandedKeys="expandedKeys" |
|||
:tree-data="treeData" |
|||
@select="onSelect" |
|||
/> |
|||
</a-card> |
|||
</a-col> |
|||
|
|||
<!-- 右侧内容区 --> |
|||
<a-col :span="18"> |
|||
<a-card class="content-card"> |
|||
<template #title> |
|||
<span class="card-title"> |
|||
<book-outlined /> 知识库内容 |
|||
</span> |
|||
</template> |
|||
|
|||
<!-- 快捷操作区域 --> |
|||
<div class="quick-actions"> |
|||
<a-space> |
|||
<a-button type="primary" v-for="action in quickActions" :key="action.title" @click="action.onClick"> |
|||
<template #icon><component :is="action.icon" /></template> |
|||
{{ action.title }} |
|||
</a-button> |
|||
</a-space> |
|||
</div> |
|||
|
|||
<!-- 内容展示区域 --> |
|||
<div class="content-area"> |
|||
<template v-if="selectedKeys.length"> |
|||
<!-- 这里可以根据选中的目录显示具体内容 --> |
|||
<div class="selected-content"> |
|||
已选择: {{ selectedKeys[0] }} |
|||
</div> |
|||
</template> |
|||
<template v-else> |
|||
<a-empty description="请选择左侧目录" /> |
|||
</template> |
|||
</div> |
|||
</a-card> |
|||
</a-col> |
|||
</a-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
import { |
|||
FolderOutlined, |
|||
BookOutlined, |
|||
FileTextOutlined, |
|||
TeamOutlined, |
|||
SettingOutlined, |
|||
PlusOutlined, |
|||
EditOutlined, |
|||
DeleteOutlined, |
|||
ExportOutlined |
|||
} from '@ant-design/icons-vue'; |
|||
|
|||
// 目录树数据 |
|||
const treeData = ref([ |
|||
{ |
|||
title: '产品文档', |
|||
key: '1', |
|||
children: [ |
|||
{ title: '使用指南', key: '1-1' }, |
|||
{ title: '常见问题', key: '1-2' }, |
|||
], |
|||
}, |
|||
{ |
|||
title: '技术文档', |
|||
key: '2', |
|||
children: [ |
|||
{ title: 'API文档', key: '2-1' }, |
|||
{ title: '开发规范', key: '2-2' }, |
|||
], |
|||
}, |
|||
]); |
|||
|
|||
// 树选择状态 |
|||
const selectedKeys = ref<string[]>([]); |
|||
const expandedKeys = ref<string[]>(['1', '2']); |
|||
|
|||
// 功能卡片数据 |
|||
const featureCards = ref([ |
|||
{ |
|||
title: '文档管理', |
|||
description: '集中管理所有知识文档', |
|||
icon: FileTextOutlined, |
|||
color: '#1890ff', |
|||
}, |
|||
{ |
|||
title: '团队协作', |
|||
description: '支持多人协同编辑', |
|||
icon: TeamOutlined, |
|||
color: '#52c41a', |
|||
}, |
|||
{ |
|||
title: '系统设置', |
|||
description: '自定义知识库配置', |
|||
icon: SettingOutlined, |
|||
color: '#722ed1', |
|||
}, |
|||
]); |
|||
|
|||
// 快捷操作按钮 |
|||
const quickActions = ref([ |
|||
{ |
|||
title: '新建文档', |
|||
icon: PlusOutlined, |
|||
onClick: () => console.log('新建文档'), |
|||
}, |
|||
{ |
|||
title: '编辑文档', |
|||
icon: EditOutlined, |
|||
onClick: () => console.log('编辑文档'), |
|||
}, |
|||
{ |
|||
title: '删除文档', |
|||
icon: DeleteOutlined, |
|||
onClick: () => console.log('删除文档'), |
|||
}, |
|||
{ |
|||
title: '导出文档', |
|||
icon: ExportOutlined, |
|||
onClick: () => console.log('导出文档'), |
|||
}, |
|||
]); |
|||
|
|||
// 树节点选择事件 |
|||
const onSelect = (selectedKeys: string[], info: any) => { |
|||
console.log('selected', selectedKeys, info); |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.knowledge-base { |
|||
padding: 24px; |
|||
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7eb 100%); |
|||
min-height: 100vh; |
|||
|
|||
.tree-card, |
|||
.content-card { |
|||
min-height: calc(100vh - 50px); |
|||
background: #fff; |
|||
border-radius: 8px; |
|||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
|||
|
|||
.quick-actions { |
|||
margin-bottom: 16px; |
|||
padding: 16px 0; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
} |
|||
|
|||
.content-area { |
|||
padding: 16px 0; |
|||
min-height: 300px; |
|||
|
|||
.selected-content { |
|||
padding: 16px; |
|||
background: #fafafa; |
|||
border-radius: 4px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,62 @@ |
|||
import type { TableColumn } from '@/components/core/dynamic-table'; |
|||
import { DictEnum } from '@/enums/dictEnum'; |
|||
import { getDictionaryByTypeName } from '@/utils/dict'; |
|||
export type TableListItem = API.KnowledgeBaseType; |
|||
export type TableColumnItem = TableColumn<TableListItem>; |
|||
|
|||
// const questionTypeList = await getDictionaryByTypeName(DictEnum.QUESTION_TYPE);
|
|||
|
|||
// 数据项类型
|
|||
// export type ListItemType = typeof tableData[number];
|
|||
// 使用TableColumn<ListItemType> 将会限制dataIndex的类型,但换来的是dataIndex有类型提示
|
|||
export const baseColumns: TableColumnItem[] = [ |
|||
{ |
|||
title: '标题', |
|||
align: 'center', |
|||
dataIndex: 'title', |
|||
// sorter: true,
|
|||
width: 150, |
|||
resizable: true, |
|||
formItemProps: { |
|||
defaultValue: '', |
|||
required: false, |
|||
}, |
|||
}, |
|||
{ |
|||
title: '描述', |
|||
align: 'center', |
|||
dataIndex: 'description', |
|||
width: 150, |
|||
resizable: true, |
|||
formItemProps: { |
|||
defaultValue: '', |
|||
required: false, |
|||
}, |
|||
}, |
|||
|
|||
{ |
|||
title: '标签', |
|||
align: 'center', |
|||
dataIndex: 'tags', |
|||
width: 200, |
|||
hideInSearch: true, |
|||
customRender: ({ record }) => { |
|||
const tags = record.tags?.join(', '); |
|||
return <div>{{ tags }}</div>; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '创建时间', |
|||
align: 'center', |
|||
width: 200, |
|||
dataIndex: 'createTime', |
|||
formItemProps: { |
|||
defaultValue: '', |
|||
required: false, |
|||
component: 'RangePicker', |
|||
componentProps: { |
|||
valueFormat: 'YYYY-MM-DD', |
|||
}, |
|||
}, |
|||
}, |
|||
]; |
|||
@ -0,0 +1,32 @@ |
|||
export const stateTypeList: any = [ |
|||
// Init(0,"初始化"),
|
|||
// Back(1,"退回"),
|
|||
// Develop(2,"开发"),
|
|||
// Test(3,"测试"),
|
|||
// End(4,"结束"),
|
|||
{ |
|||
label: '初始化', |
|||
value: 0, |
|||
color: '#f50', |
|||
}, |
|||
{ |
|||
label: '退回', |
|||
value: 1, |
|||
color: '#2db7f5', |
|||
}, |
|||
{ |
|||
label: '开发', |
|||
value: 2, |
|||
color: '#87d068', |
|||
}, |
|||
{ |
|||
label: '测试', |
|||
value: 3, |
|||
color: '#108ee9', |
|||
}, |
|||
{ |
|||
label: '结束', |
|||
value: 4, |
|||
color: '#f50', |
|||
}, |
|||
]; |
|||
@ -0,0 +1,156 @@ |
|||
<!-- |
|||
功能:功能描述 |
|||
作者:Aaron |
|||
时间:2023年07月31日 11:51:23 |
|||
版本:v1.0 |
|||
修改记录: |
|||
修改内容: |
|||
修改人员: |
|||
修改时间: |
|||
--> |
|||
<template> |
|||
<div class="bg-[#fff] p-5"> |
|||
<a-spin :spinning="loading"> |
|||
<div class="my-3"> |
|||
<a-steps :current="current" size="small"> |
|||
<a-step title="初始化"> |
|||
<template #icon> |
|||
<SvgIcon :size="22" name="init" /> |
|||
</template> |
|||
</a-step> |
|||
|
|||
<a-step title="退回"> |
|||
<template #icon> |
|||
<SvgIcon :size="24" name="back" /> |
|||
</template> |
|||
</a-step> |
|||
<a-step title="开发"> |
|||
<template #icon> |
|||
<SvgIcon :size="24" name="dev" /> |
|||
</template> |
|||
</a-step> |
|||
<a-step title="测试"> |
|||
<template #icon> |
|||
<SvgIcon :size="24" name="test" /> |
|||
</template> |
|||
</a-step> |
|||
<a-step title="结束"> |
|||
<template #icon> |
|||
<SvgIcon :size="24" name="end" /> |
|||
</template> |
|||
</a-step> |
|||
</a-steps> |
|||
</div> |
|||
|
|||
<a-tabs default-active-key="1"> |
|||
<a-tab-pane key="1" tab="基础信息"> |
|||
<SchemaForm> |
|||
<template #solution="{ formModel, field }"> |
|||
<div class="ql-box"> |
|||
<QuillEditor |
|||
theme="snow" |
|||
v-model:content="formModel[field]" |
|||
:options="editorOptions" |
|||
:readOnly="true" |
|||
contentType="html" |
|||
/> |
|||
</div> |
|||
</template> |
|||
</SchemaForm> |
|||
</a-tab-pane> |
|||
<a-tab-pane key="2" tab="处理历史"> |
|||
<a-steps progress-dot :current="historyList.length - 1" direction="vertical"> |
|||
<a-step |
|||
v-for="(item, index) in historyList" |
|||
:key="index" |
|||
:title="item.title" |
|||
:description="item.remark" |
|||
/> |
|||
</a-steps> |
|||
</a-tab-pane> |
|||
</a-tabs> |
|||
</a-spin> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { QuillEditor } from '@vueup/vue-quill'; |
|||
import '@vueup/vue-quill/dist/vue-quill.snow.css'; |
|||
import { useForm } from '@/components/core/schema-form'; |
|||
import { getEditFormSchema } from './formSchemas'; |
|||
import { useRoute } from 'vue-router'; |
|||
import { onMounted, ref } from 'vue'; |
|||
import { findOneById } from '@/api/issue'; |
|||
import { SvgIcon } from '@/components/basic/svg-icon'; |
|||
import { stateTypeList } from './data'; |
|||
const route = useRoute(); |
|||
const editorOptions = ref({ |
|||
theme: 'snow', |
|||
modules: { |
|||
toolbar: [ |
|||
[{ header: '1' }, { header: '2' }, { font: [] }], |
|||
[{ list: 'ordered' }, { list: 'bullet' }], |
|||
['bold', 'italic', 'underline'], |
|||
['link', 'image'], // 添加图片按钮 |
|||
], |
|||
}, |
|||
}); |
|||
const loading = ref(false); |
|||
|
|||
const historyList = ref< |
|||
Array<{ |
|||
state: number; |
|||
remark: string; |
|||
title?: string; |
|||
}> |
|||
>([]); |
|||
|
|||
const current = ref(0); |
|||
|
|||
const { id } = route.query; |
|||
|
|||
const [SchemaForm, formRef] = useForm({ |
|||
labelWidth: 150, |
|||
schemas: getEditFormSchema({}, true), |
|||
showActionButtonGroup: false, |
|||
actionColOptions: { |
|||
span: 24, |
|||
}, |
|||
submitButtonOptions: { |
|||
text: '保存', |
|||
}, |
|||
disabled: true, |
|||
}); |
|||
|
|||
onMounted(async () => { |
|||
loading.value = true; |
|||
const res = await findOneById({ id: id as string }); |
|||
loading.value = false; |
|||
|
|||
if (res?.files && Array.isArray(res.files) && res.files.length) { |
|||
res.files = res.files.map((e) => { |
|||
return { |
|||
name: e.originalFilename, |
|||
url: e.url, |
|||
id: e.id, |
|||
}; |
|||
}); |
|||
} |
|||
|
|||
if (res.historys && Array.isArray(res.historys) && res.historys.length) { |
|||
historyList.value = res.historys.map((e) => { |
|||
return { |
|||
state: e.state, |
|||
remark: `${e.createUserName}于${e.createTime} : ${e.remark}`, |
|||
title: stateTypeList.find((item) => item.value === e.state)?.label, |
|||
}; |
|||
}); |
|||
} |
|||
|
|||
current.value = res?.state; |
|||
|
|||
formRef?.setFieldsValue(res); |
|||
formRef?.clearValidate(); |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped></style> |
|||
@ -0,0 +1,83 @@ |
|||
import type { FormSchema } from '@/components/core/schema-form/'; |
|||
import { TableListItem } from './columns'; |
|||
import { commonUpload } from '@/api/upload'; |
|||
import { message, Button } from 'ant-design-vue'; |
|||
import { getFileExtension } from '@/utils/fileUtils'; |
|||
import { UploadOutlined } from '@ant-design/icons-vue'; |
|||
import { stateTypeList } from './data'; |
|||
import { DictEnum } from '@/enums/dictEnum'; |
|||
import { getDictionaryByTypeName } from '@/utils/dict'; |
|||
import { fetchProdList, fetchVersionPageList } from '@/api/prodVersion'; |
|||
const questionTypeList = await getDictionaryByTypeName(DictEnum.QUESTION_TYPE); |
|||
// 编辑页字段
|
|||
export const getEditFormSchema: ( |
|||
row?: Partial<TableListItem>, |
|||
isDetail?: boolean, |
|||
) => FormSchema[] = (record = {}, isDetail = false) => { |
|||
console.log('questionTypeList: ', questionTypeList); |
|||
|
|||
return [ |
|||
{ |
|||
field: 'title', |
|||
component: 'Input', |
|||
componentProps: { |
|||
showCount: true, |
|||
maxlength: 150, |
|||
}, |
|||
label: '问题标题', |
|||
colProps: { |
|||
span: 12, |
|||
}, |
|||
rules: [{ required: true }], |
|||
}, |
|||
{ |
|||
field: 'versionId', |
|||
component: 'Select', |
|||
componentProps: { |
|||
options: [], |
|||
}, |
|||
label: '版本', |
|||
colProps: { |
|||
span: 12, |
|||
}, |
|||
}, |
|||
{ |
|||
field: 'description', |
|||
component: 'InputTextArea', |
|||
componentProps: { |
|||
rows: 4, |
|||
placeholder: '请输入问题描述', |
|||
showCount: true, |
|||
maxlength: 150, |
|||
}, |
|||
label: '问题描述', |
|||
colProps: { |
|||
span: 24, |
|||
}, |
|||
rules: [{ required: true }], |
|||
}, |
|||
{ |
|||
label: '解决方案', |
|||
field: 'solution', |
|||
colProps: { |
|||
span: 24, |
|||
}, |
|||
// vShow: !isDetail,
|
|||
// rules: [{ required: true, type: 'array' }],
|
|||
slot: 'solution', |
|||
}, |
|||
] as any; |
|||
}; |
|||
|
|||
export const getFlowFormSchema: (row?: Partial<TableListItem>) => FormSchema[] = (record = {}) => { |
|||
return [ |
|||
{ |
|||
field: 'remark', |
|||
component: 'InputTextArea', |
|||
label: '文字补充内容', |
|||
colProps: { |
|||
span: 24, |
|||
}, |
|||
}, |
|||
]; |
|||
}; |
|||
@ -0,0 +1,520 @@ |
|||
<!-- |
|||
功能:功能描述 |
|||
作者:Aaron.Wu |
|||
时间:2023年05月25日 17:00:26 |
|||
版本:v1.0 |
|||
修改记录: |
|||
修改内容: |
|||
修改人员: |
|||
修改时间: |
|||
--> |
|||
<template> |
|||
<DynamicTable |
|||
size="small" |
|||
showIndex |
|||
headerTitle="问题工单列表" |
|||
titleTooltip="" |
|||
:data-request="loadData" |
|||
:columns="columns" |
|||
row-key="id" |
|||
@resize-column="handleResizeColumn" |
|||
:row-selection="rowSelection" |
|||
:scroll="{ x: '100vw' }" |
|||
> |
|||
<template v-if="isCheckRows" #title> |
|||
<Alert class="w-full" type="info" show-icon> |
|||
<template #message> |
|||
已选 {{ isCheckRows }} 项 |
|||
<a-button type="link" @click="rowSelection.selectedRowKeys = []">取消选择</a-button> |
|||
</template> |
|||
</Alert> |
|||
</template> |
|||
<template #toolbar> |
|||
<a-button type="primary" @click="handleAdd">新增</a-button> |
|||
<a-button |
|||
type="danger" |
|||
:disabled="!isCheckRows" |
|||
@click="delRowConfirm(rowSelection.selectedRowKeys)" |
|||
> |
|||
<DeleteOutlined /> 删除 |
|||
</a-button> |
|||
</template> |
|||
</DynamicTable> |
|||
<DraggableModal |
|||
v-model:visible="visible" |
|||
:width="700" |
|||
:bodyStyle="{ |
|||
height: '70vh', |
|||
}" |
|||
:force-render="true" |
|||
:title="`${curRecord.id ? '编辑' : '新增'}问题工单`" |
|||
@ok="handleOk" |
|||
@cancel="handleCancel" |
|||
> |
|||
<SchemaForm> |
|||
<template #solution="{ formModel, field }"> |
|||
<div class="ql-box"> |
|||
<QuillEditor |
|||
theme="snow" |
|||
v-model:content="formModel[field]" |
|||
:options="editorOptions" |
|||
contentType="html" |
|||
/> |
|||
</div> |
|||
</template> |
|||
</SchemaForm> |
|||
</DraggableModal> |
|||
</template> |
|||
|
|||
<script lang="tsx" setup> |
|||
import { QuillEditor } from '@vueup/vue-quill'; |
|||
import '@vueup/vue-quill/dist/vue-quill.snow.css'; |
|||
import { type TableListItem, baseColumns } from './columns'; |
|||
import { useTable, type OnChangeCallbackParams } from '@/components/core/dynamic-table'; |
|||
import { |
|||
fetchIssuePageList, |
|||
createIssue, |
|||
updateIssue, |
|||
deleteIssueById, |
|||
deleteBatchIssueById, |
|||
findOneById, |
|||
updateIssueState, |
|||
addToknowledge, |
|||
} from '@/api/issue'; |
|||
import { computed, nextTick, ref } from 'vue'; |
|||
import { Modal, message, Alert } from 'ant-design-vue'; |
|||
import { useRouter } from 'vue-router'; |
|||
import { useFormModal } from '@/hooks/useModal/index'; |
|||
import { getEditFormSchema, getFlowFormSchema } from './formSchemas'; |
|||
import { ExclamationCircleOutlined, DeleteOutlined } from '@ant-design/icons-vue'; |
|||
import { stateTypeList } from './data'; |
|||
import { fetchVersionPageList } from '@/api/prodVersion'; |
|||
import { DraggableModal } from '@/components/core/draggable-modal'; |
|||
import { useForm } from '@/components/core/schema-form'; |
|||
|
|||
defineOptions({ |
|||
name: 'issue', |
|||
}); |
|||
|
|||
const router = useRouter(); |
|||
|
|||
const curRecord = ref<Partial<TableListItem>>({}); |
|||
const visible = ref(false); |
|||
|
|||
const editorOptions = ref({ |
|||
theme: 'snow', |
|||
modules: { |
|||
toolbar: [ |
|||
[{ header: '1' }, { header: '2' }, { font: [] }], |
|||
[{ list: 'ordered' }, { list: 'bullet' }], |
|||
['bold', 'italic', 'underline'], |
|||
['link', 'image'], // 添加图片按钮 |
|||
], |
|||
}, |
|||
}); |
|||
|
|||
const [showModal] = useFormModal(); |
|||
|
|||
const [showFlowModal] = useFormModal(); |
|||
|
|||
const [SchemaForm, formRef] = useForm({ |
|||
labelWidth: 100, |
|||
labelAlign: 'right', |
|||
schemas: getEditFormSchema(curRecord.value), |
|||
showActionButtonGroup: false, |
|||
actionColOptions: { |
|||
span: 24, |
|||
}, |
|||
submitButtonOptions: { |
|||
text: '保存', |
|||
}, |
|||
}); |
|||
|
|||
const handleOk = async () => { |
|||
const values = await formRef?.validate(); |
|||
if (values) { |
|||
console.log('values: ', values); |
|||
values.id = curRecord.value.id; |
|||
|
|||
if (values.files && Array.isArray(values.files) && values.files.length) { |
|||
values.files = values.files.map((e) => { |
|||
if (e.response) { |
|||
return { |
|||
name: e.name, |
|||
url: e.response.url, |
|||
id: e.response.id, |
|||
}; |
|||
} |
|||
return { |
|||
...e, |
|||
}; |
|||
}); |
|||
values.fileIds = values.files.map((e) => e.id).join(','); |
|||
} |
|||
|
|||
await (values.id ? updateIssue : createIssue)(values); |
|||
message.success(`${values.id ? '编辑' : '新增'}成功`); |
|||
visible.value = false; |
|||
resetFormFields(); |
|||
dynamicTableInstance?.reload(); |
|||
} |
|||
}; |
|||
|
|||
const handleCancel = () => { |
|||
visible.value = false; |
|||
}; |
|||
|
|||
const columns: any = [ |
|||
...baseColumns, |
|||
{ |
|||
title: '操作', |
|||
width: 100, |
|||
dataIndex: 'ACTION', |
|||
hideInSearch: true, |
|||
align: 'center', |
|||
fixed: 'right', |
|||
actions: ({ record }) => { |
|||
const { state } = record; |
|||
return [ |
|||
{ |
|||
icon: 'searchoutlined', |
|||
color: '#3b82f6', |
|||
label: '查看', |
|||
onClick: () => handleView(record), |
|||
}, |
|||
{ |
|||
icon: 'edit', |
|||
color: '#3b82f6', |
|||
size: '15', |
|||
label: '修改', |
|||
onClick: () => handleEdit(record), |
|||
}, |
|||
|
|||
{ |
|||
icon: 'delete', |
|||
color: '#ec6f6f', |
|||
label: '删除', |
|||
popConfirm: { |
|||
title: '确定要删除吗?', |
|||
onConfirm: () => handleDelete(record.id), |
|||
}, |
|||
}, |
|||
]; |
|||
}, |
|||
}, |
|||
]; |
|||
|
|||
const changeState = async (record: Partial<TableListItem>, state: number) => { |
|||
await openFlowModal(record, state); |
|||
}; |
|||
/** |
|||
* @description 打开问题工单弹窗 |
|||
*/ |
|||
const openIssueModal = async (record: Partial<TableListItem> = {}, isReadOnly = false) => { |
|||
const [formRef] = await showModal<any>({ |
|||
modalProps: { |
|||
title: `${isReadOnly ? '查看' : record.id ? '编辑' : '新增'}问题工单`, |
|||
width: 700, |
|||
onFinish: async (values) => { |
|||
console.log('新增/编辑问题工单', values); |
|||
values.id = record.id; |
|||
|
|||
if (values.files && Array.isArray(values.files) && values.files.length) { |
|||
values.files = values.files.map((e) => { |
|||
if (e.response) { |
|||
return { |
|||
name: e.name, |
|||
url: e.response.url, |
|||
id: e.response.id, |
|||
}; |
|||
} |
|||
return { |
|||
...e, |
|||
}; |
|||
}); |
|||
values.fileIds = values.files.map((e) => e.id).join(','); |
|||
} |
|||
|
|||
await (record.id ? updateIssue : createIssue)(values); |
|||
message.success(`${record.id ? '编辑' : '新增'}成功`); |
|||
dynamicTableInstance?.reload(); |
|||
}, |
|||
footer: isReadOnly ? null : undefined, |
|||
}, |
|||
formProps: { |
|||
labelWidth: 100, |
|||
schemas: getEditFormSchema(record, isReadOnly) as any, |
|||
autoSubmitOnEnter: true, |
|||
disabled: isReadOnly, |
|||
}, |
|||
}); |
|||
|
|||
formRef?.setFieldsValue(record); |
|||
|
|||
if (record?.productId) { |
|||
const { data } = await fetchVersionPageList({ |
|||
productId: record?.productId, |
|||
current: 1, |
|||
size: 999, |
|||
}); |
|||
if (data && Array.isArray(data) && data.length) { |
|||
formRef?.updateSchema({ |
|||
field: 'versionId', |
|||
componentProps: { |
|||
options: data.map((e) => { |
|||
return { |
|||
label: e.name, |
|||
value: e.id, |
|||
}; |
|||
}), |
|||
}, |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
const openFlowModal = async (record: Partial<TableListItem> = {}, state) => { |
|||
const flowTitle = stateTypeList.find((item) => item.value === state)?.label; |
|||
const [formRef] = await showFlowModal<any>({ |
|||
modalProps: { |
|||
title: `${flowTitle}问题工单`, |
|||
width: 700, |
|||
onFinish: async (values) => { |
|||
await updateIssueState({ |
|||
id: record.id, |
|||
state: state, |
|||
...values, |
|||
}); |
|||
message.success(`${flowTitle}成功`); |
|||
dynamicTableInstance?.reload(); |
|||
}, |
|||
}, |
|||
formProps: { |
|||
labelWidth: 100, |
|||
schemas: getFlowFormSchema(record) as any, |
|||
autoSubmitOnEnter: true, |
|||
}, |
|||
}); |
|||
|
|||
formRef?.setFieldsValue(record); |
|||
}; |
|||
|
|||
const handleView = (record: TableListItem) => { |
|||
router.push({ |
|||
path: '/question/issueDetail', |
|||
query: { |
|||
id: record.id, |
|||
}, |
|||
}); |
|||
}; |
|||
|
|||
const resetFormFields = () => { |
|||
formRef?.resetFields(); |
|||
}; |
|||
|
|||
const openModal = () => { |
|||
visible.value = true; |
|||
formRef.clearValidate(); |
|||
}; |
|||
|
|||
const handleAddToKnowledge = async (record: TableListItem) => { |
|||
console.log('record: ', record); |
|||
if (!record.id) return; |
|||
const res = await addToknowledge({ |
|||
id: record.id, |
|||
}); |
|||
if (res) { |
|||
message.success('添加到知识库成功'); |
|||
dynamicTableInstance?.reload(); |
|||
} |
|||
}; |
|||
|
|||
const handleAdd = () => { |
|||
formRef?.setFieldsValue(curRecord.value); |
|||
resetFormFields(); |
|||
openModal(); |
|||
}; |
|||
|
|||
const handleEdit = async (record: TableListItem) => { |
|||
if (record?.id) { |
|||
const res = await findOneById({ |
|||
id: record.id, |
|||
}); |
|||
console.log('res: ', res); |
|||
if (res?.files && Array.isArray(res.files) && res.files.length) { |
|||
res.files = res.files.map((e) => { |
|||
return { |
|||
name: e.originalFilename, |
|||
url: e.url, |
|||
id: e.id, |
|||
}; |
|||
}); |
|||
} |
|||
curRecord.value = res; |
|||
|
|||
formRef?.setFieldsValue(res); |
|||
|
|||
if (res?.productId) { |
|||
const { data } = await fetchVersionPageList({ |
|||
productId: res?.productId, |
|||
current: 1, |
|||
size: 999, |
|||
}); |
|||
if (data && Array.isArray(data) && data.length) { |
|||
formRef?.updateSchema({ |
|||
field: 'versionId', |
|||
componentProps: { |
|||
options: data.map((e) => { |
|||
return { |
|||
label: e.name, |
|||
value: e.id, |
|||
}; |
|||
}), |
|||
}, |
|||
}); |
|||
} |
|||
} |
|||
|
|||
nextTick(() => { |
|||
openModal(); |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
const handleShow = async (record: TableListItem) => { |
|||
openIssueModal(record, true); |
|||
}; |
|||
|
|||
const handleDelete = (id: string) => { |
|||
delRowConfirm(id); |
|||
}; |
|||
|
|||
/** |
|||
* @description 表格删除行 |
|||
*/ |
|||
const delRowConfirm = async (id: string | string[]) => { |
|||
Modal.confirm({ |
|||
title: '确定要删除所选的问题工单吗?', |
|||
icon: <ExclamationCircleOutlined />, |
|||
centered: true, |
|||
onOk: async () => { |
|||
if (Array.isArray(id)) { |
|||
// 多个删除 |
|||
await deleteBatchIssueById(id).finally(dynamicTableInstance?.reload); |
|||
} else { |
|||
await deleteIssueById({ id }).finally(dynamicTableInstance?.reload); |
|||
} |
|||
}, |
|||
}); |
|||
}; |
|||
|
|||
const [DynamicTable, dynamicTableInstance] = useTable(); |
|||
|
|||
const rowSelection = ref<any>({ |
|||
selectedRowKeys: [] as string[], |
|||
onChange: (selectedRowKeys: string[], selectedRows: TableListItem[]) => { |
|||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); |
|||
rowSelection.value.selectedRowKeys = selectedRowKeys; |
|||
}, |
|||
}); |
|||
|
|||
// 是否勾选了表格行 |
|||
const isCheckRows = computed(() => rowSelection.value.selectedRowKeys.length); |
|||
|
|||
const loadData = async ( |
|||
params, |
|||
onChangeParams?: OnChangeCallbackParams, |
|||
): Promise<API.TableListResult> => { |
|||
console.log('params', params); |
|||
console.log('onChangeParams', onChangeParams); |
|||
// 手动设置搜索表单的搜索项 |
|||
// dynamicTableInstance?.getQueryFormRef()?.updateSchema?.([ |
|||
// { |
|||
// field: 'price', |
|||
// componentProps: { |
|||
// options: [ |
|||
// { |
|||
// label: '0-199', |
|||
// value: '0-199', |
|||
// }, |
|||
// { |
|||
// label: '200-999', |
|||
// value: '200-999', |
|||
// }, |
|||
// ], |
|||
// }, |
|||
// }, |
|||
// ]); |
|||
|
|||
// return new Promise((resolve) => { |
|||
// setTimeout(() => { |
|||
// resolve({ |
|||
// list: [ |
|||
// { |
|||
// id: '1', |
|||
// name: '1', |
|||
// state: 0, |
|||
// files: [ |
|||
// { |
|||
// name: '文件条款信息.xlsx', |
|||
// url: 'http://192.168.2.116:8089/oss/resource/2025-04-07/67f390f06f14d107a557e10f.xlsx', |
|||
// id: 'vc-upload-1744009547396-6', |
|||
// }, |
|||
// { |
|||
// name: '文件条款信息 (1).xlsx', |
|||
// url: 'http://192.168.2.116:8089/oss/resource/2025-04-07/67f390fc6f14d107a557e110.xlsx', |
|||
// id: 'vc-upload-1744009547396-8', |
|||
// }, |
|||
// ], |
|||
// }, |
|||
// ], |
|||
// ...params, |
|||
// }); |
|||
// }, 500); |
|||
// }); |
|||
|
|||
const dateRange = { |
|||
startDate: params.createTime |
|||
? params.createTime.length |
|||
? params.createTime[0] |
|||
: undefined |
|||
: undefined, |
|||
endDate: params.createTime |
|||
? params.createTime.length |
|||
? params.createTime[1] |
|||
: undefined |
|||
: undefined, |
|||
}; |
|||
|
|||
const res = await fetchIssuePageList({ |
|||
...params, |
|||
...dateRange, |
|||
current: params.page, |
|||
size: params.limit, |
|||
}); |
|||
console.log('res: ', res); |
|||
rowSelection.value.selectedRowKeys = []; |
|||
|
|||
return { |
|||
list: res.data.records, |
|||
pagination: { |
|||
total: Number(res.data.total), |
|||
...params, |
|||
}, |
|||
}; |
|||
}; |
|||
const handleResizeColumn = (w, col) => { |
|||
col.width = w; |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="less"> |
|||
.action-divider { |
|||
margin: 0 5px; |
|||
} |
|||
.ql-box { |
|||
.ql-editor { |
|||
height: 400px !important; |
|||
} |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue