Compare commits
9 Commits
234b376adb
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d0f216eece | |||
| ba55625181 | |||
| 0f08415f28 | |||
| 2207dbbcd2 | |||
| 7a5c81f6db | |||
| cd04abdc64 | |||
| bf5be2e832 | |||
| 3ac7e69330 | |||
| 5685dab1d0 |
@@ -47,6 +47,7 @@ export interface AiCloudFileParam extends PageParam {
|
|||||||
id?: number;
|
id?: number;
|
||||||
keywords?: string;
|
keywords?: string;
|
||||||
docId?: number;
|
docId?: number;
|
||||||
|
fileName?: string;
|
||||||
workspaceId?: number;
|
workspaceId?: number;
|
||||||
fileType?: string;
|
fileType?: string;
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ export async function updateAiHistoryBatch(data: { list: AiHistory[] }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除AI审计历史记录表
|
* 批量删除 AI审计历史记录表
|
||||||
*/
|
*/
|
||||||
export async function removeAiHistoryBatch(ids: number[]) {
|
export async function removeAiHistoryBatch(ids: number[]) {
|
||||||
const res = await request.delete<ApiResult<unknown>>(
|
const res = await request.delete<ApiResult<unknown>>(
|
||||||
@@ -127,3 +127,20 @@ export async function removeAiHistoryBatch(ids: number[]) {
|
|||||||
return Promise.reject(new Error(res.data.message));
|
return Promise.reject(new Error(res.data.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据接口名称和项目 ID 查询最新的历史记录
|
||||||
|
*/
|
||||||
|
export async function getLatestHistoryByInterface(params: {
|
||||||
|
interfaceName: string;
|
||||||
|
projectId: number;
|
||||||
|
}) {
|
||||||
|
const res = await request.get<ApiResult<AiHistory>>(
|
||||||
|
`${MODULES_API_URL}/ai/history/latest`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
if (res.data.code === 0 && res.data.data) {
|
||||||
|
return res.data.data;
|
||||||
|
}
|
||||||
|
return null; // 如果没有数据,返回 null
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,3 +184,121 @@ export async function generateAuditReportWithEvidences(projectId: number, eviden
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error('文件下载失败'));
|
return Promise.reject(new Error('文件下载失败'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据项目 ID、选中的取证单和章节内容生成审计报告并下载
|
||||||
|
* @param projectId 项目 ID
|
||||||
|
* @param evidenceIds 勾选的取证单 ID 列表
|
||||||
|
* @param chapters 章节内容数组(包含 formCommit 和 reportContent)
|
||||||
|
* @param evaluate 总体评价
|
||||||
|
* @param suggestion 审计建议
|
||||||
|
*/
|
||||||
|
export async function generateAuditReportWithContent(
|
||||||
|
projectId: number,
|
||||||
|
evidenceIds: number[],
|
||||||
|
chapters: Array<{
|
||||||
|
formCommit: number;
|
||||||
|
reportContent: string;
|
||||||
|
}>,
|
||||||
|
evaluate?: string,
|
||||||
|
suggestion?: string
|
||||||
|
) {
|
||||||
|
const res = await request.post(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/generateWithContent',
|
||||||
|
{
|
||||||
|
evidenceIds,
|
||||||
|
chapters,
|
||||||
|
evaluate,
|
||||||
|
suggestion
|
||||||
|
},
|
||||||
|
{
|
||||||
|
params: { projectId },
|
||||||
|
responseType: 'blob' // 处理二进制流响应
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error('文件下载失败'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 生成默认话术
|
||||||
|
*/
|
||||||
|
export async function generateDefaultText(params: {
|
||||||
|
projectId?: number;
|
||||||
|
formCommit: number;
|
||||||
|
chapterTitle?: string;
|
||||||
|
evidenceIds?: number[]; // 新增:选中的取证单 ID 列表
|
||||||
|
docList?: any[]; // 新增:选中的目录ID列表
|
||||||
|
fileList?: any[]; // 新增:选中的文件ID列表
|
||||||
|
}) {
|
||||||
|
const res = await request.post<ApiResult<string>>(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/generateDefaultText',
|
||||||
|
{
|
||||||
|
projectId: params.projectId,
|
||||||
|
formCommit: params.formCommit,
|
||||||
|
chapterTitle: params.chapterTitle,
|
||||||
|
evidenceIds: params.evidenceIds,
|
||||||
|
docList: params.docList,
|
||||||
|
fileList: params.fileList
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
// 后端返回的数据在 message 字段中
|
||||||
|
return res.data.message || res.data.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 分析用户自定义输入
|
||||||
|
*/
|
||||||
|
export async function analyzeUserInput(params: {
|
||||||
|
projectId: string;
|
||||||
|
formCommit: number;
|
||||||
|
userQuestion: string;
|
||||||
|
chapterContent?: string;
|
||||||
|
}) {
|
||||||
|
const res = await request.post<ApiResult<string>>(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/analyzeUserInput',
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
params
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
return res.data.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据取证单生成审计建议
|
||||||
|
*/
|
||||||
|
export async function generateAuditSuggestion(params: {
|
||||||
|
projectId: number;
|
||||||
|
evidenceIds: number[];
|
||||||
|
}) {
|
||||||
|
const res = await request.post<ApiResult<string>>(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/generateAuditSuggestion',
|
||||||
|
{ evidenceIds: params.evidenceIds },
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
projectId: params.projectId
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=UTF-8'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
// 后端返回的数据在 message 字段中
|
||||||
|
return res.data.message || res.data.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
@@ -10,6 +10,11 @@ export interface OaCompany {
|
|||||||
shortName?: string;
|
shortName?: string;
|
||||||
// 企业全称
|
// 企业全称
|
||||||
companyName?: string;
|
companyName?: string;
|
||||||
|
// 归属单位
|
||||||
|
parentCompany?: string;
|
||||||
|
parentCompanyId?: number;
|
||||||
|
// 单位编号
|
||||||
|
companyNo?: string;
|
||||||
// 企业标识
|
// 企业标识
|
||||||
companyCode?: string;
|
companyCode?: string;
|
||||||
// 类型 10企业 20政府单位
|
// 类型 10企业 20政府单位
|
||||||
|
|||||||
@@ -35,6 +35,17 @@
|
|||||||
v-model:value="form.companyName"
|
v-model:value="form.companyName"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="归属单位" name="parentCompany">
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.parentCompany"
|
||||||
|
show-search
|
||||||
|
allow-clear
|
||||||
|
option-filter-prop="label"
|
||||||
|
placeholder="请选择归属单位"
|
||||||
|
:options="parentCompanyOptions"
|
||||||
|
@change="handleParentCompanyChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item label="简称" name="shortName">
|
<a-form-item label="简称" name="shortName">
|
||||||
<a-input
|
<a-input
|
||||||
allow-clear
|
allow-clear
|
||||||
@@ -49,91 +60,101 @@
|
|||||||
v-model:value="form.companyCode"
|
v-model:value="form.companyCode"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="单位编号" name="companyNo">
|
||||||
|
<a-input
|
||||||
|
allow-clear
|
||||||
|
placeholder="请输入单位编号"
|
||||||
|
v-model:value="form.companyNo"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="企业性质" name="companyType">
|
<a-form-item label="企业性质" name="companyType">
|
||||||
<DictSelect
|
<DictSelect
|
||||||
dict-code="CompanyType"
|
dict-code="CompanyType"
|
||||||
:width="200"
|
:width="200"
|
||||||
:show-search="true"
|
:show-search="true"
|
||||||
placeholder="企业性质"
|
placeholder="企业性质"
|
||||||
v-model:value="form.companyType"
|
v-model:value="form.companyType"
|
||||||
:field-names="{
|
:field-names="{
|
||||||
label: 'dictDataName',
|
label: 'dictDataName',
|
||||||
value: 'dictDataCode'
|
value: 'dictDataCode'
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="所属行业" name="industryParent">
|
<a-form-item label="所属行业" name="industryParent">
|
||||||
<DictSelect
|
<DictSelect
|
||||||
dict-code="Industry"
|
dict-code="Industry"
|
||||||
:width="200"
|
:width="200"
|
||||||
:show-search="true"
|
:show-search="true"
|
||||||
placeholder="所属行业"
|
placeholder="所属行业"
|
||||||
v-model:value="form.industryParent"
|
v-model:value="form.industryParent"
|
||||||
:field-names="{
|
:field-names="{
|
||||||
label: 'dictDataName',
|
label: 'dictDataName',
|
||||||
value: 'dictDataCode'
|
value: 'dictDataCode'
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="知识库ID" name="kbId">
|
<a-form-item label="知识库ID" name="kbId">
|
||||||
<a-input
|
<a-input
|
||||||
:class="{ 'disabled-text': !form.kbId }"
|
:class="{ 'disabled-text': !form.kbId }"
|
||||||
:style="!form.kbId ? 'cursor: not-allowed; color: #999' : 'background-color: #f5f5f5'"
|
:style="
|
||||||
:placeholder="form.kbId ? '' : '未创建'"
|
!form.kbId
|
||||||
:value="form.kbId"
|
? 'cursor: not-allowed; color: #999'
|
||||||
readonly
|
: 'background-color: #f5f5f5'
|
||||||
|
"
|
||||||
|
:placeholder="form.kbId ? '' : '未创建'"
|
||||||
|
:value="form.kbId"
|
||||||
|
readonly
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<!-- 行业库 -->
|
<!-- 行业库 -->
|
||||||
<a-form-item label="行业库" name="bizLibIds">
|
<a-form-item label="行业库" name="bizLibIds">
|
||||||
<a-select
|
<a-select
|
||||||
show-search
|
show-search
|
||||||
:allow-clear="true"
|
:allow-clear="true"
|
||||||
optionFilterProp="label"
|
optionFilterProp="label"
|
||||||
v-model:value="form.bizLibIds"
|
v-model:value="form.bizLibIds"
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
placeholder="选择行业库"
|
placeholder="选择行业库"
|
||||||
:options="bizLibList"
|
:options="bizLibList"
|
||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<!-- 公共库 -->
|
<!-- 公共库 -->
|
||||||
<a-form-item label="公共库" name="pubLibIds">
|
<a-form-item label="公共库" name="pubLibIds">
|
||||||
<a-select
|
<a-select
|
||||||
show-search
|
show-search
|
||||||
:allow-clear="true"
|
:allow-clear="true"
|
||||||
optionFilterProp="label"
|
optionFilterProp="label"
|
||||||
v-model:value="form.pubLibIds"
|
v-model:value="form.pubLibIds"
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
placeholder="选择公共库"
|
placeholder="选择公共库"
|
||||||
:options="pubLibList"
|
:options="pubLibList"
|
||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- <a-form-item label="类型" name="companyType">-->
|
||||||
<!-- <a-form-item label="类型" name="companyType">-->
|
<!-- <a-radio-group v-model:value="form.companyType">-->
|
||||||
<!-- <a-radio-group v-model:value="form.companyType">-->
|
<!-- <a-radio :value="10">企业</a-radio>-->
|
||||||
<!-- <a-radio :value="10">企业</a-radio>-->
|
<!-- <a-radio :value="20">单位</a-radio>-->
|
||||||
<!-- <a-radio :value="20">单位</a-radio>-->
|
<!-- </a-radio-group>-->
|
||||||
<!-- </a-radio-group>-->
|
<!-- <a-input-->
|
||||||
<!-- <a-input-->
|
<!-- allow-clear-->
|
||||||
<!-- allow-clear-->
|
<!-- placeholder="请输入类型 10单位 20政府单位"-->
|
||||||
<!-- placeholder="请输入类型 10单位 20政府单位"-->
|
<!-- v-model:value="form.companyType"-->
|
||||||
<!-- v-model:value="form.companyType"-->
|
<!-- />-->
|
||||||
<!-- />-->
|
<!-- </a-form-item>-->
|
||||||
<!-- </a-form-item>-->
|
<!-- <a-form-item label="应用标识" name="companyLogo">-->
|
||||||
<!-- <a-form-item label="应用标识" name="companyLogo">-->
|
<!-- <a-input-->
|
||||||
<!-- <a-input-->
|
<!-- allow-clear-->
|
||||||
<!-- allow-clear-->
|
<!-- placeholder="请输入应用标识"-->
|
||||||
<!-- placeholder="请输入应用标识"-->
|
<!-- v-model:value="form.companyLogo"-->
|
||||||
<!-- v-model:value="form.companyLogo"-->
|
<!-- />-->
|
||||||
<!-- />-->
|
<!-- </a-form-item>-->
|
||||||
<!-- </a-form-item>-->
|
|
||||||
<a-form-item label="网站域名" name="domain">
|
<a-form-item label="网站域名" name="domain">
|
||||||
<a-input
|
<a-input
|
||||||
allow-clear
|
allow-clear
|
||||||
@@ -141,13 +162,13 @@
|
|||||||
v-model:value="form.domain"
|
v-model:value="form.domain"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- <a-form-item label="联系电话" name="phone">-->
|
<!-- <a-form-item label="联系电话" name="phone">-->
|
||||||
<!-- <a-input-->
|
<!-- <a-input-->
|
||||||
<!-- allow-clear-->
|
<!-- allow-clear-->
|
||||||
<!-- placeholder="请输入联系电话"-->
|
<!-- placeholder="请输入联系电话"-->
|
||||||
<!-- v-model:value="form.phone"-->
|
<!-- v-model:value="form.phone"-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- </a-form-item>-->
|
<!-- </a-form-item>-->
|
||||||
<a-form-item label="座机电话" name="tel">
|
<a-form-item label="座机电话" name="tel">
|
||||||
<a-input
|
<a-input
|
||||||
allow-clear
|
allow-clear
|
||||||
@@ -155,20 +176,20 @@
|
|||||||
v-model:value="form.tel"
|
v-model:value="form.tel"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- <a-form-item label="邮箱" name="email">-->
|
<!-- <a-form-item label="邮箱" name="email">-->
|
||||||
<!-- <a-input-->
|
<!-- <a-input-->
|
||||||
<!-- allow-clear-->
|
<!-- allow-clear-->
|
||||||
<!-- placeholder="请输入邮箱"-->
|
<!-- placeholder="请输入邮箱"-->
|
||||||
<!-- v-model:value="form.email"-->
|
<!-- v-model:value="form.email"-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- </a-form-item>-->
|
<!-- </a-form-item>-->
|
||||||
<!-- <a-form-item label="发票抬头" name="invoiceHeader">-->
|
<!-- <a-form-item label="发票抬头" name="invoiceHeader">-->
|
||||||
<!-- <a-input-->
|
<!-- <a-input-->
|
||||||
<!-- allow-clear-->
|
<!-- allow-clear-->
|
||||||
<!-- placeholder="请输入发票抬头"-->
|
<!-- placeholder="请输入发票抬头"-->
|
||||||
<!-- v-model:value="form.invoiceHeader"-->
|
<!-- v-model:value="form.invoiceHeader"-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- </a-form-item>-->
|
<!-- </a-form-item>-->
|
||||||
<a-form-item label="企业法人" name="businessEntity">
|
<a-form-item label="企业法人" name="businessEntity">
|
||||||
<a-input
|
<a-input
|
||||||
allow-clear
|
allow-clear
|
||||||
@@ -176,27 +197,27 @@
|
|||||||
v-model:value="form.businessEntity"
|
v-model:value="form.businessEntity"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- <a-form-item label="所在省份" name="province">-->
|
<!-- <a-form-item label="所在省份" name="province">-->
|
||||||
<!-- <a-input-->
|
<!-- <a-input-->
|
||||||
<!-- allow-clear-->
|
<!-- allow-clear-->
|
||||||
<!-- placeholder="请输入所在省份"-->
|
<!-- placeholder="请输入所在省份"-->
|
||||||
<!-- v-model:value="form.province"-->
|
<!-- v-model:value="form.province"-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- </a-form-item>-->
|
<!-- </a-form-item>-->
|
||||||
<!-- <a-form-item label="所在城市" name="city">-->
|
<!-- <a-form-item label="所在城市" name="city">-->
|
||||||
<!-- <a-input-->
|
<!-- <a-input-->
|
||||||
<!-- allow-clear-->
|
<!-- allow-clear-->
|
||||||
<!-- placeholder="请输入所在城市"-->
|
<!-- placeholder="请输入所在城市"-->
|
||||||
<!-- v-model:value="form.city"-->
|
<!-- v-model:value="form.city"-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- </a-form-item>-->
|
<!-- </a-form-item>-->
|
||||||
<!-- <a-form-item label="所在辖区" name="region">-->
|
<!-- <a-form-item label="所在辖区" name="region">-->
|
||||||
<!-- <a-input-->
|
<!-- <a-input-->
|
||||||
<!-- allow-clear-->
|
<!-- allow-clear-->
|
||||||
<!-- placeholder="请输入所在辖区"-->
|
<!-- placeholder="请输入所在辖区"-->
|
||||||
<!-- v-model:value="form.region"-->
|
<!-- v-model:value="form.region"-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- </a-form-item>-->
|
<!-- </a-form-item>-->
|
||||||
<a-form-item label="详细地址" name="address">
|
<a-form-item label="详细地址" name="address">
|
||||||
<a-input
|
<a-input
|
||||||
allow-clear
|
allow-clear
|
||||||
@@ -235,15 +256,19 @@
|
|||||||
import { ref, reactive, watch } from 'vue';
|
import { ref, reactive, watch } from 'vue';
|
||||||
import { Form, message } from 'ant-design-vue';
|
import { Form, message } from 'ant-design-vue';
|
||||||
import { assignObject, uuid } from 'ele-admin-pro';
|
import { assignObject, uuid } from 'ele-admin-pro';
|
||||||
import { addOaCompany, updateOaCompany } from '@/api/oa/oaCompany';
|
import {
|
||||||
|
addOaCompany,
|
||||||
|
updateOaCompany,
|
||||||
|
listOaCompany
|
||||||
|
} from '@/api/oa/oaCompany';
|
||||||
import { OaCompany } from '@/api/oa/oaCompany/model';
|
import { OaCompany } from '@/api/oa/oaCompany/model';
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||||
import { FormInstance } from 'ant-design-vue/es/form';
|
import { FormInstance } from 'ant-design-vue/es/form';
|
||||||
import { FileRecord } from '@/api/system/file/model';
|
import { FileRecord } from '@/api/system/file/model';
|
||||||
import {listPwlProjectLibrary} from '@/api/pwl/pwlProjectLibrary';
|
import { listPwlProjectLibrary } from '@/api/pwl/pwlProjectLibrary';
|
||||||
import DictSelect from "@/components/DictSelect/index.vue";
|
import DictSelect from '@/components/DictSelect/index.vue';
|
||||||
|
|
||||||
// 是否是修改
|
// 是否是修改
|
||||||
const isUpdate = ref(false);
|
const isUpdate = ref(false);
|
||||||
@@ -254,6 +279,13 @@
|
|||||||
// 资料库响应式数据
|
// 资料库响应式数据
|
||||||
const bizLibList = ref<any[]>([]);
|
const bizLibList = ref<any[]>([]);
|
||||||
const pubLibList = ref<any[]>([]);
|
const pubLibList = ref<any[]>([]);
|
||||||
|
const parentCompanyOptions = ref<{ label: string; value: string }[]>([
|
||||||
|
{
|
||||||
|
label: '本单位(自己)',
|
||||||
|
value: '本单位'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
const parentCompanyList = ref<OaCompany[]>([]);
|
||||||
// 存储完整的资料库数据,用于查找名称
|
// 存储完整的资料库数据,用于查找名称
|
||||||
const allLibraries = ref<any[]>([]);
|
const allLibraries = ref<any[]>([]);
|
||||||
|
|
||||||
@@ -282,7 +314,10 @@
|
|||||||
companyId: undefined,
|
companyId: undefined,
|
||||||
shortName: undefined,
|
shortName: undefined,
|
||||||
companyName: undefined,
|
companyName: undefined,
|
||||||
|
parentCompany: '本单位',
|
||||||
|
parentCompanyId: 0,
|
||||||
companyCode: undefined,
|
companyCode: undefined,
|
||||||
|
companyNo: undefined,
|
||||||
companyType: undefined,
|
companyType: undefined,
|
||||||
companyTypeMultiple: undefined,
|
companyTypeMultiple: undefined,
|
||||||
companyLogo: undefined,
|
companyLogo: undefined,
|
||||||
@@ -330,13 +365,9 @@
|
|||||||
tenantId: undefined,
|
tenantId: undefined,
|
||||||
createTime: undefined,
|
createTime: undefined,
|
||||||
updateTime: undefined,
|
updateTime: undefined,
|
||||||
oaCompanyId: undefined,
|
|
||||||
oaCompanyName: '',
|
oaCompanyName: '',
|
||||||
status: 0,
|
|
||||||
comments: '',
|
|
||||||
sortNumber: 100,
|
|
||||||
kbId: undefined,
|
kbId: undefined,
|
||||||
libraryIds: undefined,
|
libraryIds: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
/* 更新visible */
|
/* 更新visible */
|
||||||
@@ -370,26 +401,90 @@
|
|||||||
form.companyLogo = '';
|
form.companyLogo = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取归属单位下拉
|
||||||
|
const fetchParentCompanyOptions = async () => {
|
||||||
|
const list = await listOaCompany();
|
||||||
|
const currentCompanyId = props.data?.companyId;
|
||||||
|
parentCompanyList.value = list.filter(
|
||||||
|
(item) => item.companyId !== currentCompanyId
|
||||||
|
);
|
||||||
|
const options = parentCompanyList.value
|
||||||
|
.map((item) => item.companyName)
|
||||||
|
.filter((name): name is string => !!name)
|
||||||
|
.filter((name, index, arr) => arr.indexOf(name) === index)
|
||||||
|
.map((name) => ({
|
||||||
|
label: name,
|
||||||
|
value: name
|
||||||
|
}));
|
||||||
|
|
||||||
|
parentCompanyOptions.value = [
|
||||||
|
{
|
||||||
|
label: '本单位(自己)',
|
||||||
|
value: '本单位'
|
||||||
|
},
|
||||||
|
...options.filter((item) => item.value !== '本单位')
|
||||||
|
];
|
||||||
|
|
||||||
|
if (
|
||||||
|
form.parentCompany &&
|
||||||
|
!parentCompanyOptions.value.some(
|
||||||
|
(item) => item.value === form.parentCompany
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
parentCompanyOptions.value.push({
|
||||||
|
label: form.parentCompany,
|
||||||
|
value: form.parentCompany
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getParentCompanyIdByName = (name?: string) => {
|
||||||
|
if (!name || name === '本单位') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
parentCompanyList.value.find((item) => item.companyName === name)
|
||||||
|
?.companyId ?? 0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleParentCompanyChange = (value?: string) => {
|
||||||
|
form.parentCompany = value || '本单位';
|
||||||
|
form.parentCompanyId = getParentCompanyIdByName(form.parentCompany);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ensureParentCompanyOption = (value?: string) => {
|
||||||
|
if (
|
||||||
|
value &&
|
||||||
|
!parentCompanyOptions.value.some((item) => item.value === value)
|
||||||
|
) {
|
||||||
|
parentCompanyOptions.value.push({
|
||||||
|
label: value,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取资料库列表
|
// 获取资料库列表
|
||||||
const fetchLibraries = () => {
|
const fetchLibraries = () => {
|
||||||
return listPwlProjectLibrary().then(res => {
|
return listPwlProjectLibrary().then((res) => {
|
||||||
allLibraries.value = res;
|
allLibraries.value = res;
|
||||||
|
|
||||||
// 过滤行业库 (type='biz')
|
// 过滤行业库 (type='biz')
|
||||||
bizLibList.value = res
|
bizLibList.value = res
|
||||||
.filter(item => item.type === 'biz')
|
.filter((item) => item.type === 'biz')
|
||||||
.map(item => ({
|
.map((item) => ({
|
||||||
label: item.name,
|
label: item.name,
|
||||||
value: String(item.id) // 将 ID 转换为字符串
|
value: String(item.id) // 将 ID 转换为字符串
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 过滤公共库 (type='pub')
|
// 过滤公共库 (type='pub')
|
||||||
pubLibList.value = res
|
pubLibList.value = res
|
||||||
.filter(item => item.type === 'pub')
|
.filter((item) => item.type === 'pub')
|
||||||
.map(item => ({
|
.map((item) => ({
|
||||||
label: item.name,
|
label: item.name,
|
||||||
value: String(item.id) // 将 ID 转换为字符串
|
value: String(item.id) // 将 ID 转换为字符串
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
@@ -411,12 +506,16 @@
|
|||||||
const allLibraryIds = [
|
const allLibraryIds = [
|
||||||
...(form.bizLibIds || []),
|
...(form.bizLibIds || []),
|
||||||
...(form.pubLibIds || [])
|
...(form.pubLibIds || [])
|
||||||
]
|
];
|
||||||
|
|
||||||
const formData = {
|
const formData = {
|
||||||
...form,
|
...form,
|
||||||
libraryIds: allLibraryIds.join(','), // 保存为逗号分隔的字符串
|
libraryIds: allLibraryIds.join(',') // 保存为逗号分隔的字符串
|
||||||
};
|
};
|
||||||
|
if (!formData.parentCompany) formData.parentCompany = '本单位';
|
||||||
|
formData.parentCompanyId = getParentCompanyIdByName(
|
||||||
|
formData.parentCompany
|
||||||
|
);
|
||||||
const saveOrUpdate = isUpdate.value ? updateOaCompany : addOaCompany;
|
const saveOrUpdate = isUpdate.value ? updateOaCompany : addOaCompany;
|
||||||
saveOrUpdate(formData)
|
saveOrUpdate(formData)
|
||||||
.then((msg) => {
|
.then((msg) => {
|
||||||
@@ -438,24 +537,30 @@
|
|||||||
if (props.data?.libraryIds) {
|
if (props.data?.libraryIds) {
|
||||||
// 如果是字符串,按逗号分割成数组
|
// 如果是字符串,按逗号分割成数组
|
||||||
if (typeof props.data.libraryIds === 'string') {
|
if (typeof props.data.libraryIds === 'string') {
|
||||||
const libraryIdsArray = props.data.libraryIds.split(',').filter(id => id.trim() !== '');
|
const libraryIdsArray = props.data.libraryIds
|
||||||
|
.split(',')
|
||||||
|
.filter((id) => id.trim() !== '');
|
||||||
|
|
||||||
// 清空当前的选择
|
// 清空当前的选择
|
||||||
form.bizLibIds = [];
|
form.bizLibIds = [];
|
||||||
form.pubLibIds = [];
|
form.pubLibIds = [];
|
||||||
|
|
||||||
// 根据资料库类型分别设置回显
|
// 根据资料库类型分别设置回显
|
||||||
libraryIdsArray.forEach(id => {
|
libraryIdsArray.forEach((id) => {
|
||||||
// 检查是否在行业库列表中
|
// 检查是否在行业库列表中
|
||||||
const isBizLib = bizLibList.value.some(lib => lib.value === String(id)); // 转换为字符串比较
|
const isBizLib = bizLibList.value.some(
|
||||||
|
(lib) => lib.value === String(id)
|
||||||
|
); // 转换为字符串比较
|
||||||
// 检查是否在公共库列表中
|
// 检查是否在公共库列表中
|
||||||
const isPubLib = pubLibList.value.some(lib => lib.value === String(id)); // 转换为字符串比较
|
const isPubLib = pubLibList.value.some(
|
||||||
|
(lib) => lib.value === String(id)
|
||||||
|
); // 转换为字符串比较
|
||||||
|
|
||||||
if (isBizLib) {
|
if (isBizLib) {
|
||||||
form.bizLibIds.push(String(id)); // 确保存储为字符串
|
form.bizLibIds.push(String(id)); // 确保存储为字符串
|
||||||
}
|
}
|
||||||
if (isPubLib) {
|
if (isPubLib) {
|
||||||
form.pubLibIds.push(String(id)); // 确保存储为字符串
|
form.pubLibIds.push(String(id)); // 确保存储为字符串
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -463,9 +568,13 @@
|
|||||||
form.bizLibIds = [];
|
form.bizLibIds = [];
|
||||||
form.pubLibIds = [];
|
form.pubLibIds = [];
|
||||||
|
|
||||||
props.data.libraryIds.forEach(id => {
|
props.data.libraryIds.forEach((id) => {
|
||||||
const isBizLib = bizLibList.value.some(lib => lib.value === String(id));
|
const isBizLib = bizLibList.value.some(
|
||||||
const isPubLib = pubLibList.value.some(lib => lib.value === String(id));
|
(lib) => lib.value === String(id)
|
||||||
|
);
|
||||||
|
const isPubLib = pubLibList.value.some(
|
||||||
|
(lib) => lib.value === String(id)
|
||||||
|
);
|
||||||
|
|
||||||
if (isBizLib) {
|
if (isBizLib) {
|
||||||
form.bizLibIds.push(String(id));
|
form.bizLibIds.push(String(id));
|
||||||
@@ -483,21 +592,25 @@
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
async (visible) => {
|
async (visible) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
|
||||||
// 等待资料库列表加载完成后再设置回显
|
// 等待资料库列表加载完成后再设置回显
|
||||||
await fetchLibraries();
|
await Promise.all([fetchLibraries(), fetchParentCompanyOptions()]);
|
||||||
|
|
||||||
images.value = [];
|
images.value = [];
|
||||||
if (props.data) {
|
if (props.data) {
|
||||||
assignObject(form, props.data);
|
assignObject(form, props.data);
|
||||||
if(props.data.companyLogo){
|
form.parentCompany = props.data.parentCompany || '本单位';
|
||||||
|
form.parentCompanyId =
|
||||||
|
props.data.parentCompanyId ??
|
||||||
|
getParentCompanyIdByName(form.parentCompany);
|
||||||
|
ensureParentCompanyOption(form.parentCompany);
|
||||||
|
if (props.data.companyLogo) {
|
||||||
images.value.push({
|
images.value.push({
|
||||||
uid: uuid(),
|
uid: uuid(),
|
||||||
url: props.data.companyLogo,
|
url: props.data.companyLogo,
|
||||||
status: 'done'
|
status: 'done'
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置资料库回显
|
// 设置资料库回显
|
||||||
@@ -505,10 +618,14 @@
|
|||||||
|
|
||||||
isUpdate.value = true;
|
isUpdate.value = true;
|
||||||
} else {
|
} else {
|
||||||
|
form.parentCompany = '本单位';
|
||||||
|
form.parentCompanyId = 0;
|
||||||
isUpdate.value = false;
|
isUpdate.value = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resetFields();
|
resetFields();
|
||||||
|
form.parentCompany = '本单位';
|
||||||
|
form.parentCompanyId = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -108,6 +108,10 @@
|
|||||||
currentCompanyId: number;
|
currentCompanyId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'confirm', data: { dirKeys: (string | number)[], fileKeys: (string | number)[] }): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
// 树形结构相关
|
// 树形结构相关
|
||||||
const expandedKeys = ref<(string | number)[]>([]);
|
const expandedKeys = ref<(string | number)[]>([]);
|
||||||
const selectedKeys = ref<(string | number)[]>([]);
|
const selectedKeys = ref<(string | number)[]>([]);
|
||||||
@@ -184,6 +188,12 @@
|
|||||||
lastSelectedDirKeys.value = [...checkedDirKeys.value];
|
lastSelectedDirKeys.value = [...checkedDirKeys.value];
|
||||||
lastSelectedFileKeys.value = [...selectedFileKeys.value];
|
lastSelectedFileKeys.value = [...selectedFileKeys.value];
|
||||||
|
|
||||||
|
// 传递选择的数据给父组件
|
||||||
|
emit('confirm', {
|
||||||
|
dirKeys: lastSelectedDirKeys.value,
|
||||||
|
fileKeys: lastSelectedFileKeys.value
|
||||||
|
});
|
||||||
|
|
||||||
message.success(
|
message.success(
|
||||||
`已选择 ${checkedDirKeys.value.length} 个目录和 ${selectedFileKeys.value.length} 个文件`
|
`已选择 ${checkedDirKeys.value.length} 个目录和 ${selectedFileKeys.value.length} 个文件`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -80,11 +80,12 @@ const props = defineProps<{
|
|||||||
visible: boolean;
|
visible: boolean;
|
||||||
interfaceName?: string;
|
interfaceName?: string;
|
||||||
projectId?: number;
|
projectId?: number;
|
||||||
|
chapter?: any; // 当前章节对象
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:visible', visible: boolean): void;
|
(e: 'update:visible', visible: boolean): void;
|
||||||
(e: 'select', record: any): void;
|
(e: 'select', record: any, chapter?: any): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||||
@@ -99,7 +100,11 @@ const datasource: DatasourceFunction = async ({ page, limit, orders }) => {
|
|||||||
if (orders) {
|
if (orders) {
|
||||||
Object.assign(params, orders);
|
Object.assign(params, orders);
|
||||||
}
|
}
|
||||||
console.log(props,'props');
|
|
||||||
|
console.log('===== HistoryModal Props =====', props);
|
||||||
|
console.log('interfaceName:', props.interfaceName);
|
||||||
|
console.log('projectId:', props.projectId);
|
||||||
|
|
||||||
// 使用传入的接口名称进行过滤
|
// 使用传入的接口名称进行过滤
|
||||||
if (props.interfaceName) {
|
if (props.interfaceName) {
|
||||||
params.interfaceName = props.interfaceName;
|
params.interfaceName = props.interfaceName;
|
||||||
@@ -108,6 +113,8 @@ const datasource: DatasourceFunction = async ({ page, limit, orders }) => {
|
|||||||
params.projectId = props.projectId;
|
params.projectId = props.projectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('查询参数 params:', params);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await pageAiHistory(params);
|
const result = await pageAiHistory(params);
|
||||||
|
|
||||||
@@ -116,14 +123,23 @@ const datasource: DatasourceFunction = async ({ page, limit, orders }) => {
|
|||||||
let processingTime = '';
|
let processingTime = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// responseData 直接就是 AI 生成的文本内容,不是 JSON 对象
|
||||||
|
// 这里只是为了统计展示信息,不影响主要内容显示
|
||||||
if (record.responseData) {
|
if (record.responseData) {
|
||||||
const responseData = JSON.parse(record.responseData);
|
// 尝试解析是否为 JSON 格式(兼容旧数据)
|
||||||
if (responseData.data && Array.isArray(responseData.data)) {
|
try {
|
||||||
dataCount = responseData.data.length;
|
const parsed = JSON.parse(record.responseData);
|
||||||
} else if (responseData.data?.data && Array.isArray(responseData.data.data)) {
|
if (parsed.data && Array.isArray(parsed.data)) {
|
||||||
dataCount = responseData.data.data.length;
|
dataCount = parsed.data.length;
|
||||||
|
} else if (parsed.data?.data && Array.isArray(parsed.data.data)) {
|
||||||
|
dataCount = parsed.data.data.length;
|
||||||
|
}
|
||||||
|
processingTime = parsed.processing_time || parsed.generated_time || '';
|
||||||
|
} catch (e) {
|
||||||
|
// 如果不是 JSON,说明是纯文本,不需要统计这些数据
|
||||||
|
dataCount = 0;
|
||||||
|
processingTime = '';
|
||||||
}
|
}
|
||||||
processingTime = responseData.processing_time || responseData.generated_time || '';
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('解析响应数据失败:', error);
|
console.warn('解析响应数据失败:', error);
|
||||||
@@ -258,6 +274,14 @@ const getRequestDataPreview = (record: any) => {
|
|||||||
const hasValidData = (record: any) => {
|
const hasValidData = (record: any) => {
|
||||||
try {
|
try {
|
||||||
if (record.responseData) {
|
if (record.responseData) {
|
||||||
|
// responseData 可能是纯文本(AI 生成的审计报告内容),也可能是 JSON 对象
|
||||||
|
// 对于纯文本,只要有内容就认为有效
|
||||||
|
if (typeof record.responseData === 'string') {
|
||||||
|
// 如果是字符串,检查是否非空
|
||||||
|
return record.responseData.trim().length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是对象,尝试解析 JSON
|
||||||
const responseData = JSON.parse(record.responseData);
|
const responseData = JSON.parse(record.responseData);
|
||||||
const hasData = (responseData.data && Array.isArray(responseData.data) && responseData.data.length > 0) ||
|
const hasData = (responseData.data && Array.isArray(responseData.data) && responseData.data.length > 0) ||
|
||||||
(responseData.data?.data && Array.isArray(responseData.data.data) && responseData.data.data.length > 0);
|
(responseData.data?.data && Array.isArray(responseData.data.data) && responseData.data.data.length > 0);
|
||||||
@@ -265,13 +289,15 @@ const hasValidData = (record: any) => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('检查数据有效性失败:', error);
|
console.warn('检查数据有效性失败:', error);
|
||||||
|
// 如果解析失败,但有 responseData 字符串,也认为有效(说明是纯文本)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelect = (record: any) => {
|
const handleSelect = (record: any) => {
|
||||||
if (!hasValidData(record)) return;
|
if (!hasValidData(record)) return;
|
||||||
emit('select', record);
|
emit('select', record, props.chapter);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -170,45 +170,34 @@
|
|||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- 单内容部分 -->
|
<!-- 单内容部分 -->
|
||||||
<template v-if="!item.children">
|
<template v-if="!item.children">
|
||||||
<a-textarea
|
<div class="content-with-ai-button">
|
||||||
v-model:value="item.content"
|
<a-textarea
|
||||||
:rows="item.rows || 8"
|
v-model:value="item.content"
|
||||||
:placeholder="'点击(AI生成)按钮让AI为您生成该部分内容,或直接填入'"
|
:rows="item.rows || 8"
|
||||||
class="content-textarea"
|
:placeholder="'点击 (AI 生成) 按钮让 AI 为您生成该部分内容,或直接填入'"
|
||||||
style="margin-top: 16px; background-color: #f0fdf4"
|
class="content-textarea"
|
||||||
/>
|
style="background-color: #f0fdf4"
|
||||||
|
/>
|
||||||
<div style="margin-top: 12px;">
|
<a-button
|
||||||
<div class="question-prompt">AI小助手</div>
|
type="primary"
|
||||||
<div class="textarea-with-button" style="width: 600px">
|
size="large"
|
||||||
<a-textarea
|
@click="generateContent(index)"
|
||||||
v-model:value="item.suggestion"
|
:loading="item.generating"
|
||||||
:rows="3"
|
class="ai-generate-btn"
|
||||||
placeholder="请输入您的要求并回车..."
|
>
|
||||||
class="suggestion-textarea-inner"
|
<template #icon>
|
||||||
@pressEnter="generateContent(index)"
|
<RobotOutlined/>
|
||||||
/>
|
</template>
|
||||||
<a-button
|
AI 生成
|
||||||
type="danger"
|
</a-button>
|
||||||
size="small"
|
|
||||||
@click="generateContent(index)"
|
|
||||||
:loading="item.generating"
|
|
||||||
class="send-button-inner"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<RobotOutlined/>
|
|
||||||
</template>
|
|
||||||
发送
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 多内容部分 -->
|
<!-- 多内容部分 -->
|
||||||
<template v-else>
|
<template v-if="item.children">
|
||||||
<div v-for="(child, childIndex) in item.children" :key="childIndex" class="child-section" style="border-left: 3px solid #1890ff; padding-left: 16px; margin-bottom: 20px;">
|
<div v-for="(child, childIndex) in item.children" :key="childIndex" class="child-section">
|
||||||
<div class="child-title" style="display: flex; align-items: center; justify-content: space-between;">
|
<div class="child-header">
|
||||||
<span style="font-weight: bold;">{{ child.name }}</span>
|
<span class="child-title">{{ `(${childIndex + 1})${child.name}` }}</span>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -236,10 +225,10 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="large"
|
||||||
@click.stop="generateContent(index, childIndex)"
|
@click.stop="generateContent(index, childIndex)"
|
||||||
:loading="child.generating"
|
:loading="child.generating"
|
||||||
class="child-action-button ai-generate-button"
|
class="ai-generate-btn"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<RobotOutlined/>
|
<RobotOutlined/>
|
||||||
@@ -253,34 +242,8 @@
|
|||||||
:rows="child.rows || 6"
|
:rows="child.rows || 6"
|
||||||
:placeholder="child.placeholder"
|
:placeholder="child.placeholder"
|
||||||
class="content-textarea"
|
class="content-textarea"
|
||||||
style="margin-top: 12px; background-color: #f0fdf4"
|
style="background-color: #f0fdf4"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- AI小助手功能 -->
|
|
||||||
<div style="margin-top: 12px">
|
|
||||||
<div class="question-prompt">AI小助手</div>
|
|
||||||
<div class="textarea-with-button" style="width: 600px">
|
|
||||||
<a-textarea
|
|
||||||
v-model:value="child.suggestion"
|
|
||||||
:rows="3"
|
|
||||||
placeholder="请输入优化提示词并回车..."
|
|
||||||
class="suggestion-textarea-inner"
|
|
||||||
@pressEnter="generateContent(index, childIndex)"
|
|
||||||
/>
|
|
||||||
<a-button
|
|
||||||
type="danger"
|
|
||||||
size="small"
|
|
||||||
@click="generateContent(index, childIndex)"
|
|
||||||
:loading="child.generating"
|
|
||||||
class="send-button-inner"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<RobotOutlined/>
|
|
||||||
</template>
|
|
||||||
发送
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -308,7 +271,7 @@ import {
|
|||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
SaveOutlined
|
SaveOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import {generateAuditReport, downloadAuditReport, queryAuditReport, saveAuditReport} from "@/api/ai/auditReport";
|
import {generateDefaultText, downloadAuditReport, queryAuditReport, saveAuditReport} from "@/api/ai/auditReport";
|
||||||
import {getPwlProjectLibraryByIds} from '@/api/pwl/pwlProjectLibrary';
|
import {getPwlProjectLibraryByIds} from '@/api/pwl/pwlProjectLibrary';
|
||||||
|
|
||||||
const useForm = Form.useForm;
|
const useForm = Form.useForm;
|
||||||
@@ -1410,14 +1373,13 @@ const generateContent = async (sectionIndex: number, childIndex?: number) => {
|
|||||||
|
|
||||||
item.generating = true;
|
item.generating = true;
|
||||||
try {
|
try {
|
||||||
const result = await generateAuditReport({
|
// 获取章节标题
|
||||||
kbId: form.kbId || '',
|
const chapterTitle = isChild ? item.title : section.title;
|
||||||
kbIds: combinedKbIds.value,
|
|
||||||
|
const result = await generateDefaultText({
|
||||||
|
projectId: form.id,
|
||||||
formCommit: formCommit,
|
formCommit: formCommit,
|
||||||
history: item.content || '',
|
chapterTitle: chapterTitle
|
||||||
suggestion: item.suggestion || '',
|
|
||||||
// 新增:传递完整的上下文数据
|
|
||||||
...contextData
|
|
||||||
});
|
});
|
||||||
item.content = result;
|
item.content = result;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1807,24 +1769,68 @@ export default {
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-with-ai-button {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.ant-textarea {
|
||||||
|
min-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-generate-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: #722ed1 !important;
|
||||||
|
border-color: #722ed1 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #9254de !important;
|
||||||
|
border-color: #9254de !important;
|
||||||
|
box-shadow: 0 4px 12px rgba(114, 46, 209, 0.4) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.child-section {
|
.child-section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
border-bottom: 1px dashed #e8e8e8;
|
border-bottom: 1px dashed #e8e8e8;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.child-title {
|
.child-header {
|
||||||
font-weight: 600;
|
display: flex;
|
||||||
font-size: 15px;
|
align-items: center;
|
||||||
color: #333;
|
justify-content: space-between;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 12px;
|
||||||
padding-left: 8px;
|
padding: 12px 16px;
|
||||||
border-left: 3px solid #1890ff;
|
background: #f5f7fa;
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
|
border-left: 3px solid #1890ff;
|
||||||
|
|
||||||
|
.child-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-generate-btn {
|
||||||
|
background-color: #722ed1 !important;
|
||||||
|
border-color: #722ed1 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #9254de !important;
|
||||||
|
border-color: #9254de !important;
|
||||||
|
box-shadow: 0 4px 12px rgba(114, 46, 209, 0.4) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-textarea {
|
.content-textarea {
|
||||||
|
|||||||
@@ -488,7 +488,11 @@
|
|||||||
|
|
||||||
<!-- 返回顶部按钮 -->
|
<!-- 返回顶部按钮 -->
|
||||||
<a-back-top :target="getScrollContainer" />
|
<a-back-top :target="getScrollContainer" />
|
||||||
<FileModal ref="fileModal" :current-company-id="currentCompanyId" />
|
<FileModal
|
||||||
|
ref="fileModal"
|
||||||
|
:current-company-id="currentCompanyId"
|
||||||
|
@confirm="handleFileSelectConfirm"
|
||||||
|
/>
|
||||||
<HistoryModal
|
<HistoryModal
|
||||||
v-model:visible="showHistory"
|
v-model:visible="showHistory"
|
||||||
:interface-name="currentInterfaceName"
|
:interface-name="currentInterfaceName"
|
||||||
@@ -523,7 +527,7 @@
|
|||||||
RobotOutlined,
|
RobotOutlined,
|
||||||
SaveOutlined
|
SaveOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { generateAuditReport } from '@/api/ai/auditReport';
|
import { generateDefaultText } from '@/api/ai/auditReport';
|
||||||
import { getPwlProjectLibraryByIds } from '@/api/pwl/pwlProjectLibrary';
|
import { getPwlProjectLibraryByIds } from '@/api/pwl/pwlProjectLibrary';
|
||||||
import { addAiHistory } from '@/api/ai/aiHistory';
|
import { addAiHistory } from '@/api/ai/aiHistory';
|
||||||
import * as auditContentApi from '@/api/ai/auditContent';
|
import * as auditContentApi from '@/api/ai/auditContent';
|
||||||
@@ -593,7 +597,6 @@
|
|||||||
|
|
||||||
// ========== 文档选择相关 ==========
|
// ========== 文档选择相关 ==========
|
||||||
const fileModal = ref();
|
const fileModal = ref();
|
||||||
const showDocSelect = ref(false);
|
|
||||||
const currentSectionIndex = ref(0);
|
const currentSectionIndex = ref(0);
|
||||||
const selectedDocList = ref<string[]>([]);
|
const selectedDocList = ref<string[]>([]);
|
||||||
const selectedFileList = ref<string[]>([]);
|
const selectedFileList = ref<string[]>([]);
|
||||||
@@ -731,10 +734,19 @@
|
|||||||
message.success(`已切换到:${tableOption.title}`);
|
message.success(`已切换到:${tableOption.title}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* 处理文件选择确认 */
|
||||||
|
const handleFileSelectConfirm = (data: { dirKeys: (string | number)[], fileKeys: (string | number)[] }) => {
|
||||||
|
checkedDirKeys.value = data.dirKeys;
|
||||||
|
selectedFileKeys.value = data.fileKeys;
|
||||||
|
lastSelectedDirKeys.value = [...data.dirKeys];
|
||||||
|
lastSelectedFileKeys.value = [...data.fileKeys];
|
||||||
|
selectedDocList.value = data.dirKeys.map((key) => key.toString());
|
||||||
|
selectedFileList.value = data.fileKeys.map((key) => key.toString());
|
||||||
|
};
|
||||||
|
|
||||||
/* 打开文档选择弹窗 */
|
/* 打开文档选择弹窗 */
|
||||||
const openDocSelect = (sectionIndex: number) => {
|
const openDocSelect = (sectionIndex: number) => {
|
||||||
currentSectionIndex.value = sectionIndex;
|
currentSectionIndex.value = sectionIndex;
|
||||||
showDocSelect.value = true;
|
|
||||||
|
|
||||||
const tableInfo = getTableInfo(sectionIndex);
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
if (!tableInfo) return;
|
if (!tableInfo) return;
|
||||||
@@ -748,7 +760,7 @@
|
|||||||
key.toString()
|
key.toString()
|
||||||
);
|
);
|
||||||
|
|
||||||
fileModal.value.open(tableInfo.tableKey);
|
fileModal.value.open();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1267,13 +1279,17 @@
|
|||||||
|
|
||||||
section.generating = true;
|
section.generating = true;
|
||||||
try {
|
try {
|
||||||
section.content = await generateAuditReport({
|
// 获取章节标题
|
||||||
kbId: form.kbId || '',
|
const chapterTitle = childIndex !== undefined && section.children
|
||||||
kbIds: combinedKbIds.value,
|
? section.children[childIndex].title
|
||||||
|
: section.title;
|
||||||
|
|
||||||
|
section.content = await generateDefaultText({
|
||||||
|
projectId: form.id,
|
||||||
formCommit: section.formCommit || 0,
|
formCommit: section.formCommit || 0,
|
||||||
history: section.content || '',
|
chapterTitle: chapterTitle,
|
||||||
suggestion: section.suggestion || '',
|
docList: checkedDirKeys.value,
|
||||||
...contextData
|
fileList: selectedFileKeys.value
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
section.generating = false;
|
section.generating = false;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单 -->
|
||||||
<template>
|
<template>
|
||||||
<a-space :size="10" style="flex-wrap: wrap">
|
<a-space :size="10" style="flex-wrap: wrap">
|
||||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
<a-button v-if="false" type="primary" class="ele-btn-icon" @click="add">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
<a-popconfirm
|
<a-popconfirm
|
||||||
title="确定要删除此记录吗?"
|
title="确定要删除此记录吗?"
|
||||||
@confirm="remove(record)"
|
@confirm="remove(record)"
|
||||||
|
v-if="false"
|
||||||
>
|
>
|
||||||
<a class="ele-text-danger">删除</a>
|
<a class="ele-text-danger">删除</a>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
@@ -63,51 +64,143 @@
|
|||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="showDocManage"
|
v-model:visible="showDocManage"
|
||||||
:title="`文档管理 - ${currentKbName}`"
|
:title="`文档管理 - ${currentKbName}`"
|
||||||
width="800px"
|
width="1200px"
|
||||||
:footer="null"
|
:footer="null"
|
||||||
|
wrap-class-name="doc-manage-modal"
|
||||||
>
|
>
|
||||||
<div style="margin-bottom: 16px;">
|
<div class="doc-manage-container">
|
||||||
<a-button type="primary" @click="openImport">新增文档</a-button>
|
<div class="doc-layout">
|
||||||
</div>
|
<!-- 左侧目录树 -->
|
||||||
<a-table
|
<div class="dir-tree-panel">
|
||||||
:dataSource="docList"
|
<div class="dir-header">
|
||||||
:columns="docColumns"
|
<span>文档目录</span>
|
||||||
:loading="docLoading"
|
<a-space>
|
||||||
rowKey="id"
|
<a-button
|
||||||
:pagination="{
|
type="link"
|
||||||
current: currentPage,
|
size="small"
|
||||||
pageSize: 10,
|
@click="openEditDir"
|
||||||
total: total,
|
title="编辑目录"
|
||||||
showSizeChanger: false,
|
:disabled="!selectedKeys.length"
|
||||||
showTotal: (total) => `共 ${total} 条`
|
>
|
||||||
}"
|
<template #icon><EditOutlined /></template>
|
||||||
@change="(pag) => { currentPage = pag.current; loadDocuments(); }"
|
</a-button>
|
||||||
>
|
<a-button
|
||||||
<template #bodyCell="{ column, record }">
|
type="link"
|
||||||
<template v-if="column.key === 'action'">
|
size="small"
|
||||||
<a-space>
|
@click="openAddDir"
|
||||||
<a-popconfirm
|
title="新增目录"
|
||||||
title="确定要删除此文档吗?"
|
>
|
||||||
@confirm="deleteDoc(record)"
|
<template #icon><PlusOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
@click="deleteSelectedDir"
|
||||||
|
title="删除目录"
|
||||||
|
:disabled="!selectedKeys.length"
|
||||||
|
class="ele-text-danger"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="tree-container">
|
||||||
|
<a-tree
|
||||||
|
v-if="treeData.length > 0"
|
||||||
|
:tree-data="treeData"
|
||||||
|
:expanded-keys="expandedKeys"
|
||||||
|
:selected-keys="selectedKeys"
|
||||||
|
:load-data="onLoadData"
|
||||||
|
@expand="onExpand"
|
||||||
|
@select="onSelect"
|
||||||
|
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
||||||
>
|
>
|
||||||
<a class="ele-text-danger">删除</a>
|
<template #title="{ name, id }">
|
||||||
</a-popconfirm>
|
<span :class="{ 'active-dir': selectedKeys[0] === id }">{{ name }}</span>
|
||||||
</a-space>
|
</template>
|
||||||
</template>
|
</a-tree>
|
||||||
</template>
|
<a-empty v-else :image="simpleImage" description="暂无目录" />
|
||||||
</a-table>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧文档列表 -->
|
||||||
|
<div class="doc-list-panel">
|
||||||
|
<div class="doc-header">
|
||||||
|
<div class="doc-actions">
|
||||||
|
<a-button type="primary" @click="openImport">
|
||||||
|
<template #icon><UploadOutlined /></template>
|
||||||
|
上传文档
|
||||||
|
</a-button>
|
||||||
|
<span class="doc-tips">请选择分类上传资料</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="doc-content">
|
||||||
|
<a-table
|
||||||
|
:dataSource="docList"
|
||||||
|
:columns="docColumns"
|
||||||
|
:loading="docLoading"
|
||||||
|
rowKey="id"
|
||||||
|
:scroll="{ y: 500 }"
|
||||||
|
:pagination="pagination"
|
||||||
|
@change="handleTableChange"
|
||||||
|
size="middle"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'action'">
|
||||||
|
<a-space>
|
||||||
|
<a-popconfirm
|
||||||
|
title="确定要删除此文档吗?"
|
||||||
|
@confirm="deleteDoc(record)"
|
||||||
|
>
|
||||||
|
<a class="ele-text-danger">删除</a>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<!-- 导入弹窗 -->
|
<!-- 导入弹窗 -->
|
||||||
<Import v-model:visible="showImport" @done="loadDocuments" :kbId="currentKbId"/>
|
<Import2
|
||||||
|
v-model:visible="showImport"
|
||||||
|
@done="loadCloudFiles"
|
||||||
|
:doc="selectedDoc"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 新增/编辑目录弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="showDirModal"
|
||||||
|
:title="dirModalTitle"
|
||||||
|
width="400px"
|
||||||
|
@ok="handleSaveDir"
|
||||||
|
@cancel="closeDirModal"
|
||||||
|
>
|
||||||
|
<a-form :model="dirForm" layout="vertical">
|
||||||
|
<a-form-item label="目录名称">
|
||||||
|
<a-input v-model:value="dirForm.name" placeholder="请输入目录名称" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="排序号">
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="dirForm.sortNumber"
|
||||||
|
placeholder="请输入排序号"
|
||||||
|
:min="0"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { createVNode, ref } from 'vue';
|
import { createVNode, ref, computed } from 'vue';
|
||||||
import { message, Modal } from 'ant-design-vue';
|
import { message, Modal, Empty } from 'ant-design-vue';
|
||||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
import { ExclamationCircleOutlined, PlusOutlined, UploadOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
import type { EleProTable } from 'ele-admin-pro';
|
import type { EleProTable } from 'ele-admin-pro';
|
||||||
import {toDateString} from 'ele-admin-pro';
|
import {toDateString} from 'ele-admin-pro';
|
||||||
import type {
|
import type {
|
||||||
@@ -118,8 +211,11 @@
|
|||||||
import PwlProjectLibraryEdit from './components/pwlProjectLibraryEdit.vue';
|
import PwlProjectLibraryEdit from './components/pwlProjectLibraryEdit.vue';
|
||||||
import { pagePwlProjectLibrary, removePwlProjectLibrary, removeBatchPwlProjectLibrary } from '@/api/pwl/pwlProjectLibrary';
|
import { pagePwlProjectLibrary, removePwlProjectLibrary, removeBatchPwlProjectLibrary } from '@/api/pwl/pwlProjectLibrary';
|
||||||
import type { PwlProjectLibrary, PwlProjectLibraryParam } from '@/api/pwl/pwlProjectLibrary/model';
|
import type { PwlProjectLibrary, PwlProjectLibraryParam } from '@/api/pwl/pwlProjectLibrary/model';
|
||||||
import Import from '@/views/oa/oaCompany/components/Import.vue';
|
import Import2 from '@/views/oa/oaCompany/components/Import2.vue';
|
||||||
import {getKnowledgeBaseDocuments, deleteKnowledgeBaseDocument} from '@/api/ai/knowledgeBase';
|
import { listAiCloudDoc, addAiCloudDoc, updateAiCloudDoc, removeAiCloudDoc } from '@/api/ai/aiCloudDoc';
|
||||||
|
import type { AiCloudDoc } from '@/api/ai/aiCloudDoc/model';
|
||||||
|
import { listAiCloudFile, removeAiCloudFile } from '@/api/ai/aiCloudFile';
|
||||||
|
import type { AiCloudFile } from '@/api/ai/aiCloudFile/model';
|
||||||
|
|
||||||
// 表格实例
|
// 表格实例
|
||||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||||
@@ -138,37 +234,129 @@
|
|||||||
// 文档管理相关响应式变量
|
// 文档管理相关响应式变量
|
||||||
const showDocManage = ref(false); // 是否显示文档管理弹窗
|
const showDocManage = ref(false); // 是否显示文档管理弹窗
|
||||||
const showImport = ref(false); // 是否显示导入弹窗
|
const showImport = ref(false); // 是否显示导入弹窗
|
||||||
// 新增分页状态变量
|
const showDirModal = ref(false); // 是否显示目录弹窗(新增/编辑)
|
||||||
const currentPage = ref(1);
|
|
||||||
const total = ref(0);
|
|
||||||
|
|
||||||
// 文档管理相关变量
|
// 文档管理相关变量
|
||||||
const currentKbId = ref(''); // 当前知识库ID
|
const currentKbId = ref(''); // 当前知识库ID
|
||||||
const currentKbName = ref(''); // 当前知识库名称
|
const currentKbName = ref(''); // 当前知识库名称
|
||||||
const docList = ref<any[]>([]); // 文档列表数据
|
const currentCompanyId = ref<number>(0); // 当前单位ID - 固定为0
|
||||||
|
const docList = ref<AiCloudFile[]>([]); // 文档列表数据
|
||||||
const docLoading = ref(false); // 文档加载状态
|
const docLoading = ref(false); // 文档加载状态
|
||||||
|
const allDirs = ref<AiCloudDoc[]>([]); // 所有目录列表
|
||||||
|
|
||||||
|
// 树形结构相关
|
||||||
|
const expandedKeys = ref<(string | number)[]>([]);
|
||||||
|
const selectedKeys = ref<(string | number)[]>([]);
|
||||||
|
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||||
|
|
||||||
|
// 目录弹窗相关
|
||||||
|
const isEditingDir = ref(false); // 是否为编辑模式
|
||||||
|
const currentDirId = ref<number>(); // 当前操作的目录ID
|
||||||
|
|
||||||
|
// 目录表单
|
||||||
|
const dirForm = ref({
|
||||||
|
name: '',
|
||||||
|
sortNumber: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算目录弹窗标题
|
||||||
|
const dirModalTitle = computed(() => {
|
||||||
|
const mode = isEditingDir.value ? '编辑' : '新增';
|
||||||
|
const location = selectedDirName.value ? `在『${selectedDirName.value}』下` : '在根目录下';
|
||||||
|
return `${mode}目录 - ${location}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 分页配置
|
||||||
|
const pagination = ref({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
showSizeChanger: false,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (total: number) => `共 ${total} 条`,
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100']
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算树形数据
|
||||||
|
const treeData = computed(() => {
|
||||||
|
const buildTree = (parentId: number = 0): any[] => {
|
||||||
|
return allDirs.value
|
||||||
|
.filter(item => item.parentId === parentId)
|
||||||
|
.map(item => ({
|
||||||
|
...item,
|
||||||
|
key: item.id,
|
||||||
|
title: item.name,
|
||||||
|
children: buildTree(item.id),
|
||||||
|
isLeaf: allDirs.value.filter(child => child.parentId === item.id).length === 0
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
return buildTree(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 选中的目录名称
|
||||||
|
const selectedDirName = computed(() => {
|
||||||
|
if (selectedKeys.value.length === 0) return '';
|
||||||
|
const selectedId = selectedKeys.value[0];
|
||||||
|
const dir = allDirs.value.find(item => item.id === selectedId);
|
||||||
|
return dir?.name || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算选中的目录ID(用于文件上传)
|
||||||
|
const selectedCategoryId = computed(() => {
|
||||||
|
if (selectedKeys.value.length === 0) return '';
|
||||||
|
return selectedKeys.value[0].toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算选中的文档对象
|
||||||
|
const selectedDoc = computed(() => {
|
||||||
|
if (selectedKeys.value.length === 0) return null;
|
||||||
|
const selectedId = selectedKeys.value[0];
|
||||||
|
const doc = allDirs.value.find(item => item.id === selectedId);
|
||||||
|
return doc ? {
|
||||||
|
id: doc.id!,
|
||||||
|
categoryId: doc.categoryId || ''
|
||||||
|
} : null;
|
||||||
|
});
|
||||||
|
|
||||||
// 文档表格列配置
|
// 文档表格列配置
|
||||||
const docColumns = ref([
|
const docColumns = ref([
|
||||||
{
|
{
|
||||||
title: '文件名',
|
title: '文件名',
|
||||||
dataIndex: 'name', // 改为接口返回的name字段
|
dataIndex: 'fileName',
|
||||||
key: 'fileName',
|
key: 'fileName',
|
||||||
|
ellipsis: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '文件大小',
|
title: '文件大小',
|
||||||
dataIndex: 'size', // 改为接口返回的size字段
|
dataIndex: 'fileSize',
|
||||||
key: 'fileSize',
|
key: 'fileSize',
|
||||||
|
width: 120,
|
||||||
|
customRender: ({ text }: { text: string }) => {
|
||||||
|
if (!text) return '-';
|
||||||
|
const size = Number(text);
|
||||||
|
if (size < 1024) return size + ' B';
|
||||||
|
if (size < 1024 * 1024) return (size / 1024).toFixed(1) + ' KB';
|
||||||
|
return (size / (1024 * 1024)).toFixed(1) + ' MB';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '文件类型',
|
||||||
|
dataIndex: 'fileType',
|
||||||
|
key: 'fileType',
|
||||||
|
width: 120,
|
||||||
|
ellipsis: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '上传时间',
|
title: '上传时间',
|
||||||
dataIndex: 'gmtModified', // 改为接口返回的gmtModified字段
|
dataIndex: 'uploadTime',
|
||||||
key: 'createTime',
|
key: 'uploadTime',
|
||||||
customRender: ({ text }) => toDateString(text) // 添加时间格式化
|
width: 180,
|
||||||
|
customRender: ({ text }: { text: string }) => toDateString(text)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: 100,
|
width: 80
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -276,48 +464,111 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 打开文档管理弹窗
|
// 打开文档管理弹窗
|
||||||
const openDocManage = (record: PwlProjectLibrary) => {
|
const openDocManage = async (record: PwlProjectLibrary) => {
|
||||||
currentKbId.value = record.kbId; // 使用record中的kbId
|
currentKbId.value = record.kbId;
|
||||||
currentKbName.value = record.name; // 使用单位名称作为知识库名称
|
currentKbName.value = record.name;
|
||||||
currentPage.value = 1;
|
currentCompanyId.value = 0; // 固定为0
|
||||||
|
pagination.value.current = 1;
|
||||||
showDocManage.value = true;
|
showDocManage.value = true;
|
||||||
loadDocuments();
|
|
||||||
|
// 重置选择状态
|
||||||
|
expandedKeys.value = [];
|
||||||
|
selectedKeys.value = [];
|
||||||
|
|
||||||
|
// 加载目录列表
|
||||||
|
await loadAllCloudDocs(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载所有目录
|
||||||
|
const loadAllCloudDocs = async (resetState = false) => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
companyId: 0 // 固定为0
|
||||||
|
};
|
||||||
|
const result = await listAiCloudDoc(params);
|
||||||
|
allDirs.value = result || [];
|
||||||
|
|
||||||
|
if (resetState) {
|
||||||
|
// 默认展开根节点并选中第一个目录
|
||||||
|
if (allDirs.value.length > 0) {
|
||||||
|
const rootDirs = allDirs.value.filter(item => item.parentId === 0);
|
||||||
|
if (rootDirs.length > 0) {
|
||||||
|
expandedKeys.value = [rootDirs[0].id!];
|
||||||
|
selectedKeys.value = [rootDirs[0].id!];
|
||||||
|
loadCloudFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error('加载目录列表失败');
|
||||||
|
console.error('加载目录错误:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树节点展开
|
||||||
|
const onExpand = (keys: (string | number)[]) => {
|
||||||
|
expandedKeys.value = keys;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树节点选择
|
||||||
|
const onSelect = (keys: (string | number)[], { node }: any) => {
|
||||||
|
selectedKeys.value = keys;
|
||||||
|
pagination.value.current = 1;
|
||||||
|
loadCloudFiles();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 异步加载子节点
|
||||||
|
const onLoadData = (node: any) => {
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 表格分页变化处理
|
||||||
|
const handleTableChange = (pag: any) => {
|
||||||
|
pagination.value.current = pag.current;
|
||||||
|
pagination.value.pageSize = pag.pageSize;
|
||||||
|
loadCloudFiles();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载文档列表
|
// 加载文档列表
|
||||||
const loadDocuments = async () => {
|
const loadCloudFiles = async () => {
|
||||||
docLoading.value = true;
|
docLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await getKnowledgeBaseDocuments(
|
if (!selectedCategoryId.value) {
|
||||||
currentKbId.value,
|
docList.value = [];
|
||||||
10,
|
pagination.value.total = 0;
|
||||||
currentPage.value
|
return;
|
||||||
);
|
}
|
||||||
docList.value = Array.isArray(response?.list) ? response.list : [];
|
|
||||||
total.value = response?.count || 0;
|
const params = {
|
||||||
|
docId: parseInt(selectedCategoryId.value),
|
||||||
|
page: pagination.value.current,
|
||||||
|
pageSize: pagination.value.pageSize
|
||||||
|
};
|
||||||
|
const result = await listAiCloudFile(params);
|
||||||
|
|
||||||
|
if (result && result.records) {
|
||||||
|
docList.value = result.records;
|
||||||
|
pagination.value.total = result.total;
|
||||||
|
} else {
|
||||||
|
docList.value = result || [];
|
||||||
|
pagination.value.total = docList.value.length;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('加载文档列表失败');
|
message.error('加载文件列表失败');
|
||||||
console.error('加载文档错误:', error);
|
console.error('加载文件错误:', error);
|
||||||
} finally {
|
} finally {
|
||||||
docLoading.value = false;
|
docLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除文档
|
// 删除文档
|
||||||
const deleteDoc = async (record: any) => {
|
const deleteDoc = async (record: AiCloudFile) => {
|
||||||
try {
|
try {
|
||||||
// 执行删除操作
|
await removeAiCloudFile(record.id);
|
||||||
await deleteKnowledgeBaseDocument(currentKbId.value, record.id);
|
await loadCloudFiles();
|
||||||
|
|
||||||
// 立即本地删除(核心修改)
|
|
||||||
const index = docList.value.findIndex(item => item.id === record.id);
|
|
||||||
if (index > -1) {
|
|
||||||
docList.value.splice(index, 1);
|
|
||||||
total.value -= 1;
|
|
||||||
}
|
|
||||||
message.success('删除成功');
|
message.success('删除成功');
|
||||||
// 阿里云异步删除,需等待异步删除完成再重新查询
|
|
||||||
// await loadDocuments();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('删除失败');
|
message.error('删除失败');
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -326,9 +577,150 @@
|
|||||||
|
|
||||||
// 打开导入弹窗
|
// 打开导入弹窗
|
||||||
const openImport = () => {
|
const openImport = () => {
|
||||||
|
if (selectedKeys.value.length === 0) {
|
||||||
|
message.info('请先选择一个目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedDoc.value) {
|
||||||
|
message.info('选中的目录信息不完整');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
showImport.value = true;
|
showImport.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 打开新增目录弹窗
|
||||||
|
const openAddDir = () => {
|
||||||
|
if (selectedKeys.value.length === 0) {
|
||||||
|
message.info('请先选择一个目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isEditingDir.value = false;
|
||||||
|
currentDirId.value = undefined;
|
||||||
|
dirForm.value.name = '';
|
||||||
|
dirForm.value.sortNumber = 0;
|
||||||
|
showDirModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开编辑目录弹窗
|
||||||
|
const openEditDir = () => {
|
||||||
|
if (selectedKeys.value.length === 0) {
|
||||||
|
message.info('请先选择一个目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedDir = allDirs.value.find(item => item.id === selectedKeys.value[0]);
|
||||||
|
if (!selectedDir) {
|
||||||
|
message.error('未找到选中的目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEditingDir.value = true;
|
||||||
|
currentDirId.value = selectedDir.id;
|
||||||
|
dirForm.value.name = selectedDir.name || '';
|
||||||
|
dirForm.value.sortNumber = selectedDir.sortNumber || 0;
|
||||||
|
showDirModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除选中目录
|
||||||
|
const deleteSelectedDir = async () => {
|
||||||
|
if (selectedKeys.value.length === 0) {
|
||||||
|
message.info('请先选择一个目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedDir = allDirs.value.find(item => item.id === selectedKeys.value[0]);
|
||||||
|
if (!selectedDir) {
|
||||||
|
message.error('未找到选中的目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteDir(selectedDir.id!, selectedDir.name || '');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭目录弹窗
|
||||||
|
const closeDirModal = () => {
|
||||||
|
showDirModal.value = false;
|
||||||
|
dirForm.value.name = '';
|
||||||
|
dirForm.value.sortNumber = 0;
|
||||||
|
currentDirId.value = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理保存目录(新增/编辑)
|
||||||
|
const handleSaveDir = async () => {
|
||||||
|
if (!dirForm.value.name) {
|
||||||
|
message.error('请输入目录名称');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isEditingDir.value) {
|
||||||
|
const updateData: AiCloudDoc = {
|
||||||
|
id: currentDirId.value,
|
||||||
|
name: dirForm.value.name,
|
||||||
|
sortNumber: dirForm.value.sortNumber || 0
|
||||||
|
};
|
||||||
|
await updateAiCloudDoc(updateData);
|
||||||
|
message.success('修改目录成功');
|
||||||
|
} else {
|
||||||
|
const newDir: AiCloudDoc = {
|
||||||
|
companyId: 0, // 固定为0
|
||||||
|
parentId: selectedKeys.value[0] as number,
|
||||||
|
name: dirForm.value.name,
|
||||||
|
sortNumber: dirForm.value.sortNumber || 0
|
||||||
|
};
|
||||||
|
await addAiCloudDoc(newDir);
|
||||||
|
message.success('新增目录成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
showDirModal.value = false;
|
||||||
|
await loadAllCloudDocs(false);
|
||||||
|
|
||||||
|
if (!isEditingDir.value) {
|
||||||
|
const parentId = selectedKeys.value[0] as number;
|
||||||
|
if (!expandedKeys.value.includes(parentId)) {
|
||||||
|
expandedKeys.value = [...expandedKeys.value, parentId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error(isEditingDir.value ? '修改目录失败' : '新增目录失败');
|
||||||
|
console.error('目录操作错误:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除目录
|
||||||
|
const deleteDir = async (dirId: number, dirName: string) => {
|
||||||
|
const hasChildren = allDirs.value.some(item => item.parentId === dirId);
|
||||||
|
if (hasChildren) {
|
||||||
|
message.error(`目录「${dirName}」包含子目录,无法删除。请先删除该目录下的所有子目录。`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: '提示',
|
||||||
|
content: `确定要删除目录「${dirName}」吗?`,
|
||||||
|
icon: createVNode(ExclamationCircleOutlined),
|
||||||
|
maskClosable: true,
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await removeAiCloudDoc(dirId);
|
||||||
|
message.success('删除目录成功');
|
||||||
|
await loadAllCloudDocs(false);
|
||||||
|
|
||||||
|
if (selectedKeys.value.includes(dirId)) {
|
||||||
|
selectedKeys.value = [];
|
||||||
|
docList.value = [];
|
||||||
|
pagination.value.total = 0;
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || '删除目录失败');
|
||||||
|
console.error('删除目录错误:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* 删除单个 */
|
/* 删除单个 */
|
||||||
const remove = (row: PwlProjectLibrary) => {
|
const remove = (row: PwlProjectLibrary) => {
|
||||||
@@ -399,4 +791,175 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style lang="less" scoped>
|
||||||
|
/* 文档管理布局 */
|
||||||
|
.doc-manage-container {
|
||||||
|
height: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-layout {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dir-tree-panel {
|
||||||
|
width: 280px;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dir-header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
background: #fafafa;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-container {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-list-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-tips {
|
||||||
|
color: #ff4d4f;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 树节点激活样式 */
|
||||||
|
:deep(.active-dir) {
|
||||||
|
color: #1890ff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-tree-node-content-wrapper) {
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-tree-node-content-wrapper:hover) {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-tree .ant-tree-treenode-selected .ant-tree-node-content-wrapper) {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优化表格样式 */
|
||||||
|
:deep(.doc-manage-modal .ant-modal-body) {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-table-wrapper) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-spin-nested-loading) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-spin-container) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-table) {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-table-container) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-table-body) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-table-thead > tr > th) {
|
||||||
|
background: #fafafa;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-table-tbody > tr > td) {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件名列特殊处理,允许换行 */
|
||||||
|
:deep(.doc-content .ant-table-tbody > tr > td:first-child) {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.4;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分页样式调整 */
|
||||||
|
:deep(.doc-content .ant-pagination) {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.doc-content .ant-table-pagination) {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 删除按钮样式 */
|
||||||
|
:deep(.ele-text-danger) {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user