34 changed files with 1677 additions and 127 deletions
@ -0,0 +1,104 @@ |
|||||
|
/* |
||||
|
* @Author: AaronWu 2463371514@qq.com |
||||
|
* @Date: 2025-04-01 09:09:04 |
||||
|
* @LastEditors: AaronWu 2463371514@qq.com |
||||
|
* @LastEditTime: 2025-04-01 11:34:26 |
||||
|
* @FilePath: /KnowledgeBaseSupportManage/src/api/user/index.ts |
||||
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
|
*/ |
||||
|
import type { BaseResponse } from '@/utils/request'; |
||||
|
import { request } from '@/utils/request'; |
||||
|
/** |
||||
|
* @description 查询列表 |
||||
|
* @param {SearchListParams} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function fetchKnowledgeBaseList(data: API.SearchListParams) { |
||||
|
return request<BaseResponse<API.SearchListResult>>( |
||||
|
{ |
||||
|
url: 'question/list', |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}, |
||||
|
{ |
||||
|
isGetDataDirectly: false, |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 查询分页列表 |
||||
|
* @param {SearchPageListParams} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function fetchKnowledgeBasePageList(data: API.SearchPageListParams) { |
||||
|
return request<BaseResponse<API.SearchPageListResult>>( |
||||
|
{ |
||||
|
url: 'question/list', |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}, |
||||
|
{ |
||||
|
isGetDataDirectly: false, |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 新增单条 |
||||
|
* @param {KnowledgeBaseType} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function createKnowledgeBase(data: API.KnowledgeBaseType) { |
||||
|
return request({ |
||||
|
url: 'question/create', |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 修改单条 |
||||
|
* @param {KnowledgeBaseType} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function updateKnowledgeBase(data: API.KnowledgeBaseType) { |
||||
|
return request({ |
||||
|
url: 'question/update', |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 查询单条 |
||||
|
*/ |
||||
|
export function findOneById(params: { id: string }) { |
||||
|
return request({ |
||||
|
url: `question/getById`, |
||||
|
method: 'get', |
||||
|
params, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 删除单条 ?id=${data.id} |
||||
|
*/ |
||||
|
export function deleteKnowledgeBaseById(params: API.DeleteKnowledgeBaseParams) { |
||||
|
return request({ |
||||
|
url: `question/delById`, |
||||
|
method: 'post', |
||||
|
params, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 删除多条 |
||||
|
*/ |
||||
|
export function deleteBatchKnowledgeBaseById(data: API.DeleteBatchKnowledgeBaseParams) { |
||||
|
return request({ |
||||
|
url: `question/delete`, |
||||
|
method: 'delete', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
declare namespace API { |
||||
|
/** 问题工单类型 */ |
||||
|
type KnowledgeBaseType = { |
||||
|
id?: string; |
||||
|
agent?: string; // 代理商
|
||||
|
attribute?: string; // 问题属性
|
||||
|
billcode?: string; // 问题号
|
||||
|
contacts?: string; // 联系人
|
||||
|
contactsEmail?: string; // 联系人邮箱
|
||||
|
contactsMobile?: string; // 联系人手机号
|
||||
|
createTime?: string; // 创建时间
|
||||
|
createUserid?: number; // 创建人ID
|
||||
|
customer?: string; // 客户
|
||||
|
description?: string; // 问题描述
|
||||
|
dr?: number; // 删除标记
|
||||
|
product?: string; // 产品
|
||||
|
productId?: string; // 产品ID
|
||||
|
version?: string; // 版本
|
||||
|
versionId?: string; // 版本ID
|
||||
|
serviceNumber?: string; // 服务号
|
||||
|
state?: number; // 状态
|
||||
|
title?: string; // 问题标题
|
||||
|
updateTime?: string; // 更新时间
|
||||
|
updateUserid?: number; // 更新人ID
|
||||
|
version?: string; // 版本
|
||||
|
fileList?: any[]; // 附件列表
|
||||
|
}; |
||||
|
|
||||
|
type CreateKnowledgeBaseParams = { |
||||
|
id?: string; |
||||
|
agent?: string; // 代理商
|
||||
|
attribute?: string; // 问题属性
|
||||
|
billcode?: string; // 问题号
|
||||
|
contacts?: string; // 联系人
|
||||
|
contactsEmail?: string; // 联系人邮箱
|
||||
|
contactsMobile?: string; // 联系人手机号
|
||||
|
createTime?: string; // 创建时间
|
||||
|
createUserid?: number; // 创建人ID
|
||||
|
customer?: string; // 客户
|
||||
|
description?: string; // 问题描述
|
||||
|
dr?: number; // 删除标记
|
||||
|
product?: string; // 产品
|
||||
|
serviceNumber?: string; // 服务号
|
||||
|
state?: number; // 状态
|
||||
|
title?: string; // 问题标题
|
||||
|
updateTime?: string; // 更新时间
|
||||
|
updateUserid?: number; // 更新人ID
|
||||
|
version?: string; // 版本
|
||||
|
fileList?: any[]; // 附件列表
|
||||
|
}; |
||||
|
|
||||
|
type DeleteKnowledgeBaseParams = { |
||||
|
id: string; |
||||
|
}; |
||||
|
|
||||
|
type DeleteBatchKnowledgeBaseParams = string[]; |
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
import type { BaseResponse } from '@/utils/request'; |
||||
|
import { request } from '@/utils/request'; |
||||
|
|
||||
|
/** |
||||
|
* @description 查询类别列表 |
||||
|
* @param {SearchPageListParams} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function fetchProdList(data) { |
||||
|
return request<BaseResponse<API.SearchPageListResult>>({ |
||||
|
url: `/product/list`, |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 新增单条 |
||||
|
* @param {ProdType} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function createProd(data: API.ProdType) { |
||||
|
return request({ |
||||
|
url: `/product/create`, |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 修改单条 |
||||
|
* @param {ProdType} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function updateProd(data: API.ProdType) { |
||||
|
return request({ |
||||
|
url: `/product/update`, |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 删除 |
||||
|
*/ |
||||
|
export function deleteProd(data: API.DeleteProdParams) { |
||||
|
return request({ |
||||
|
url: `/product/delete?id=${data.id}`, |
||||
|
method: 'delete', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 根据字典类型查询字典值分页列表 |
||||
|
* @param {SearchPageListParams} params |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function fetchVersionPageList(params: API.SearchPageListParams) { |
||||
|
return request<BaseResponse<API.SearchPageListResult>>( |
||||
|
{ |
||||
|
url: `/productVersion/list`, |
||||
|
method: 'get', |
||||
|
params, |
||||
|
}, |
||||
|
{ |
||||
|
isGetDataDirectly: false, |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 新增单条 |
||||
|
* @param {VersionType} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function createVersion(data: API.VersionType) { |
||||
|
return request({ |
||||
|
url: `/productVersion/create`, |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 修改单条 |
||||
|
* @param {VersionType} data |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function updateVersion(data: API.VersionType) { |
||||
|
return request({ |
||||
|
url: `/productVersion/update`, |
||||
|
method: 'post', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 删除多条 |
||||
|
*/ |
||||
|
export function deleteBatchVersionById(data: API.DeleteBatchVersionParams) { |
||||
|
return request({ |
||||
|
url: `/productVersion/delete`, |
||||
|
method: 'delete', |
||||
|
data, |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
declare namespace API { |
||||
|
type ProdType = { |
||||
|
id?: string; |
||||
|
name: string; |
||||
|
}; |
||||
|
|
||||
|
type DeleteProdParams = { |
||||
|
id: string; |
||||
|
}; |
||||
|
|
||||
|
type VersionType = { |
||||
|
id?: string; |
||||
|
productId: string; |
||||
|
name: string; |
||||
|
remark?: string; |
||||
|
}; |
||||
|
|
||||
|
type DeleteVersionParams = { |
||||
|
id: string; |
||||
|
}; |
||||
|
|
||||
|
type DeleteBatchVersionParams = string[]; |
||||
|
} |
||||
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 646 B |
|
After Width: | Height: | Size: 897 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 476 B |
@ -0,0 +1,3 @@ |
|||||
|
export enum DictEnum { |
||||
|
QUESTION_TYPE = '问题属性', |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
import { fetchDictValueListByType } from '@/api/dict'; |
||||
|
|
||||
|
export const getDictionaryByTypeName = async (name: string) => { |
||||
|
const res = await fetchDictValueListByType({ typeName: name }); |
||||
|
if (res && Array.isArray(res) && res.length) { |
||||
|
return res |
||||
|
.filter((e) => e.enable === 1) |
||||
|
.map((e) => { |
||||
|
return { |
||||
|
label: e.dictValue, |
||||
|
value: e.dictValue, |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export const getLabelByDictId = ( |
||||
|
options: Array<{ label: string; value: string }>, |
||||
|
id: string, |
||||
|
) => {}; |
||||
@ -0,0 +1,150 @@ |
|||||
|
<!-- |
||||
|
功能:功能描述 |
||||
|
作者: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> </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.message" |
||||
|
/> |
||||
|
</a-steps> |
||||
|
</a-tab-pane> |
||||
|
</a-tabs> |
||||
|
</a-spin> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
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 loading = ref(false); |
||||
|
|
||||
|
const historyList = ref< |
||||
|
Array<{ |
||||
|
status: number; |
||||
|
message: string; |
||||
|
title?: string; |
||||
|
}> |
||||
|
>([ |
||||
|
{ |
||||
|
status: 0, |
||||
|
message: '初始化', |
||||
|
}, |
||||
|
{ |
||||
|
status: 1, |
||||
|
message: '退回', |
||||
|
}, |
||||
|
{ |
||||
|
status: 2, |
||||
|
message: '开发', |
||||
|
}, |
||||
|
{ |
||||
|
status: 3, |
||||
|
message: '测试', |
||||
|
}, |
||||
|
{ |
||||
|
status: 4, |
||||
|
message: '结束', |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
historyList.value = historyList.value.map((item) => { |
||||
|
return { |
||||
|
...item, |
||||
|
title: stateTypeList.find((e) => e.value === item.status)?.label, |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
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, |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
current.value = res?.state; |
||||
|
|
||||
|
formRef?.setFieldsValue(res); |
||||
|
formRef?.clearValidate(); |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less" scoped></style> |
||||
@ -0,0 +1,325 @@ |
|||||
|
<template> |
||||
|
<div class="bg-white m-4 mr-2 overflow-hidden h-full"> |
||||
|
<BasicTree |
||||
|
ref="treeRef" |
||||
|
:actionList="actionList" |
||||
|
:beforeRightClick="getRightMenuList" |
||||
|
:checkable="false" |
||||
|
:clickRowToExpand="false" |
||||
|
:treeData="treeData" |
||||
|
checkStrictly |
||||
|
:defaultExpandAll="true" |
||||
|
search |
||||
|
toolbar |
||||
|
:highlight="true" |
||||
|
@select="handleSelect" |
||||
|
:toolbarStrictly="false" |
||||
|
> |
||||
|
<!-- <template #titleBefore="item"> </template> --> |
||||
|
</BasicTree> |
||||
|
<div v-if="query" class="m-4 flex justify-center"> |
||||
|
<a-button class="w-[80%]" type="primary" @click="handleReset()">重置</a-button> |
||||
|
<!-- <Checkbox v-model:checked="recursion" @change="handleQuery()">本级及子级</Checkbox> --> |
||||
|
</div> |
||||
|
<div v-else class="m-4 flex justify-center"> |
||||
|
<a-button type="primary" class="w-[80%]" @click="openTypeModal({})"> |
||||
|
{{ '新增产品' }} |
||||
|
</a-button> |
||||
|
<!-- <a-button |
||||
|
v-permission="{ action: RoleEnum.DICT_DELETE, effect: 'disabled' }" |
||||
|
class="mr-2" |
||||
|
@click="handleBatchDelete()" |
||||
|
> |
||||
|
{{ '删除' }} |
||||
|
</a-button> --> |
||||
|
</div> |
||||
|
<!-- <OrgRole @register="registerModal" @success="handleSuccess" /> --> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="tsx"> |
||||
|
import { defineComponent, h, onMounted, ref, unref } from 'vue'; |
||||
|
import { Checkbox, message, Tag } from 'ant-design-vue'; // antd组件 |
||||
|
import { useI18n } from '@/hooks/useI18n'; |
||||
|
import { |
||||
|
BasicTree, |
||||
|
ContextMenuItem, |
||||
|
TreeActionItem, |
||||
|
TreeActionType, |
||||
|
TreeItem, |
||||
|
} from '@/components/core/Tree'; |
||||
|
import { eachTree, findChildrenByParentId, findNodeByKey } from '@/utils/helper/treeHelper'; |
||||
|
import { RoleEnum } from '@/enums/roleEnum'; |
||||
|
import { OrgTypeEnum } from '@/enums/biz/base'; |
||||
|
import { useFormModal } from '@/hooks/useModal/index'; |
||||
|
|
||||
|
import { createProd, deleteProd, fetchProdList, updateProd } from '@/api/prodVersion'; |
||||
|
// import { useModal } from '@/components/Modal'; |
||||
|
// import OrgRole from './orgRole/index.vue'; |
||||
|
import { useMessage } from '@/hooks/useMessage'; |
||||
|
import { mockTree } from './mockData'; |
||||
|
import { Nullable } from '@/utils/types'; |
||||
|
import { prodFormSchema } from './formSchemas'; |
||||
|
import { TableListItem } from './columns'; |
||||
|
import { omit } from 'lodash-es'; |
||||
|
import { isArray } from '@/utils/is'; |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
name: 'BaseProdValueManagement', |
||||
|
components: { |
||||
|
BasicTree, |
||||
|
// OrgRole, |
||||
|
Checkbox, |
||||
|
Tag, |
||||
|
}, |
||||
|
props: { |
||||
|
query: { |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
}, |
||||
|
}, |
||||
|
emits: ['select', 'add', 'edit', 'change', 'reset'], |
||||
|
setup(props, { emit }) { |
||||
|
const { t } = useI18n(); |
||||
|
const { createMessage, createConfirm } = useMessage(); |
||||
|
|
||||
|
const treeRef = ref<Nullable<TreeActionType>>(null); |
||||
|
const treeData = ref<TreeItem[]>([]); |
||||
|
const recursion = ref<boolean>(false); |
||||
|
// 绑定角色 |
||||
|
// const [registerModal, { openModal }] = useModal(); |
||||
|
|
||||
|
const [showModal] = useFormModal(); |
||||
|
|
||||
|
/** |
||||
|
* @description 打开弹窗 |
||||
|
*/ |
||||
|
const openTypeModal = async (record: Partial<TableListItem> = {}, isReadOnly = false) => { |
||||
|
const [formRef] = await showModal<any>({ |
||||
|
modalProps: { |
||||
|
title: `${isReadOnly ? '查看' : record.id ? '编辑' : '新增'}产品`, |
||||
|
width: 500, |
||||
|
onFinish: async (values) => { |
||||
|
values.id = record.id; |
||||
|
await (record.id ? updateProd : createProd)(values); |
||||
|
message.success(`${record.id ? '编辑' : '新增'}成功`); |
||||
|
fetch(); |
||||
|
}, |
||||
|
}, |
||||
|
formProps: { |
||||
|
labelWidth: 100, |
||||
|
schemas: prodFormSchema, |
||||
|
autoSubmitOnEnter: true, |
||||
|
}, |
||||
|
}); |
||||
|
formRef?.setFieldsValue(record); |
||||
|
}; |
||||
|
|
||||
|
function getTree() { |
||||
|
const tree = unref(treeRef); |
||||
|
if (!tree) { |
||||
|
throw new Error('树结构加载失败,请刷新页面'); |
||||
|
} |
||||
|
return tree; |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
fetch(); |
||||
|
}); |
||||
|
|
||||
|
// 加载数据 |
||||
|
async function fetch() { |
||||
|
const types = (await fetchProdList({})) as unknown as TreeItem[]; |
||||
|
// const types = mockTree; |
||||
|
console.log('types: ', types); |
||||
|
treeData.value = types.map((e) => { |
||||
|
return { |
||||
|
...e, |
||||
|
name: e.name, |
||||
|
editable: true, |
||||
|
}; |
||||
|
}) as unknown as TreeItem[]; |
||||
|
eachTree(treeData.value, (item) => { |
||||
|
item.key = item.id; |
||||
|
item.title = item.name; |
||||
|
item.slots = { titleBefore: 'titleBefore' }; |
||||
|
return item; |
||||
|
}); |
||||
|
console.log('treeData.value: ', treeData.value); |
||||
|
console.log('[treeData.value[0].id]: ', [treeData.value[0].id]); |
||||
|
|
||||
|
if (isArray(treeData.value) && treeData.value.length > 0) { |
||||
|
getTree().setSelectedKeys([treeData.value[0].id]); |
||||
|
handleSelect([treeData.value[0].id]); |
||||
|
} |
||||
|
// setTimeout(() => { |
||||
|
// getTree().filterByLevel(2); |
||||
|
// }, 0); |
||||
|
} |
||||
|
|
||||
|
// 选择节点 |
||||
|
function handleSelect(keys: string[]) { |
||||
|
console.log('keys: ', keys); |
||||
|
if (keys[0]) { |
||||
|
const node = findNodeByKey(keys[0], treeData.value); |
||||
|
const parent = findNodeByKey(node?.parentId, treeData.value); |
||||
|
let childrenIds: string[] = []; |
||||
|
if (recursion.value) { |
||||
|
childrenIds = findChildrenByParentId(keys[0], treeData.value); |
||||
|
} else { |
||||
|
childrenIds = [node.id]; |
||||
|
} |
||||
|
emit('select', parent, node, childrenIds); |
||||
|
} else { |
||||
|
emit('select', {}, {}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
let actionList: TreeActionItem[] = []; |
||||
|
let getRightMenuList = (_: any): ContextMenuItem[] => { |
||||
|
return []; |
||||
|
}; |
||||
|
if (!props.query) { |
||||
|
// 悬停图标 |
||||
|
actionList = [ |
||||
|
{ |
||||
|
render: (node) => { |
||||
|
if (node.editable) { |
||||
|
return h( |
||||
|
'a', |
||||
|
{ |
||||
|
class: 'ml-2', |
||||
|
onClick: (e: Event) => { |
||||
|
e?.stopPropagation(); |
||||
|
e?.preventDefault(); |
||||
|
const current = findNodeByKey(node?.id, treeData.value); |
||||
|
// const parent = findNodeByKey(node?.parentId, treeData.value); |
||||
|
// emit('edit', parent, current); |
||||
|
openTypeModal(current); |
||||
|
}, |
||||
|
}, |
||||
|
'编辑', |
||||
|
); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
render: (node) => { |
||||
|
if (node.editable) { |
||||
|
return h( |
||||
|
'a', |
||||
|
{ |
||||
|
class: 'ml-2', |
||||
|
onClick: (e: Event) => { |
||||
|
e?.stopPropagation(); |
||||
|
e?.preventDefault(); |
||||
|
batchDelete(node.id); |
||||
|
}, |
||||
|
}, |
||||
|
'删除', |
||||
|
); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
// 右键菜单 |
||||
|
// getRightMenuList = (node: any): ContextMenuItem[] => { |
||||
|
// return [ |
||||
|
// { |
||||
|
// label: '编辑', |
||||
|
// auth: RoleEnum.DICT_EDIT, |
||||
|
// handler: () => { |
||||
|
// const current = findNodeByKey(unref(node)?.id, treeData.value); |
||||
|
// const parent = findNodeByKey(unref(node)?.parentId, treeData.value); |
||||
|
// emit('edit', parent, current); |
||||
|
// }, |
||||
|
// }, |
||||
|
// { |
||||
|
// label: '删除', |
||||
|
// auth: RoleEnum.DICT_DELETE, |
||||
|
// handler: () => { |
||||
|
// batchDelete([unref(node).id]); |
||||
|
// }, |
||||
|
// }, |
||||
|
// ]; |
||||
|
// }; |
||||
|
} |
||||
|
|
||||
|
// 执行批量删除 |
||||
|
async function batchDelete(id: string) { |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
content: '选中节点及其子结点将被永久删除, 是否确定删除?', |
||||
|
onOk: async () => { |
||||
|
try { |
||||
|
await deleteProd({ id }); |
||||
|
createMessage.success(t('common.tips.deleteSuccess')); |
||||
|
fetch(); |
||||
|
} catch (e) {} |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 点击树外面的 新增 |
||||
|
function handleAdd() { |
||||
|
emit('add', findNodeByKey('0', treeData.value)); |
||||
|
} |
||||
|
|
||||
|
// 点击树外面的 批量删除 |
||||
|
// function handleBatchDelete() { |
||||
|
// const { checked } = getTree().getCheckedKeys() as { |
||||
|
// checked: string[]; |
||||
|
// halfChecked: string[]; |
||||
|
// }; |
||||
|
// if (!checked || checked.length <= 0) { |
||||
|
// createMessage.warning(t('common.tips.pleaseSelectTheData')); |
||||
|
// return; |
||||
|
// } |
||||
|
// batchDelete(checked); |
||||
|
// } |
||||
|
|
||||
|
// 切换显示方式 |
||||
|
function changeDisplay() { |
||||
|
emit('change', '2'); |
||||
|
} |
||||
|
|
||||
|
// 重置 |
||||
|
function handleReset() { |
||||
|
getTree().setSelectedKeys([]); |
||||
|
emit('reset'); |
||||
|
} |
||||
|
|
||||
|
// 选择 本级及子级 |
||||
|
function handleQuery() { |
||||
|
handleSelect(getTree().getSelectedKeys() as string[]); |
||||
|
} |
||||
|
|
||||
|
// 新增或编辑成功回调 |
||||
|
function handleSuccess() { |
||||
|
fetch(); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
t, |
||||
|
treeRef, |
||||
|
treeData, |
||||
|
fetch, |
||||
|
handleAdd, |
||||
|
// handleBatchDelete, |
||||
|
getRightMenuList, |
||||
|
actionList, |
||||
|
handleSelect, |
||||
|
changeDisplay, |
||||
|
handleReset, |
||||
|
handleQuery, |
||||
|
RoleEnum, |
||||
|
// registerModal, |
||||
|
handleSuccess, |
||||
|
recursion, |
||||
|
OrgTypeEnum, |
||||
|
openTypeModal, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,30 @@ |
|||||
|
/* |
||||
|
* @Author: AaronWu 2463371514@qq.com |
||||
|
* @Date: 2025-04-01 13:43:34 |
||||
|
* @LastEditors: AaronWu 2463371514@qq.com |
||||
|
* @LastEditTime: 2025-04-01 13:52:02 |
||||
|
* @FilePath: /IssueSupportManage/src/views/system/dictionary/columns.tsx |
||||
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
|
*/ |
||||
|
import type { TableColumn } from '@/components/core/dynamic-table'; |
||||
|
|
||||
|
export type TableListItem = API.VersionType; |
||||
|
export type TableColumnItem = TableColumn<TableListItem>; |
||||
|
|
||||
|
// 数据项类型
|
||||
|
// export type ListItemType = typeof tableData[number];
|
||||
|
// 使用TableColumn<ListItemType> 将会限制dataIndex的类型,但换来的是dataIndex有类型提示
|
||||
|
export const baseColumns: TableColumnItem[] = [ |
||||
|
{ |
||||
|
title: '版本名称', |
||||
|
align: 'center', |
||||
|
dataIndex: 'name', |
||||
|
width: 150, |
||||
|
ellipsis: true, |
||||
|
resizable: true, |
||||
|
formItemProps: { |
||||
|
defaultValue: '', |
||||
|
required: false, |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
@ -0,0 +1,48 @@ |
|||||
|
import type { FormSchema } from '@/components/core/schema-form/'; |
||||
|
|
||||
|
// 列表编辑页字段
|
||||
|
export const editFormSchema: FormSchema[] = [ |
||||
|
{ |
||||
|
field: 'id', |
||||
|
label: 'ID', |
||||
|
component: 'Input', |
||||
|
vShow: false, |
||||
|
}, |
||||
|
{ |
||||
|
label: '版本名称', |
||||
|
field: 'name', |
||||
|
component: 'Input', |
||||
|
colProps: { |
||||
|
span: 24, |
||||
|
}, |
||||
|
rules: [{ required: true }], |
||||
|
}, |
||||
|
{ |
||||
|
label:'备注', |
||||
|
field: 'remark', |
||||
|
component: 'InputTextArea', |
||||
|
colProps: { |
||||
|
span: 24, |
||||
|
}, |
||||
|
rules: [{ required: false }], |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
// 新增产品表单
|
||||
|
export const prodFormSchema: FormSchema[] = [ |
||||
|
{ |
||||
|
field: 'id', |
||||
|
label: 'ID', |
||||
|
component: 'Input', |
||||
|
vShow: false, |
||||
|
}, |
||||
|
{ |
||||
|
label: '名称', |
||||
|
field: 'name', |
||||
|
component: 'Input', |
||||
|
colProps: { |
||||
|
span: 24, |
||||
|
}, |
||||
|
rules: [{ required: true }], |
||||
|
}, |
||||
|
]; |
||||
@ -0,0 +1,241 @@ |
|||||
|
<!-- |
||||
|
* @Author: AaronWu 2463371514@qq.com |
||||
|
* @Date: 2025-04-01 13:17:38 |
||||
|
* @LastEditors: AaronWu 2463371514@qq.com |
||||
|
* @LastEditTime: 2025-04-01 14:43:11 |
||||
|
* @FilePath: /IssueSupportManage/src/views/system/dictionary/index.vue |
||||
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE |
||||
|
--> |
||||
|
<template> |
||||
|
<SplitPanel> |
||||
|
<template #left-content> |
||||
|
<BaseTypeTree ref="treeRef" class="" @reset="handleReset" @select="handleTypeSelect" /> |
||||
|
</template> |
||||
|
<template #right-content> |
||||
|
<DynamicTable |
||||
|
search-type="drawer" |
||||
|
size="small" |
||||
|
showIndex |
||||
|
headerTitle="版本列表" |
||||
|
titleTooltip="" |
||||
|
:data-request="loadData" |
||||
|
:columns="columns" |
||||
|
row-key="id" |
||||
|
@resize-column="handleResizeColumn" |
||||
|
:row-selection="rowSelection" |
||||
|
> |
||||
|
<template v-if="isCheckRows" #[`title`]> |
||||
|
<Alert class="w-full" type="info" show-icon> |
||||
|
<template #message> |
||||
|
已选 {{ rowSelection.selectedRowKeys.length }} 项 |
||||
|
<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" |
||||
|
v-if="isCheckRows" |
||||
|
@click="delRowConfirm(rowSelection.selectedRowKeys)" |
||||
|
> |
||||
|
<DeleteOutlined /> 删除 |
||||
|
</a-button> |
||||
|
</template> |
||||
|
</DynamicTable> |
||||
|
</template> |
||||
|
</SplitPanel> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="tsx" setup> |
||||
|
import { computed, h, ref } from 'vue'; |
||||
|
import { SplitPanel } from '@/components/basic/split-panel'; |
||||
|
import BaseTypeTree from './Tree.vue'; |
||||
|
import { OnChangeCallbackParams, useTable } from '@/components/core/dynamic-table'; |
||||
|
import { baseColumns } from './columns'; |
||||
|
import { message, Modal, Alert } from 'ant-design-vue'; |
||||
|
import { ExclamationCircleOutlined, DeleteOutlined } from '@ant-design/icons-vue'; |
||||
|
import { useFormModal } from '@/hooks/useModal'; |
||||
|
import { editFormSchema } from './formSchemas'; |
||||
|
import { |
||||
|
createVersion, |
||||
|
deleteBatchVersionById, |
||||
|
fetchVersionPageList, |
||||
|
updateVersion, |
||||
|
} from '@/api/prodVersion'; |
||||
|
const columns: any = [ |
||||
|
...baseColumns, |
||||
|
{ |
||||
|
title: '操作', |
||||
|
width: 50, |
||||
|
dataIndex: 'ACTION', |
||||
|
hideInSearch: true, |
||||
|
align: 'center', |
||||
|
fixed: 'right', |
||||
|
actions: ({ record }) => [ |
||||
|
{ |
||||
|
color: '#3b82f6', |
||||
|
size: '15', |
||||
|
label: '编辑', |
||||
|
onClick: () => handleEdit(record), |
||||
|
}, |
||||
|
{ |
||||
|
color: '#ec6f6f', |
||||
|
label: '删除', |
||||
|
onClick: () => handleDelete(record.id), |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
]; |
||||
|
const treeRef = ref<InstanceType<typeof BaseTypeTree>>(); |
||||
|
const [showModal] = useFormModal(); |
||||
|
|
||||
|
const currentType = ref<API.DictType>({ |
||||
|
id: '', |
||||
|
name: '', |
||||
|
}); |
||||
|
|
||||
|
const [DynamicTable, dynamicTableInstance] = useTable(); |
||||
|
|
||||
|
function handleTypeSelect(_parent = {}, record: API.DictType, childrenIds = []) { |
||||
|
console.log('record: ', record); |
||||
|
currentType.value = record; |
||||
|
dynamicTableInstance?.reload(); |
||||
|
} |
||||
|
|
||||
|
function handleReset() { |
||||
|
currentType.value = { |
||||
|
id: '', |
||||
|
name: '', |
||||
|
}; |
||||
|
dynamicTableInstance?.reload(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description 表格删除行 |
||||
|
*/ |
||||
|
const delRowConfirm = async (ids: string[]) => { |
||||
|
Modal.confirm({ |
||||
|
title: '确定要删除所选的版本吗?', |
||||
|
icon: <ExclamationCircleOutlined />, |
||||
|
centered: true, |
||||
|
onOk: async () => { |
||||
|
await deleteBatchVersionById(ids).finally(dynamicTableInstance?.reload); |
||||
|
}, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const handleAdd = () => { |
||||
|
// openDrawer(true, { type: ActionEnum.ADD, record: {} }); |
||||
|
|
||||
|
// modal方式 |
||||
|
openModal({}); |
||||
|
}; |
||||
|
|
||||
|
const handleEdit = (record: API.VersionType) => { |
||||
|
if (record.enable === 1) { |
||||
|
message.warning('启用状态不可编辑'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
console.log('record: ', record); |
||||
|
|
||||
|
//modal 方式打开 |
||||
|
openModal(record); |
||||
|
}; |
||||
|
|
||||
|
const handleDelete = (id: string) => { |
||||
|
delRowConfirm([id]); |
||||
|
}; |
||||
|
/** |
||||
|
* @description 打开弹窗 |
||||
|
*/ |
||||
|
const openModal = async (record: Partial<API.VersionType> = {}, isReadOnly = false) => { |
||||
|
if (!currentType.value.id) { |
||||
|
message.warning('请先选择左侧产品'); |
||||
|
return; |
||||
|
} |
||||
|
const [formRef] = await showModal<any>({ |
||||
|
modalProps: { |
||||
|
title: `${isReadOnly ? '查看' : record.id ? '编辑' : '新增'}版本`, |
||||
|
width: 700, |
||||
|
onFinish: async (values) => { |
||||
|
console.log('新增/编辑版本', values); |
||||
|
values.id = record.id; |
||||
|
values.productId = currentType.value.id; |
||||
|
await (record.id ? updateVersion : createVersion)(values); |
||||
|
message.success(`${record.id ? '编辑' : '新增'}成功`); |
||||
|
dynamicTableInstance?.reload(); |
||||
|
}, |
||||
|
footer: isReadOnly ? null : undefined, |
||||
|
}, |
||||
|
formProps: { |
||||
|
labelWidth: 100, |
||||
|
schemas: editFormSchema, |
||||
|
autoSubmitOnEnter: true, |
||||
|
disabled: isReadOnly, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
formRef?.setFieldsValue(record); |
||||
|
// if (record?.id) { |
||||
|
// const { roles } = await getTenantInfo({ employeeId: record.id }); |
||||
|
// formRef?.setFieldsValue({ roles }); |
||||
|
// } |
||||
|
}; |
||||
|
|
||||
|
const rowSelection = ref<any>({ |
||||
|
selectedRowKeys: [] as string[], |
||||
|
onChange: (selectedRowKeys: string[], selectedRows: API.VersionType[]) => { |
||||
|
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); |
||||
|
|
||||
|
if (!currentType.value.id) { |
||||
|
return { |
||||
|
list: [], |
||||
|
pagination: { |
||||
|
total: 0, |
||||
|
size: params.limit, |
||||
|
...params, |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
const res = await fetchVersionPageList({ |
||||
|
...params, |
||||
|
productId: currentType.value.id, |
||||
|
current: params.page, |
||||
|
size: params.limit, |
||||
|
}); |
||||
|
console.log('res: ', res); |
||||
|
rowSelection.value.selectedRowKeys = []; |
||||
|
|
||||
|
return { |
||||
|
list: res.data, |
||||
|
pagination: { |
||||
|
total: Number(res.data.total), |
||||
|
...params, |
||||
|
}, |
||||
|
}; |
||||
|
}; |
||||
|
const handleResizeColumn = (w, col) => { |
||||
|
// console.log('w', w, col); |
||||
|
col.width = w; |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped></style> |
||||
@ -0,0 +1,14 @@ |
|||||
|
/* |
||||
|
* @Author: AaronWu 2463371514@qq.com |
||||
|
* @Date: 2025-04-01 13:24:31 |
||||
|
* @LastEditors: AaronWu 2463371514@qq.com |
||||
|
* @LastEditTime: 2025-04-01 13:25:12 |
||||
|
* @FilePath: /IssueSupportManage/src/views/system/dictionary/mockData.ts |
||||
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
|
*/ |
||||
|
export const mockTree: any[] = [ |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: '字典类别', |
||||
|
}, |
||||
|
]; |
||||
Loading…
Reference in new issue