Browse Source

合同优化

jyj_dev
zhouhaibin 1 month ago
parent
commit
b47d9aa8ed
  1. 61
      src/api/contractReview/ContractualAuditConfig/index.ts
  2. 94
      src/api/contractReview/ContractualAuditConfig/model.ts
  3. 57
      src/api/contractReview/ContractualProductInfo/index.ts
  4. 160
      src/api/contractReview/ContractualProductInfo/model.ts
  5. 10
      src/api/contractReview/JyjcontractualTaskBatch/index.ts
  6. 57
      src/api/contractReview/RequirementContractualNormal/index.ts
  7. 70
      src/api/contractReview/RequirementContractualNormal/model.ts
  8. 57
      src/api/contractReview/RequirementCpu/index.ts
  9. 85
      src/api/contractReview/RequirementCpu/model.ts
  10. 57
      src/api/contractReview/RequirementDatabase/index.ts
  11. 85
      src/api/contractReview/RequirementDatabase/model.ts
  12. 57
      src/api/contractReview/RequirementEntity/index.ts
  13. 100
      src/api/contractReview/RequirementEntity/model.ts
  14. 57
      src/api/contractReview/RequirementOs/index.ts
  15. 85
      src/api/contractReview/RequirementOs/model.ts
  16. 57
      src/api/contractReview/RequirementProductAttribute/index.ts
  17. 85
      src/api/contractReview/RequirementProductAttribute/model.ts
  18. 5
      src/components/Application/src/AppLogo.vue
  19. 7
      src/views/auth/login/Login.vue
  20. 4
      src/views/chat/index.vue
  21. 72
      src/views/contractReview/ContractualAuditConfig/ContractualAuditConfig.data.ts
  22. 177
      src/views/contractReview/ContractualAuditConfig/ContractualAuditConfigModal.vue
  23. 148
      src/views/contractReview/ContractualAuditConfig/components/contractualNormalComponent.vue
  24. 151
      src/views/contractReview/ContractualAuditConfig/components/cpuOsDatebaseComponent.vue
  25. 459
      src/views/contractReview/ContractualAuditConfig/components/entityTreeComponent.vue
  26. 163
      src/views/contractReview/ContractualAuditConfig/components/productAttributeComponent.vue
  27. 115
      src/views/contractReview/ContractualAuditConfig/index.vue
  28. 156
      src/views/contractReview/ContractualProductInfo/ContractualProductInfo.data.ts
  29. 68
      src/views/contractReview/ContractualProductInfo/ContractualProductInfoModal.vue
  30. 115
      src/views/contractReview/ContractualProductInfo/index.vue
  31. 458
      src/views/contractReview/ContractualTasks/ContractualShowModa11111l.vue
  32. 255
      src/views/contractReview/ContractualTasks/ContractualShowModal.vue
  33. 20
      src/views/contractReview/ContractualTasks/ContractualTasks.data.ts
  34. 38
      src/views/contractReview/ContractualTasks/ContractualTasksTable.vue
  35. 89
      src/views/contractReview/ContractualTasks/PdfViewer.vue
  36. 2
      src/views/contractReview/ContractualTasks/showResultCard.vue
  37. 12
      src/views/contractReview/JyjcontractualTaskBatch/JyjcontractualTaskBatch.data.ts
  38. 66
      src/views/contractReview/RequirementContractualNormal/RequirementContractualNormal.data.ts
  39. 68
      src/views/contractReview/RequirementContractualNormal/RequirementContractualNormalModal.vue
  40. 115
      src/views/contractReview/RequirementContractualNormal/index.vue
  41. 81
      src/views/contractReview/RequirementCpu/RequirementCpu.data.ts
  42. 68
      src/views/contractReview/RequirementCpu/RequirementCpuModal.vue
  43. 115
      src/views/contractReview/RequirementCpu/index.vue
  44. 81
      src/views/contractReview/RequirementDatabase/RequirementDatabase.data.ts
  45. 68
      src/views/contractReview/RequirementDatabase/RequirementDatabaseModal.vue
  46. 115
      src/views/contractReview/RequirementDatabase/index.vue
  47. 96
      src/views/contractReview/RequirementEntity/RequirementEntity.data.ts
  48. 68
      src/views/contractReview/RequirementEntity/RequirementEntityModal.vue
  49. 115
      src/views/contractReview/RequirementEntity/index.vue
  50. 81
      src/views/contractReview/RequirementOs/RequirementOs.data.ts
  51. 68
      src/views/contractReview/RequirementOs/RequirementOsModal.vue
  52. 115
      src/views/contractReview/RequirementOs/index.vue
  53. 81
      src/views/contractReview/RequirementProductAttribute/RequirementProductAttribute.data.ts
  54. 68
      src/views/contractReview/RequirementProductAttribute/RequirementProductAttributeModal.vue
  55. 115
      src/views/contractReview/RequirementProductAttribute/index.vue
  56. 374
      src/views/workbench/index.vue

61
src/api/contractReview/ContractualAuditConfig/index.ts

@ -0,0 +1,61 @@
import { defHttp } from '@/utils/http/axios';
import { ID, IDS, commonExport } from '@/api/base';
import { ContractualAuditConfigVO, ContractualAuditConfigForm, ContractualAuditConfigQuery } from './model';
/**
*
* @param params
* @returns
*/
export function ContractualAuditConfigList(params?: ContractualAuditConfigQuery) {
return defHttp.get<ContractualAuditConfigVO[]>({ url: '/productManagement/ContractualAuditConfig/list', params });
}
/**
*
* @param params
* @returns
*/
export function ContractualAuditConfigExport(params?: ContractualAuditConfigQuery) {
return commonExport('/productManagement/ContractualAuditConfig/export', params ?? {});
}
/**
*
* @param id id
* @returns
*/
export function ContractualAuditConfigInfo(id: ID) {
return defHttp.get<ContractualAuditConfigVO>({ url: '/productManagement/ContractualAuditConfig/' + id });
}
/**
*
* @param data
* @returns
*/
export function ContractualAuditConfigAdd(data: ContractualAuditConfigForm) {
return defHttp.postWithMsg<void>({ url: '/productManagement/ContractualAuditConfig', data });
}
/**
*
* @param data
* @returns
*/
export function ContractualAuditConfigUpdate(data: ContractualAuditConfigForm) {
return defHttp.putWithMsg<void>({ url: '/productManagement/ContractualAuditConfig', data });
}
/**
*
* @param id id
* @returns
*/
export function ContractualAuditConfigRemove(id: ID | IDS) {
return defHttp.deleteWithMsg<void>({ url: '/productManagement/ContractualAuditConfig/' + id },);
}
export function getRequirementTypeDict(){
return defHttp.get<any>({url:"/productManagement/ContractualAuditConfig/getRequirementTypeDict"})
}

94
src/api/contractReview/ContractualAuditConfig/model.ts

@ -0,0 +1,94 @@
import { BaseEntity, PageQuery } from '@/api/base';
export interface ContractualAuditConfigVO {
/**
* ID
*/
id: string | number;
/**
*
*/
auditElement: string;
/**
* ID
*/
requirementId: string | number;
/**
*
*/
requirementDescription: string;
/**
* 0-1-
*/
isForm: number;
/**
* CPU/OS/
*/
requirementCpuosdatabase?: any[];
/**
*
*/
requirementEntity?: any[];
/**
*
*/
requirementProductAttribute?: any[];
/**
*
*/
requirementContractualNormal?: any[];
}
export interface ContractualAuditConfigForm extends BaseEntity {
/**
* ID
*/
id?: string | number;
/**
*
*/
auditElement?: string;
/**
* ID
*/
requirementId?: string | number;
/**
*
*/
requirementDescription?: string;
}
export interface ContractualAuditConfigQuery extends PageQuery {
/**
*
*/
auditElement?: string;
/**
* ID
*/
requirementId?: string | number;
/**
*
*/
requirementDescription?: string;
/**
*
*/
params?: any;
}

57
src/api/contractReview/ContractualProductInfo/index.ts

@ -0,0 +1,57 @@
import { defHttp } from '@/utils/http/axios';
import { ID, IDS, commonExport } from '@/api/base';
import { ContractualProductInfoVO, ContractualProductInfoForm, ContractualProductInfoQuery } from './model';
/**
*
* @param params
* @returns
*/
export function ContractualProductInfoList(params?: ContractualProductInfoQuery) {
return defHttp.get<ContractualProductInfoVO[]>({ url: '/productManagement/ContractualProductInfo/list', params });
}
/**
*
* @param params
* @returns
*/
export function ContractualProductInfoExport(params?: ContractualProductInfoQuery) {
return commonExport('/productManagement/ContractualProductInfo/export', params ?? {});
}
/**
*
* @param id id
* @returns
*/
export function ContractualProductInfoInfo(id: ID) {
return defHttp.get<ContractualProductInfoVO>({ url: '/productManagement/ContractualProductInfo/' + id });
}
/**
*
* @param data
* @returns
*/
export function ContractualProductInfoAdd(data: ContractualProductInfoForm) {
return defHttp.postWithMsg<void>({ url: '/productManagement/ContractualProductInfo', data });
}
/**
*
* @param data
* @returns
*/
export function ContractualProductInfoUpdate(data: ContractualProductInfoForm) {
return defHttp.putWithMsg<void>({ url: '/productManagement/ContractualProductInfo', data });
}
/**
*
* @param id id
* @returns
*/
export function ContractualProductInfoRemove(id: ID | IDS) {
return defHttp.deleteWithMsg<void>({ url: '/productManagement/ContractualProductInfo/' + id },);
}

160
src/api/contractReview/ContractualProductInfo/model.ts

@ -0,0 +1,160 @@
import { BaseEntity, PageQuery } from '@/api/base';
export interface ContractualProductInfoVO {
/**
* ID
*/
id: string | number;
/**
* ID
*/
taskId: string | number;
/**
*
*/
fileName: string;
/**
*
*/
brand: string;
/**
*
*/
versionStr: string;
/**
*
*/
unitPrice: number;
/**
*
*/
priceUnit: string;
/**
*
*/
quantity: number;
/**
*
*/
totalPrice: number;
/**
*
*/
type: string;
}
export interface ContractualProductInfoForm extends BaseEntity {
/**
* ID
*/
id?: string | number;
/**
* ID
*/
taskId?: string | number;
/**
*
*/
fileName?: string;
/**
*
*/
brand?: string;
/**
*
*/
versionStr?: string;
/**
*
*/
unitPrice?: number;
/**
*
*/
priceUnit?: string;
/**
*
*/
quantity?: number;
/**
*
*/
totalPrice?: number;
/**
*
*/
type?: string;
}
export interface ContractualProductInfoQuery extends PageQuery {
/**
* ID
*/
taskId?: string | number;
/**
*
*/
fileName?: string;
/**
*
*/
brand?: string;
/**
*
*/
versionStr?: string;
/**
*
*/
unitPrice?: number;
/**
*
*/
priceUnit?: string;
/**
*
*/
quantity?: number;
/**
*
*/
totalPrice?: number;
/**
*
*/
type?: string;
/**
*
*/
params?: any;
}

10
src/api/contractReview/JyjcontractualTaskBatch/index.ts

@ -94,10 +94,20 @@ export function getPdfFile(id:Number) {
{
url: `/productManagement/JyjcontractualTaskBatch/getContractulPdf/${id}`,
responseType: 'blob',
timeout: 1000 * 60 * 10,
headers: {
Accept: 'application/pdf',
}
},
{ isReturnNativeResponse: true }
);
}
/**
* id获取合同的审查结果
* @param id id
* @returns
*/
export function getContractulContent(id: ID) {
return defHttp.get({ url: '/productManagement/JyjcontractualTaskBatch/getContractulContent/' + id });
}

57
src/api/contractReview/RequirementContractualNormal/index.ts

@ -0,0 +1,57 @@
import { defHttp } from '@/utils/http/axios';
import { ID, IDS, commonExport } from '@/api/base';
import { RequirementContractualNormalVO, RequirementContractualNormalForm, RequirementContractualNormalQuery } from './model';
/**
*
* @param params
* @returns
*/
export function RequirementContractualNormalList(params?: RequirementContractualNormalQuery) {
return defHttp.get<RequirementContractualNormalVO[]>({ url: '/productManagement/RequirementContractualNormal/list', params });
}
/**
*
* @param params
* @returns
*/
export function RequirementContractualNormalExport(params?: RequirementContractualNormalQuery) {
return commonExport('/productManagement/RequirementContractualNormal/export', params ?? {});
}
/**
*
* @param id id
* @returns
*/
export function RequirementContractualNormalInfo(id: ID) {
return defHttp.get<RequirementContractualNormalVO>({ url: '/productManagement/RequirementContractualNormal/' + id });
}
/**
*
* @param data
* @returns
*/
export function RequirementContractualNormalAdd(data: RequirementContractualNormalForm) {
return defHttp.postWithMsg<void>({ url: '/productManagement/RequirementContractualNormal', data });
}
/**
*
* @param data
* @returns
*/
export function RequirementContractualNormalUpdate(data: RequirementContractualNormalForm) {
return defHttp.putWithMsg<void>({ url: '/productManagement/RequirementContractualNormal', data });
}
/**
*
* @param id id
* @returns
*/
export function RequirementContractualNormalRemove(id: ID | IDS) {
return defHttp.deleteWithMsg<void>({ url: '/productManagement/RequirementContractualNormal/' + id },);
}

70
src/api/contractReview/RequirementContractualNormal/model.ts

@ -0,0 +1,70 @@
import { BaseEntity, PageQuery } from '@/api/base';
export interface RequirementContractualNormalVO {
/**
* ID
*/
id: string | number;
/**
* ID
*/
configId: string | number;
/**
*
*/
checkItem: string;
/**
*
*/
requirementDesc: string;
}
export interface RequirementContractualNormalForm extends BaseEntity {
/**
* ID
*/
id?: string | number;
/**
* ID
*/
configId?: string | number;
/**
*
*/
checkItem?: string;
/**
*
*/
requirementDesc?: string;
}
export interface RequirementContractualNormalQuery extends PageQuery {
/**
* ID
*/
configId?: string | number;
/**
*
*/
checkItem?: string;
/**
*
*/
requirementDesc?: string;
/**
*
*/
params?: any;
}

57
src/api/contractReview/RequirementCpu/index.ts

@ -0,0 +1,57 @@
import { defHttp } from '@/utils/http/axios';
import { ID, IDS, commonExport } from '@/api/base';
import { RequirementCpuVO, RequirementCpuForm, RequirementCpuQuery } from './model';
/**
* CPU要求列表
* @param params
* @returns
*/
export function RequirementCpuList(params?: RequirementCpuQuery) {
return defHttp.get<RequirementCpuVO[]>({ url: '/productManagement/RequirementCpu/list', params });
}
/**
* CPU要求列表
* @param params
* @returns
*/
export function RequirementCpuExport(params?: RequirementCpuQuery) {
return commonExport('/productManagement/RequirementCpu/export', params ?? {});
}
/**
* CPU要求详细
* @param id id
* @returns
*/
export function RequirementCpuInfo(id: ID) {
return defHttp.get<RequirementCpuVO>({ url: '/productManagement/RequirementCpu/' + id });
}
/**
* CPU要求
* @param data
* @returns
*/
export function RequirementCpuAdd(data: RequirementCpuForm) {
return defHttp.postWithMsg<void>({ url: '/productManagement/RequirementCpu', data });
}
/**
* CPU要求
* @param data
* @returns
*/
export function RequirementCpuUpdate(data: RequirementCpuForm) {
return defHttp.putWithMsg<void>({ url: '/productManagement/RequirementCpu', data });
}
/**
* CPU要求
* @param id id
* @returns
*/
export function RequirementCpuRemove(id: ID | IDS) {
return defHttp.deleteWithMsg<void>({ url: '/productManagement/RequirementCpu/' + id },);
}

85
src/api/contractReview/RequirementCpu/model.ts

@ -0,0 +1,85 @@
import { BaseEntity, PageQuery } from '@/api/base';
export interface RequirementCpuVO {
/**
* ID
*/
id: string | number;
/**
* ID
*/
configId: string | number;
/**
*
*/
name: string;
/**
*
*/
model: string;
/**
*
*/
versionStr: string;
}
export interface RequirementCpuForm extends BaseEntity {
/**
* ID
*/
id?: string | number;
/**
* ID
*/
configId?: string | number;
/**
*
*/
name?: string;
/**
*
*/
model?: string;
/**
*
*/
versionStr?: string;
}
export interface RequirementCpuQuery extends PageQuery {
/**
* ID
*/
configId?: string | number;
/**
*
*/
name?: string;
/**
*
*/
model?: string;
/**
*
*/
versionStr?: string;
/**
*
*/
params?: any;
}

57
src/api/contractReview/RequirementDatabase/index.ts

@ -0,0 +1,57 @@
import { defHttp } from '@/utils/http/axios';
import { ID, IDS, commonExport } from '@/api/base';
import { RequirementDatabaseVO, RequirementDatabaseForm, RequirementDatabaseQuery } from './model';
/**
*
* @param params
* @returns
*/
export function RequirementDatabaseList(params?: RequirementDatabaseQuery) {
return defHttp.get<RequirementDatabaseVO[]>({ url: '/productManagement/RequirementDatabase/list', params });
}
/**
*
* @param params
* @returns
*/
export function RequirementDatabaseExport(params?: RequirementDatabaseQuery) {
return commonExport('/productManagement/RequirementDatabase/export', params ?? {});
}
/**
*
* @param id id
* @returns
*/
export function RequirementDatabaseInfo(id: ID) {
return defHttp.get<RequirementDatabaseVO>({ url: '/productManagement/RequirementDatabase/' + id });
}
/**
*
* @param data
* @returns
*/
export function RequirementDatabaseAdd(data: RequirementDatabaseForm) {
return defHttp.postWithMsg<void>({ url: '/productManagement/RequirementDatabase', data });
}
/**
*
* @param data
* @returns
*/
export function RequirementDatabaseUpdate(data: RequirementDatabaseForm) {
return defHttp.putWithMsg<void>({ url: '/productManagement/RequirementDatabase', data });
}
/**
*
* @param id id
* @returns
*/
export function RequirementDatabaseRemove(id: ID | IDS) {
return defHttp.deleteWithMsg<void>({ url: '/productManagement/RequirementDatabase/' + id },);
}

85
src/api/contractReview/RequirementDatabase/model.ts

@ -0,0 +1,85 @@
import { BaseEntity, PageQuery } from '@/api/base';
export interface RequirementDatabaseVO {
/**
* ID
*/
id: string | number;
/**
* ID
*/
configId: string | number;
/**
*
*/
name: string;
/**
*
*/
model: string;
/**
*
*/
versionStr: string;
}
export interface RequirementDatabaseForm extends BaseEntity {
/**
* ID
*/
id?: string | number;
/**
* ID
*/
configId?: string | number;
/**
*
*/
name?: string;
/**
*
*/
model?: string;
/**
*
*/
versionStr?: string;
}
export interface RequirementDatabaseQuery extends PageQuery {
/**
* ID
*/
configId?: string | number;
/**
*
*/
name?: string;
/**
*
*/
model?: string;
/**
*
*/
versionStr?: string;
/**
*
*/
params?: any;
}

57
src/api/contractReview/RequirementEntity/index.ts

@ -0,0 +1,57 @@
import { defHttp } from '@/utils/http/axios';
import { ID, IDS, commonExport } from '@/api/base';
import { RequirementEntityVO, RequirementEntityForm, RequirementEntityQuery } from './model';
/**
*
* @param params
* @returns
*/
export function RequirementEntityList(params?: RequirementEntityQuery) {
return defHttp.get<RequirementEntityVO[]>({ url: '/productManagement/RequirementEntity/list', params });
}
/**
*
* @param params
* @returns
*/
export function RequirementEntityExport(params?: RequirementEntityQuery) {
return commonExport('/productManagement/RequirementEntity/export', params ?? {});
}
/**
*
* @param id id
* @returns
*/
export function RequirementEntityInfo(id: ID) {
return defHttp.get<RequirementEntityVO>({ url: '/productManagement/RequirementEntity/' + id });
}
/**
*
* @param data
* @returns
*/
export function RequirementEntityAdd(data: RequirementEntityForm) {
return defHttp.postWithMsg<void>({ url: '/productManagement/RequirementEntity', data });
}
/**
*
* @param data
* @returns
*/
export function RequirementEntityUpdate(data: RequirementEntityForm) {
return defHttp.putWithMsg<void>({ url: '/productManagement/RequirementEntity', data });
}
/**
*
* @param id id
* @returns
*/
export function RequirementEntityRemove(id: ID | IDS) {
return defHttp.deleteWithMsg<void>({ url: '/productManagement/RequirementEntity/' + id },);
}

100
src/api/contractReview/RequirementEntity/model.ts

@ -0,0 +1,100 @@
import { BaseEntity, PageQuery } from '@/api/base';
export interface RequirementEntityVO {
/**
* ID
*/
id: string | number;
/**
* ID
*/
configId: string | number;
/**
*
*/
entityName: string;
/**
* ID0
*/
parentId: string | number;
/**
* 1
*/
level: number;
/**
* 0,1,2
*/
path: string;
}
export interface RequirementEntityForm extends BaseEntity {
/**
* ID
*/
id?: string | number;
/**
* ID
*/
configId?: string | number;
/**
*
*/
entityName?: string;
/**
* ID0
*/
parentId?: string | number;
/**
* 1
*/
level?: number;
/**
* 0,1,2
*/
path?: string;
}
export interface RequirementEntityQuery extends PageQuery {
/**
* ID
*/
configId?: string | number;
/**
*
*/
entityName?: string;
/**
* ID0
*/
parentId?: string | number;
/**
* 1
*/
level?: number;
/**
* 0,1,2
*/
path?: string;
/**
*
*/
params?: any;
}

57
src/api/contractReview/RequirementOs/index.ts

@ -0,0 +1,57 @@
import { defHttp } from '@/utils/http/axios';
import { ID, IDS, commonExport } from '@/api/base';
import { RequirementOsVO, RequirementOsForm, RequirementOsQuery } from './model';
/**
*
* @param params
* @returns
*/
export function RequirementOsList(params?: RequirementOsQuery) {
return defHttp.get<RequirementOsVO[]>({ url: '/productManagement/RequirementOs/list', params });
}
/**
*
* @param params
* @returns
*/
export function RequirementOsExport(params?: RequirementOsQuery) {
return commonExport('/productManagement/RequirementOs/export', params ?? {});
}
/**
*
* @param id id
* @returns
*/
export function RequirementOsInfo(id: ID) {
return defHttp.get<RequirementOsVO>({ url: '/productManagement/RequirementOs/' + id });
}
/**
*
* @param data
* @returns
*/
export function RequirementOsAdd(data: RequirementOsForm) {
return defHttp.postWithMsg<void>({ url: '/productManagement/RequirementOs', data });
}
/**
*
* @param data
* @returns
*/
export function RequirementOsUpdate(data: RequirementOsForm) {
return defHttp.putWithMsg<void>({ url: '/productManagement/RequirementOs', data });
}
/**
*
* @param id id
* @returns
*/
export function RequirementOsRemove(id: ID | IDS) {
return defHttp.deleteWithMsg<void>({ url: '/productManagement/RequirementOs/' + id },);
}

85
src/api/contractReview/RequirementOs/model.ts

@ -0,0 +1,85 @@
import { BaseEntity, PageQuery } from '@/api/base';
export interface RequirementOsVO {
/**
* ID
*/
id: string | number;
/**
* ID
*/
configId: string | number;
/**
*
*/
name: string;
/**
*
*/
model: string;
/**
*
*/
versionStr: string;
}
export interface RequirementOsForm extends BaseEntity {
/**
* ID
*/
id?: string | number;
/**
* ID
*/
configId?: string | number;
/**
*
*/
name?: string;
/**
*
*/
model?: string;
/**
*
*/
versionStr?: string;
}
export interface RequirementOsQuery extends PageQuery {
/**
* ID
*/
configId?: string | number;
/**
*
*/
name?: string;
/**
*
*/
model?: string;
/**
*
*/
versionStr?: string;
/**
*
*/
params?: any;
}

57
src/api/contractReview/RequirementProductAttribute/index.ts

@ -0,0 +1,57 @@
import { defHttp } from '@/utils/http/axios';
import { ID, IDS, commonExport } from '@/api/base';
import { RequirementProductAttributeVO, RequirementProductAttributeForm, RequirementProductAttributeQuery } from './model';
/**
*
* @param params
* @returns
*/
export function RequirementProductAttributeList(params?: RequirementProductAttributeQuery) {
return defHttp.get<RequirementProductAttributeVO[]>({ url: '/productManagement/RequirementProductAttribute/list', params });
}
/**
*
* @param params
* @returns
*/
export function RequirementProductAttributeExport(params?: RequirementProductAttributeQuery) {
return commonExport('/productManagement/RequirementProductAttribute/export', params ?? {});
}
/**
*
* @param id id
* @returns
*/
export function RequirementProductAttributeInfo(id: ID) {
return defHttp.get<RequirementProductAttributeVO>({ url: '/productManagement/RequirementProductAttribute/' + id });
}
/**
*
* @param data
* @returns
*/
export function RequirementProductAttributeAdd(data: RequirementProductAttributeForm) {
return defHttp.postWithMsg<void>({ url: '/productManagement/RequirementProductAttribute', data });
}
/**
*
* @param data
* @returns
*/
export function RequirementProductAttributeUpdate(data: RequirementProductAttributeForm) {
return defHttp.putWithMsg<void>({ url: '/productManagement/RequirementProductAttribute', data });
}
/**
*
* @param id id
* @returns
*/
export function RequirementProductAttributeRemove(id: ID | IDS) {
return defHttp.deleteWithMsg<void>({ url: '/productManagement/RequirementProductAttribute/' + id },);
}

85
src/api/contractReview/RequirementProductAttribute/model.ts

@ -0,0 +1,85 @@
import { BaseEntity, PageQuery } from '@/api/base';
export interface RequirementProductAttributeVO {
/**
* ID
*/
id: string | number;
/**
* ID
*/
configId: string | number;
/**
*
*/
attributeName: string;
/**
*
*/
requirement: string;
/**
*
*/
productType: string;
}
export interface RequirementProductAttributeForm extends BaseEntity {
/**
* ID
*/
id?: string | number;
/**
* ID
*/
configId?: string | number;
/**
*
*/
attributeName?: string;
/**
*
*/
requirement?: string;
/**
*
*/
productType?: string;
}
export interface RequirementProductAttributeQuery extends PageQuery {
/**
* ID
*/
configId?: string | number;
/**
*
*/
attributeName?: string;
/**
*
*/
requirement?: string;
/**
*
*/
productType?: string;
/**
*
*/
params?: any;
}

5
src/components/Application/src/AppLogo.vue

@ -4,9 +4,10 @@
-->
<template>
<div class="anticon" :class="getAppLogoClass" @click="goHome">
<img src="../../../assets/images/logo.png" />
<!-- <img src="../../../assets/images/logo.png" /> -->
<div class="ml-2 truncate md:opacity-100" :class="getTitleClass" v-show="showTitle">
{{ title }}
<!-- {{ title }} -->
合同审核
</div>
</div>
</template>

7
src/views/auth/login/Login.vue

@ -16,15 +16,12 @@
<div class="flex h-full">
<div class="hidden min-h-full pl-4 mr-4 xl:flex xl:flex-col xl:w-6/12">
<!-- <AppLogo class="-enter-x" /> -->
<img src="../../../assets/images/logomini.png" class="w-8/12 mt-20 mx-auto" />
<!-- <img src="../../../assets/images/logomini.png" class="w-8/12 mt-20 mx-auto" /> -->
<div class="my-auto">
<img :alt="title" src="../../../assets/svg/login-box-bg.png" class="w-full mx-auto mb-15" />
<div class="mb-10 font-medium text-white -enter-x">
<!-- <span class="inline-block mt-4 text-3xl"> {{ t('sys.login.signInTitle') }}</span> -->
<span class="inline-block text-3xl text-black">AI辅助方案审核系统</span>
<div class="mb-15 font-normal text-red-600 -enter-x text-black">
本平台为互联网非涉密平台严禁处理传输国家秘密
</div>
<span class="inline-block text-3xl text-black">AI辅助方案合同系统</span>
</div>
<!-- <div class="mt-5 font-normal text-white dark:text-gray-500 -enter-x">
{{ t('sys.login.signInDesc') }}

4
src/views/chat/index.vue

@ -280,7 +280,7 @@
{
_id: '1',
indexId: 1,
content: '欢迎使用AI辅助方案审核系统',
content: '欢迎使用AI辅助方案合同系统',
senderId: users['ai']._id,
username: users['ai'].username,
avatar: users['ai'].avatar,
@ -318,7 +318,7 @@
const bMsg = {
_id: '2',
indexId: 1,
content: '欢迎使用AI辅助方案审核系统',
content: '欢迎使用AI辅助方案合同系统',
senderId: users['ai']._id,
username: users['ai'].username,
avatar: users['ai'].avatar,

72
src/views/contractReview/ContractualAuditConfig/ContractualAuditConfig.data.ts

@ -0,0 +1,72 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
import { getDictOptions } from '@/utils/dict';
import { useRender } from '@/hooks/component/useRender';
const { renderDict } = useRender();
export const formSchemas: FormSchema[] = [
{
label: '审核类型',
field: 'requirementType',
component: 'Input',
},
{
label: '审核要素',
field: 'auditElement',
component: 'Select',
componentProps: {
options: getDictOptions('contract_audit_rule_name'),
},
},
];
export const columns: BasicColumn[] = [
{
title: 'id',
dataIndex: 'id',
width: 50,
ifShow: false,
},
{
title: '审核类型',
dataIndex: 'requirementType',
},
{
title: '审核要素',
dataIndex: 'auditElement',
customRender: ({ value }) => renderDict(value, 'contract_audit_rule_name'),
},
{
title: '规则描述',
dataIndex: 'requirementDescription',
},
];
export const modalSchemas: FormSchema[] = [
{
label:"id",
field:"id",
component:"Input",
show:false,
},
{
label: '审核类型',
field:'requirementType',
component: 'Input',
required: true,
},
{
label: '审核要素',
field: 'auditElement',
required: true,
component: 'Select',
componentProps: {
options: getDictOptions('contract_audit_rule_name'),
},
},
{
label: '规则配置',
field: 'config',
slot: 'configSlot',
},
];

177
src/views/contractReview/ContractualAuditConfig/ContractualAuditConfigModal.vue

@ -0,0 +1,177 @@
<template>
<BasicModal
v-bind="$attrs"
:title="title"
:width="1000"
:scrollTop="true"
@register="registerInnerModal"
@ok="handleSubmit"
@cancel="resetForm"
>
<BasicForm @register="registerForm">
<template #configSlot="{model}">
<component
:is="currentComponent(model.auditElement)"
:ref="setComponentRef"
/>
</template>
</BasicForm>
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { computed, ref, unref, shallowRef, nextTick } from 'vue';
import { ContractualAuditConfigInfo, ContractualAuditConfigAdd, ContractualAuditConfigUpdate } from '@/api/contractReview/ContractualAuditConfig';
import { modalSchemas } from './ContractualAuditConfig.data';
import cpuOsDatebaseComponent from './components/cpuOsDatebaseComponent.vue';
import entityTreeComponent from './components/entityTreeComponent.vue';
import productAttributeComponent from './components/productAttributeComponent.vue';
import contractualNormalComponent from './components/contractualNormalComponent.vue';
//
interface ComponentInstance {
validate: () => boolean;
getData: () => any[];
setData?: (data: any[]) => void; // setData
}
const emit = defineEmits(['register', 'reload']);
const isUpdate = ref<boolean>(false);
const title = computed<string>(() => {
return isUpdate.value ? '编辑合同审核配置' : '新增合同审核配置';
});
//
const activeComponentRef = ref<ComponentInstance | null>(null);
//
const currentComponent = (auditElement: string) => {
if (auditElement === 'os' || auditElement === 'cpu' || auditElement === 'database') {
return cpuOsDatebaseComponent;
} else if (auditElement === 'entity') {
return entityTreeComponent;
} else if (auditElement === 'productAttribute') {
return productAttributeComponent;
} else if (auditElement === 'contractualNormal') {
return contractualNormalComponent;
}
return null;
};
//
const setComponentRef = (el: any) => {
if (el) {
activeComponentRef.value = el;
}
};
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable; update: boolean }) => {
modalLoading(true);
const { record, update } = data;
isUpdate.value = update;
if (update && record) {
const ret = await ContractualAuditConfigInfo(record.id);
await setFieldsValue(ret);
//
await nextTick();
// auditElement
if (activeComponentRef.value?.setData) {
// 使
const retData = ret as any;
if (retData.auditElement === 'os' || retData.auditElement === 'cpu' || retData.auditElement === 'database') {
// CPU/OS/
if (retData.requirementCpuosdatabaseList) {
activeComponentRef.value.setData(retData.requirementCpuosdatabaseList);
}
} else if (retData.auditElement === 'entity') {
//
if (retData.requirementEntityList) {
activeComponentRef.value.setData(retData.requirementEntityList);
}
} else if (retData.auditElement === 'productAttribute') {
//
if (retData.requirementProductAttributeList) {
activeComponentRef.value.setData(retData.requirementProductAttributeList);
}
} else if (retData.auditElement === 'contractualNormal') {
//
if (retData.requirementContractualNormalList) {
activeComponentRef.value.setData(retData.requirementContractualNormalList);
}
}
}
}
modalLoading(false);
},
);
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({
labelWidth: 100,
showActionButtonGroup: false,
baseColProps: { span: 24 },
schemas: modalSchemas,
});
async function handleSubmit() {
try {
modalLoading(true);
//
if (activeComponentRef.value) {
const isValid = activeComponentRef.value.validate();
if (!isValid) {
modalLoading(false);
return;
}
}
const data = await validate();
if (data.requirementType) {
data.requirementType = data.requirementType.trim();
}
// auditElement
if (activeComponentRef.value?.getData) {
const componentData = activeComponentRef.value.getData()
// .map(item => {
// const { id, ...rest } = item;
// return rest;
// });
// 使
const formData = data as any;
if (formData.auditElement === 'os' || formData.auditElement === 'cpu' || formData.auditElement === 'database') {
formData.requirementCpuosdatabaseList = componentData;
} else if (formData.auditElement === 'entity') {
formData.requirementEntityList = componentData;
} else if (formData.auditElement === 'productAttribute') {
formData.requirementProductAttributeList = componentData;
} else if (formData.auditElement === 'contractualNormal') {
formData.requirementContractualNormalList = componentData;
}
}
if (unref(isUpdate)) {
await ContractualAuditConfigUpdate(data);
} else {
await ContractualAuditConfigAdd(data);
}
emit('reload');
closeModal();
await resetForm();
} catch (e) {
} finally {
modalLoading(false);
}
}
</script>
<style scoped></style>

148
src/views/contractReview/ContractualAuditConfig/components/contractualNormalComponent.vue

@ -0,0 +1,148 @@
<template>
<div>
<Table
:columns="columns"
:dataSource="dataSource"
:bordered="true"
:pagination="false"
:rowClassName="() => 'compact-row'"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'checkItem'">
<Form.Item
:validateStatus="!record.checkItem ? 'error' : ''"
:help="!record.checkItem ? '产品类型为必填项' : ''"
class="compact-form-item"
>
<Input v-model:value="record.checkItem" placeholder="请输入产品类型" />
</Form.Item>
</template>
<template v-if="column.dataIndex === 'requirementDesc'">
<Form.Item
:validateStatus="!record.requirementDesc ? 'error' : ''"
:help="!record.requirementDesc ? '要求为必填项' : ''"
class="compact-form-item"
>
<Textarea
v-model:value="record.requirementDesc"
placeholder="请输入要求"
:rows="2"
:auto-size="{ minRows: 2, maxRows: 6 }"
/>
</Form.Item>
</template>
<template v-if="column.key === 'operation'">
<a-button type="link" @click="handleDelete(index)">删除</a-button>
</template>
</template>
</Table>
<Button type="primary" @click="handleAdd" style="margin-top: 16px">新增</Button>
</div>
</template>
<script setup lang="ts">
import { ref, defineExpose } from 'vue'
import { Table, Input, Button, Form, message,Textarea } from 'ant-design-vue'
interface TableItem {
key: string
checkItem: string
requirementDesc: string
requirement: string
}
const columns = [
{
title: '检查项(必填)',
dataIndex: 'checkItem',
key: 'checkItem',
width: '33%'
},
{
title: '要求描述(必填)',
dataIndex: 'requirementDesc',
key: 'requirementDesc',
width: '33%'
},
{
title: '操作',
key: 'operation',
width: 80,
},
]
const dataSource = ref<TableItem[]>([])
const validate = () => {
const emptyItems = dataSource.value.filter(item => !item.checkItem || !item.requirementDesc)
if (emptyItems.length > 0) {
message.error('请填写所有必填项')
return false
}
return true
}
const handleAdd = () => {
for (const item of dataSource.value) {
if (!item.checkItem || !item.requirementDesc) {
message.error('请先填写已有行的必填项')
return
}
}
dataSource.value.push({
key: Date.now().toString(),
checkItem: '',
requirementDesc: '',
requirement: ''
})
}
const getData = () => {
return dataSource.value
}
const setData = (data: TableItem[]) => {
if (!data || data.length === 0) {
dataSource.value = []
return
}
// key
const formattedData = data.map(item => ({
...item,
key: item.key || Date.now().toString()
}))
dataSource.value = formattedData
}
const handleDelete = (index: number) => {
if (dataSource.value.length <= 1) {
message.warning('至少保留一行数据')
return
}
dataSource.value.splice(index, 1)
}
defineExpose({
getData,
validate,
setData
})
</script>
<style scoped>
.compact-row :deep(.ant-form-item) {
margin-bottom: 0;
}
.compact-form-item {
margin-bottom: 0 !important;
}
:deep(.ant-table-tbody > tr > td) {
padding: 8px;
}
</style>

151
src/views/contractReview/ContractualAuditConfig/components/cpuOsDatebaseComponent.vue

@ -0,0 +1,151 @@
<template>
<div>
<Table
:columns="columns"
:dataSource="dataSource"
:bordered="true"
:pagination="false"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'name'">
<Form.Item
:validateStatus="!record.name ? 'error' : ''"
:help="!record.name ? '产品名称为必填项' : ''"
class="compact-form-item"
>
<Input v-model:value="record.name" placeholder="请输入名称" />
</Form.Item>
</template>
<template v-if="column.dataIndex === 'model'">
<Input v-model:value="record.model" placeholder="请输入型号" />
</template>
<template v-if="column.dataIndex === 'versionStr'">
<Input v-model:value="record.versionStr" placeholder="请输入版本" />
</template>
<template v-if="column.key === 'operation'">
<Button type="link" @click="handleDelete(index)">删除</Button>
</template>
</template>
</Table>
<Button type="primary" @click="handleAdd" style="margin-top: 16px">新增</Button>
</div>
</template>
<script setup lang="ts">
import { ref, defineExpose } from 'vue'
import { Table, Input, Button, Form } from 'ant-design-vue'
import { message } from 'ant-design-vue'
interface TableItem {
key: string
name: string
model: string
versionStr: string
}
const columns = [
{
title: '产品名称(必填)',
dataIndex: 'name',
key: 'name',
width: '33%',
rules: [{ required: true, message: '名称为必填项' }]
},
{
title: '型号',
dataIndex: 'model',
key: 'model',
width: '33%'
},
{
title: '版本',
dataIndex: 'versionStr',
key: 'versionStr',
width: '33%'
},
{
title: '操作',
key: 'operation',
width: 80,
},
]
const dataSource = ref<TableItem[]>([])
//
const validate = () => {
const emptyNameItems = dataSource.value.filter(item => !item.name);
if (emptyNameItems.length > 0) {
message.error('请填写所有行的名称');
return false;
}
return true;
}
const handleAdd = () => {
//
for (const item of dataSource.value) {
if (!item.name) {
message.error('请先填写已有行的名称')
return
}
}
dataSource.value.push({
key: Date.now().toString(),
name: '',
model: '',
versionStr: ''
})
}
const getData = () => {
return dataSource.value
}
//
const setData = (data: TableItem[]) => {
if (!data || data.length === 0) {
dataSource.value = []
return
}
// key
const formattedData = data.map(item => ({
...item,
key: item.key || Date.now().toString()
}))
dataSource.value = formattedData
}
//
const handleDelete = (index: number) => {
if (dataSource.value.length <= 1) {
message.warning('至少保留一行数据');
return;
}
dataSource.value.splice(index, 1);
}
defineExpose({
getData,
validate,
setData
})
</script>
<style scoped>
.compact-row :deep(.ant-form-item) {
margin-bottom: 0;
}
.compact-form-item {
margin-bottom: 0 !important;
}
:deep(.ant-table-tbody > tr > td) {
padding: 8px;
}
</style>

459
src/views/contractReview/ContractualAuditConfig/components/entityTreeComponent.vue

@ -0,0 +1,459 @@
<template>
<div>
<!-- 树形组件支持选择和展开节点 -->
<Tree
v-model:selectedKeys="selectedKeys"
v-model:expandedKeys="expandedKeys"
:treeData="treeData"
:replaceFields="{ title: 'entityName', key: 'id' }" <!-- 映射字段名称 -->
:autoExpandParent="autoExpandParent"
>
<!-- 自定义节点标题渲染 -->
<template #title="{ entityName, id }">
<div class="tree-node">
<!-- 编辑状态显示输入框 -->
<div v-if="editingId == id" class="inline-edit">
<Input
v-model:value="editingName"
placeholder="请输入主体名称"
@pressEnter="saveEdit"
@blur="saveEdit"
ref="editInputRef"
/>
</div>
<!-- 非编辑状态显示节点名称支持双击编辑 -->
<span v-else @dblclick.stop="handleEdit(id, entityName)">{{ entityName }}</span>
<!-- 非根节点的操作按钮 -->
<div class="operation-btns" v-if="id !== rootId">
<Button type="link" @click.stop="handleAdd(id)">新增</Button>
<Button type="link" @click.stop="handleEdit(id, entityName)">编辑</Button>
<Button type="link" danger @click.stop="handleDelete(id)">删除</Button>
</div>
<!-- 根节点只显示新增按钮 -->
<div class="operation-btns" v-else>
<Button type="link" @click.stop="handleAdd(id)">新增</Button>
</div>
</div>
</template>
</Tree>
</div>
</template>
<script setup lang="ts">
import { ref, defineExpose, nextTick, onMounted, watch } from 'vue';
import { Tree, Button, Input, message } from 'ant-design-vue';
import type { TreeDataItem, DataNode } from 'ant-design-vue/es/tree';
//
interface EntityItem {
id: string; //
entityName: string; //
parentId: string; // ID
level: number; //
path: string; //
children?: EntityItem[]; //
key: string; // Treekey
title?: string; // Treetitle
}
// ID1
const rootId = '1';
const selectedKeys = ref<string[]>([]); //
const expandedKeys = ref<string[]>([rootId]); //
const autoExpandParent = ref<boolean>(true); //
const treeData = ref<EntityItem[]>([]); //
const editingId = ref<string>(''); // ID
const editingName = ref<string>(''); //
const editInputRef = ref(null); //
// ID
const nextId = ref<number>(2); // 21
// expandedKeys/
watch(expandedKeys, () => {
autoExpandParent.value = false;
});
//
onMounted(() => {
initRootNode();
});
/**
* 初始化根节点
* 如果树为空创建一个"所有单位"的根节点
*/
const initRootNode = () => {
//
if (treeData.value.length == 0) {
const rootNode: EntityItem = {
id: rootId,
entityName: '所有单位',
parentId: '0', // ID0
level: 1,
path: '0',
key: rootId, // key
children: []
};
treeData.value.push(rootNode);
}
};
/**
* 生成新的节点ID
* 返回递增的数字字符串
*/
const generateId = (): string => {
const id = nextId.value.toString();
nextId.value++;
return id;
};
/**
* 将树形数据转换为扁平列表
* 用于提交数据到服务器
*/
const convertToList = (tree: EntityItem[]): EntityItem[] => {
const list: EntityItem[] = [];
const traverse = (nodes: EntityItem[]) => {
nodes.forEach(node => {
const { children, ...rest } = node;
list.push(rest); // children
if (children) {
traverse(children); //
}
});
};
traverse(tree);
console.log(list);
return list;
};
/**
* 将扁平列表转换为树形结构
* 用于从服务器接收数据并显示
*/
const convertToTree = (list: EntityItem[]): EntityItem[] => {
// IDnextId
const maxId = Math.max(...list.map(item => parseInt(item.id)), 1);
nextId.value = maxId + 1;
// 便
const nodeMap = new Map<string, EntityItem>();
list.forEach(item => {
// key
nodeMap.set(item.id, {
...item,
key: item.id, // key
children: []
});
});
//
const tree: EntityItem[] = [];
// children
nodeMap.forEach(node => {
node.children = [];
});
//
nodeMap.forEach(node => {
if (node.parentId == '0' || !nodeMap.has(node.parentId)) {
//
tree.push(node);
} else {
// children
const parentNode = nodeMap.get(node.parentId);
if (parentNode) {
parentNode.children!.push(node);
}
}
});
return tree;
};
/**
* 获取数据列表
* 将当前树形结构转换为扁平列表返回
*/
const getData = () => {
return convertToList(treeData.value);
};
/**
* 设置数据
* 接收外部数据并转换为树形结构显示
*/
const setData = (data: EntityItem[]) => {
if (!data || data.length == 0) {
initRootNode();
return;
}
// key
const processedData = data.map(item => ({
...item,
key: item.id // key
}));
//
const hasRoot = processedData.some(item => item.id == rootId);
if (!hasRoot) {
//
processedData.push({
id: rootId,
entityName: '所有单位',
parentId: '0',
level: 1,
path: '0',
key: rootId // key
});
}
//
const tree = convertToTree(processedData);
treeData.value = tree;
//
expandedKeys.value = [rootId];
autoExpandParent.value = true;
};
/**
* 处理添加节点
* @param parentId 父节点ID
*/
const handleAdd = (parentId: string) => {
//
const parentNode = findNodeById(treeData.value, parentId);
if (!parentNode) return;
//
if (parentId !== rootId && (!parentNode.entityName || parentNode.entityName.trim() == '')) {
message.error('请先完成当前节点的编辑再添加子节点');
return;
}
//
const allNodes = convertToList(treeData.value);
const emptyNameNode = allNodes.find(node =>
node.id !== rootId && (!node.entityName || node.entityName.trim() == '')
);
if (emptyNameNode) {
message.error('存在未完成编辑的节点,请先完成编辑');
return;
}
//
const newId = generateId();
const newNode: EntityItem = {
id: newId,
entityName: '', //
parentId: parentId,
level: parentNode.level + 1,
path: parentNode.path + ',' + parentId,
key: newId // key
};
// children
if (!parentNode.children) parentNode.children = [];
parentNode.children.push(newNode);
//
if (!expandedKeys.value.includes(parentId)) {
expandedKeys.value = [...expandedKeys.value, parentId];
autoExpandParent.value = true;
}
// 使setTimeout
setTimeout(() => {
handleEdit(newNode.id, newNode.entityName);
}, 100);
};
/**
* 处理编辑节点
* @param id 节点ID
* @param name 节点名称
*/
const handleEdit = (id: string, name: string) => {
//
if (id == rootId) return;
//
editingId.value = id;
editingName.value = name;
//
const node = findNodeById(treeData.value, id);
if (node) {
const pathArray = node.path.split(',');
expandedKeys.value = Array.from(new Set([...expandedKeys.value, ...pathArray]));
autoExpandParent.value = true;
}
//
nextTick(() => {
if (editInputRef.value) {
(editInputRef.value as any).focus();
}
});
};
/**
* 保存编辑内容
*/
const saveEdit = () => {
//
if (editingId.value == '') {
return;
}
//
const trimmedName = editingName.value.trim();
if (!trimmedName) {
message.error('主体名称不能为空');
return;
}
//
updateNode(treeData.value, editingId.value, { entityName: trimmedName });
//
editingId.value = '';
};
/**
* 处理删除节点
* @param id 节点ID
*/
const handleDelete = async (id: string) => {
//
if (id == rootId) return;
//
const node = findNodeById(treeData.value, id);
if (node && node.children && node.children.length > 0) {
message.error('该节点包含子节点,不能删除');
return;
}
//
removeNode(treeData.value, id);
};
/**
* 根据ID查找节点
* @param nodes 节点列表
* @param id 节点ID
* @returns 找到的节点或null
*/
const findNodeById = (nodes: EntityItem[], id: string): EntityItem | null => {
for (const node of nodes) {
if (node.id == id) return node;
if (node.children) {
const found = findNodeById(node.children, id);
if (found) return found;
}
}
return null;
};
/**
* 更新节点数据
* @param nodes 节点列表
* @param id 要更新的节点ID
* @param updatedData 更新的数据
*/
const updateNode = (nodes: EntityItem[], id: string, updatedData: Partial<EntityItem>) => {
for (const node of nodes) {
if (node.id == id) {
Object.assign(node, updatedData);
return;
}
if (node.children) {
updateNode(node.children, id, updatedData);
}
}
};
/**
* 删除节点
* @param nodes 节点列表
* @param id 要删除的节点ID
*/
const removeNode = (nodes: EntityItem[], id: string) => {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].id == id) {
nodes.splice(i, 1);
return;
}
if (nodes[i].children) {
removeNode(nodes[i].children!, id);
}
}
};
/**
* 验证数据有效性
* 检查是否有节点以及是否有空名称的节点
*/
const validate = () => {
//
const rootNode = findNodeById(treeData.value, rootId);
if (!rootNode || !rootNode.children || rootNode.children.length == 0) {
message.error('请至少添加一个主体');
return false;
}
//
const allNodes = convertToList(treeData.value);
const emptyNameNode = allNodes.find(node =>
node.id !== rootId && (!node.entityName || node.entityName.trim() == '')
);
if (emptyNameNode) {
message.error('存在未完成编辑的节点,请完成所有节点的编辑');
return false;
}
return true;
};
//
defineExpose({
getData, //
validate, //
setData //
});
</script>
<style scoped>
/* 节点样式 */
.tree-node {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 8px;
}
/* 编辑输入框样式 */
.inline-edit {
min-width: 150px;
margin-right: 8px;
}
/* 操作按钮样式,默认隐藏 */
.operation-btns {
display: none;
}
/* 鼠标悬停时显示操作按钮 */
.tree-node:hover .operation-btns {
display: block;
}
</style>

163
src/views/contractReview/ContractualAuditConfig/components/productAttributeComponent.vue

@ -0,0 +1,163 @@
<template>
<div>
<Table
:columns="columns"
:dataSource="dataSource"
:bordered="true"
:pagination="false"
:rowClassName="() => 'compact-row'"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'productType'">
<Form.Item
:validateStatus="!record.productType ? 'error' : ''"
:help="!record.productType ? '产品类型为必填项' : ''"
class="compact-form-item"
>
<Input v-model:value="record.productType" placeholder="请输入产品类型" />
</Form.Item>
</template>
<template v-if="column.dataIndex === 'attributeName'">
<Form.Item
:validateStatus="!record.attributeName ? 'error' : ''"
:help="!record.attributeName ? '属性名称为必填项' : ''"
class="compact-form-item"
>
<Input v-model:value="record.attributeName" placeholder="请输入属性名称" />
</Form.Item>
</template>
<template v-if="column.dataIndex === 'requirement'">
<Form.Item
:validateStatus="!record.requirement ? 'error' : ''"
:help="!record.requirement ? '要求为必填项' : ''"
class="compact-form-item"
>
<Textarea
v-model:value="record.requirement"
placeholder="请输入要求"
:rows="2"
:auto-size="{ minRows: 2, maxRows: 6 }"
/>
</Form.Item>
</template>
<template v-if="column.key === 'operation'">
<Button type="link" @click="handleDelete(index)">删除</Button>
</template>
</template>
</Table>
<Button type="primary" @click="handleAdd" style="margin-top: 16px">新增</Button>
</div>
</template>
<script setup lang="ts">
import { ref, defineExpose } from 'vue'
import { Table, Input, Button, Form, message,Textarea } from 'ant-design-vue'
interface TableItem {
key: string
productType: string
attributeName: string
requirement: string
}
const columns = [
{
title: '产品类型(必填)',
dataIndex: 'productType',
key: 'productType',
width: '33%'
},
{
title: '属性名称(必填)',
dataIndex: 'attributeName',
key: 'attributeName',
width: '33%'
},
{
title: '要求(必填)',
dataIndex: 'requirement',
key: 'requirement',
width: '33%'
},
{
title: '操作',
key: 'operation',
width: 80,
},
]
const dataSource = ref<TableItem[]>([])
const validate = () => {
const emptyItems = dataSource.value.filter(item => !item.productType || !item.attributeName || !item.requirement)
if (emptyItems.length > 0) {
message.error('请填写所有必填项')
return false
}
return true
}
const handleAdd = () => {
for (const item of dataSource.value) {
if (!item.productType || !item.attributeName || !item.requirement) {
message.error('请先填写已有行的必填项')
return
}
}
dataSource.value.push({
key: Date.now().toString(),
productType: '',
attributeName: '',
requirement: ''
})
}
const handleDelete = (index: number) => {
if (dataSource.value.length <= 1) {
message.warning('至少保留一行数据');
return;
}
dataSource.value.splice(index, 1);
}
const getData = () => {
return dataSource.value
}
const setData = (data: TableItem[]) => {
if (!data || data.length === 0) {
dataSource.value = []
return
}
// key
const formattedData = data.map(item => ({
...item,
key: item.key || Date.now().toString()
}))
dataSource.value = formattedData
}
defineExpose({
getData,
validate,
setData
})
</script>
<style scoped>
.compact-row :deep(.ant-form-item) {
margin-bottom: 0;
}
.compact-form-item {
margin-bottom: 0 !important;
}
:deep(.ant-table-tbody > tr > td) {
padding: 8px;
}
</style>

115
src/views/contractReview/ContractualAuditConfig/index.vue

@ -0,0 +1,115 @@
<template>
<PageWrapper dense>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button
@click="downloadExcel(ContractualAuditConfigExport, '合同审核配置数据', getForm().getFieldsValue())"
v-auth="'productManagement:ContractualAuditConfig:export'"
>导出</a-button
>
<a-button
type="primary"
danger
@click="multipleRemove(ContractualAuditConfigRemove)"
:disabled="!selected"
v-auth="'productManagement:ContractualAuditConfig:remove'"
>删除</a-button
>
<a-button
type="primary"
@click="handleAdd"
v-auth="'productManagement:ContractualAuditConfig:add'"
>新增</a-button
>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
stopButtonPropagation
:actions="[
{
label: '修改',
icon: IconEnum.EDIT,
type: 'primary',
ghost: true,
auth: 'productManagement:ContractualAuditConfig:edit',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
auth: 'productManagement:ContractualAuditConfig:remove',
popConfirm: {
placement: 'left',
title: '是否删除合同审核配置[' + record.id + ']?',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<ContractualAuditConfigModal @register="registerModal" @reload="reload" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { ContractualAuditConfigList, ContractualAuditConfigExport, ContractualAuditConfigRemove } from '@/api/contractReview/ContractualAuditConfig';
import { downloadExcel } from '@/utils/file/download';
import { useModal } from '@/components/Modal';
import ContractualAuditConfigModal from './ContractualAuditConfigModal.vue';
import { formSchemas, columns } from './ContractualAuditConfig.data';
import { IconEnum } from '@/enums/appEnum';
defineOptions({ name: 'ContractualAuditConfig' });
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({
rowSelection: {
type: 'checkbox',
},
title: '合同审核配置列表',
api: ContractualAuditConfigList,
showIndexColumn: false,
rowKey: 'id',
useSearchForm: true,
formConfig: {
schemas: formSchemas,
baseColProps: {
xs: 24,
sm: 24,
md: 24,
lg: 6,
},
},
columns: columns,
actionColumn: {
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
},
});
const [registerModal, { openModal }] = useModal();
function handleEdit(record: Recordable) {
openModal(true, { record, update: true });
}
function handleAdd() {
openModal(true, { update: false });
}
async function handleDelete(record: Recordable) {
await ContractualAuditConfigRemove([record.id]);
await reload();
}
</script>
<style scoped></style>

156
src/views/contractReview/ContractualProductInfo/ContractualProductInfo.data.ts

@ -0,0 +1,156 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
export const formSchemas: FormSchema[] = [
{
label: '文件名称',
field: 'fileName',
component: 'Input',
},
{
label: '品牌',
field: 'brand',
component: 'Input',
},
{
label: '型号(版本号)',
field: 'versionStr',
component: 'Input',
},
{
label: '单价',
field: 'unitPrice',
component: 'Input',
},
{
label: '价格单位',
field: 'priceUnit',
component: 'Input',
},
{
label: '数量',
field: 'quantity',
component: 'Input',
},
{
label: '总价',
field: 'totalPrice',
component: 'Input',
},
{
label: '类型',
field: 'type',
component: 'Input',
},
];
export const columns: BasicColumn[] = [
{
title: '主键ID',
dataIndex: 'id',
ifShow: false,
},
{
title: '任务ID',
dataIndex: 'taskId',
ifShow: false,
},
{
title: '文件名称',
dataIndex: 'fileName',
},
{
title: '品牌',
dataIndex: 'brand',
},
{
title: '类型',
dataIndex: 'type',
},
{
title: '型号(版本号)',
dataIndex: 'versionStr',
},
{
title: '单价',
dataIndex: 'unitPrice',
},
{
title: '价格单位',
dataIndex: 'priceUnit',
},
{
title: '数量',
dataIndex: 'quantity',
},
{
title: '总价',
dataIndex: 'totalPrice',
},
];
export const modalSchemas: FormSchema[] = [
{
label: '主键ID',
field: 'id',
required: false,
component: 'Input',
show: false,
},
{
label: '任务ID',
field: 'taskId',
required: false,
component: 'Input',
show: false,
},
{
label: '文件名称',
field: 'fileName',
required: false,
component: 'Input',
},
{
label: '品牌',
field: 'brand',
required: false,
component: 'Input',
},
{
label: '型号(版本号)',
field: 'versionStr',
required: false,
component: 'Input',
},
{
label: '类型',
field: 'type',
required: false,
component: 'Input',
},
{
label: '单价',
field: 'unitPrice',
required: false,
component: 'Input',
},
{
label: '价格单位',
field: 'priceUnit',
required: false,
component: 'Input',
},
{
label: '数量',
field: 'quantity',
required: false,
component: 'Input',
},
{
label: '总价',
field: 'totalPrice',
required: false,
component: 'Input',
},
];

68
src/views/contractReview/ContractualProductInfo/ContractualProductInfoModal.vue

@ -0,0 +1,68 @@
<template>
<BasicModal
v-bind="$attrs"
:title="title"
@register="registerInnerModal"
@ok="handleSubmit"
@cancel="resetForm"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { computed, ref, unref } from 'vue';
import { ContractualProductInfoInfo, ContractualProductInfoAdd, ContractualProductInfoUpdate } from '@/api/contractReview/ContractualProductInfo';
import { modalSchemas } from './ContractualProductInfo.data';
defineOptions({ name: 'ContractualProductInfoModal' });
const emit = defineEmits(['register', 'reload']);
const isUpdate = ref<boolean>(false);
const title = computed<string>(() => {
return isUpdate.value ? '编辑合同产品信息' : '新增合同产品信息';
});
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable; update: boolean }) => {
modalLoading(true);
const { record, update } = data;
isUpdate.value = update;
if (update && record) {
const ret = await ContractualProductInfoInfo(record.id);
await setFieldsValue(ret);
}
modalLoading(false);
},
);
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({
labelWidth: 100,
showActionButtonGroup: false,
baseColProps: { span: 24 },
schemas: modalSchemas,
});
async function handleSubmit() {
try {
modalLoading(true);
const data = await validate();
if (unref(isUpdate)) {
await ContractualProductInfoUpdate(data);
} else {
await ContractualProductInfoAdd(data);
}
emit('reload');
closeModal();
await resetForm();
} catch (e) {
} finally {
modalLoading(false);
}
}
</script>
<style scoped></style>

115
src/views/contractReview/ContractualProductInfo/index.vue

@ -0,0 +1,115 @@
<template>
<PageWrapper dense>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button
@click="downloadExcel(ContractualProductInfoExport, '合同产品信息数据', getForm().getFieldsValue())"
v-auth="'productManagement:ContractualProductInfo:export'"
>导出</a-button
>
<a-button
type="primary"
danger
@click="multipleRemove(ContractualProductInfoRemove)"
:disabled="!selected"
v-auth="'productManagement:ContractualProductInfo:remove'"
>删除</a-button
>
<a-button
type="primary"
@click="handleAdd"
v-auth="'productManagement:ContractualProductInfo:add'"
>新增</a-button
>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
stopButtonPropagation
:actions="[
{
label: '修改',
icon: IconEnum.EDIT,
type: 'primary',
ghost: true,
auth: 'productManagement:ContractualProductInfo:edit',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
auth: 'productManagement:ContractualProductInfo:remove',
popConfirm: {
placement: 'left',
title: '是否删除合同产品信息[' + record.id + ']?',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<ContractualProductInfoModal @register="registerModal" @reload="reload" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { ContractualProductInfoList, ContractualProductInfoExport, ContractualProductInfoRemove } from '@/api/contractReview/ContractualProductInfo';
import { downloadExcel } from '@/utils/file/download';
import { useModal } from '@/components/Modal';
import ContractualProductInfoModal from './ContractualProductInfoModal.vue';
import { formSchemas, columns } from './ContractualProductInfo.data';
import { IconEnum } from '@/enums/appEnum';
defineOptions({ name: 'ContractualProductInfo' });
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({
rowSelection: {
type: 'checkbox',
},
title: '合同产品信息列表',
api: ContractualProductInfoList,
showIndexColumn: false,
rowKey: 'id',
useSearchForm: true,
formConfig: {
schemas: formSchemas,
baseColProps: {
xs: 24,
sm: 24,
md: 24,
lg: 6,
},
},
columns: columns,
actionColumn: {
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
},
});
const [registerModal, { openModal }] = useModal();
function handleEdit(record: Recordable) {
openModal(true, { record, update: true });
}
function handleAdd() {
openModal(true, { update: false });
}
async function handleDelete(record: Recordable) {
await ContractualProductInfoRemove([record.id]);
await reload();
}
</script>
<style scoped></style>

458
src/views/contractReview/ContractualTasks/ContractualShowModa11111l.vue

@ -0,0 +1,458 @@
<template>
<BasicModal
v-bind="$attrs"
title="title"
:canFullscreen="true"
:defaultFullscreen="true"
@ok="submit"
@register="registerInnerModal"
>
<div class="modal-content">
<div class="grid grid-cols-6 gap-4 h-full">
<div class="col-span-2 pdf-container">
<div class="pdf-container">
<vue-office-docx :src="pdfUrl" />
</div>
</div>
<div class="col-span-2 contract-text-container">
<div class="contract-text-header">
<h3>合同文本内容</h3>
</div>
<div class="contract-text-content">
<div class="contract-text-scroll">
<pre>{{ contractText }}</pre>
</div>
</div>
</div>
<div class="col-span-2 review-container">
<div class="review-content">
<showResultCard v-model:cardList="cardList" />
</div>
</div>
</div>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, onMounted, reactive, h, nextTick } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import VueOfficeDocx from '@vue-office/pdf';
import { Tabs, TabPane, Card, Modal } from 'ant-design-vue';
import { useRouter } from 'vue-router';
import CanvasEditor from '@/views/CanvasEditor/index.vue';
import { getContractualResultById,getContractulContent } from '@/api/contractReview/JyjcontractualTaskBatch';
import { modifyContractReview } from '@/api/documentReview/DocumentTaskResults';
import { getPdfFile } from '@/api/contractReview/JyjcontractualTaskBatch';
import showResultCard from './showResultCard.vue';
import { cloneDeep } from 'lodash-es';
const emit = defineEmits(['register', 'reload']);
const pdfUrl = ref<string>('');
const currentId = ref<any>(0);
const pdfViewerkey = ref(0);
const scale = ref(1.5); //
//
const cardList = ref<any>([]);
const selectedCard = ref(''); //
const handleCardClick = (title) => {
selectedCard.value = title;
console.log('点击的卡片标题:', title);
};
// 便
const initialCardList = ref<any[]>([]);
//
const contractText = ref<string>('');
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable }) => {
modalLoading(true);
cardList.value = [];
const { record } = data;
const res = await getContractualResultById(record.id);
//
try {
const contentRes = await getContractulContent(record.id);
contractText.value = contentRes || '无法获取合同文本内容';
} catch (error) {
console.error('获取合同文本内容失败:', error);
contractText.value = '获取合同文本内容失败';
}
pdfViewerkey.value += 1;
currentId.value = record.id;
for (const item of res.results) {
for (const content of item.contentList) {
cardList.value.push({
title: content.problemTitle,
text: content.text,
content: content.problemDesc,
isPosition: content.isPosition,
accord: item.accord,
});
}
}
const response = await getPdfFile(record.id);
if (response?.data) {
console.log('PDF加载成功2:', pdfUrl.value);
const blob = new Blob([response.data], { type: 'application/pdf' });
pdfUrl.value = URL.createObjectURL(blob);
console.log('PDF加载成功3:', pdfUrl.value);
// PDF
nextTick(() => {
setTimeout(fitToWidth, 500);
});
}
//
initialCardList.value = cloneDeep(cardList.value);
modalLoading(false);
},
//
);
//
let parentContent = reactive<any>(undefined);
//
const content = ref<any>(undefined);
//
const view = ref<string | undefined>(undefined);
//
onMounted(() => {
console.log('模拟父组件向后端请求数据, 传递给子组件');
getEditorContent();
view.value = 'parent';
});
//
const fitToWidth = () => {
const pdfContainer = document.querySelector('.pdf-wrapper');
if (pdfContainer) {
const containerWidth = pdfContainer.clientWidth;
const pdfCanvas = document.querySelector('.vue3-pdf canvas') as HTMLCanvasElement;
if (pdfCanvas) {
const originalWidth = pdfCanvas.width / scale.value;
scale.value = +((containerWidth / originalWidth) * 0.95).toFixed(1); // 95%
}
}
};
//
const getEditorContent = () => {
parentContent = {
header: [
{
value: '',
size: 12,
bold: false,
color: 'rgb(33, 53, 71)',
italic: false,
},
],
main: [
{
value:
'父类传递的数据 通过后端获取!\n甲方:企查查科技股份有限公司\n法定代表人:陈德强\n住所:苏州工业园区科创东区东石泾港路2号润港产业园6号楼10层1001室11',
size: 10,
bold: false,
},
],
};
};
//
const canvasEditor = ref<InstanceType<typeof CanvasEditor> | null>(null);
//
const handleSaveContent = () => {
// 访
(canvasEditor.value as any).saveContent();
};
//
const handleSaveCanvasEditorContent = (data: any) => {
console.log('从子组件接收到的数据:', data);
// data json , 便
content.value = JSON.stringify(data);
console.log('转换后的数据 content 为: ', content.value);
};
const router = useRouter();
//
const loading = ref(false);
const handleMenuClick = ({ key }) => {
console.log(key);
router.push({ path: key });
};
//
const fetchTaskList = async () => {};
//
onMounted(() => {
fetchTaskList();
});
setTimeout(() => {
loading.value = false;
}, 1500);
//
const submit = async () => {
//
const initialLength = initialCardList.value.length;
const currentLength = cardList.value.length;
//
const hasContentChanged =
JSON.stringify(initialCardList.value) !== JSON.stringify(cardList.value);
if (!hasContentChanged) {
//
closeModal();
return;
}
//
if (initialLength > 0 && currentLength === 0) {
Modal.confirm({
title: '确认提交',
content: h('div', [
'当前合同已无异常点, 状态将变为',
h('span', { style: { color: '#52c41a', fontWeight: 'bold' } }, '审核通过'),
',是否确认?',
]),
okText: '确认',
cancelText: '取消',
async onOk() {
await handleSubmit();
},
});
return;
}
//
if (initialLength === 0 && currentLength > 0) {
Modal.confirm({
title: '确认提交',
content: h('div', [
'当前合同新增异常点, 状态将变为',
h('span', { style: { color: '#ff4d4f', fontWeight: 'bold' } }, '审核不合格'),
',是否确认?',
]),
okText: '确认',
cancelText: '取消',
async onOk() {
await handleSubmit();
},
});
return;
}
//
Modal.confirm({
title: '确认提交',
content: '内容已修改,是否提交?',
okText: '确认',
cancelText: '取消',
async onOk() {
await handleSubmit();
},
});
};
//
const handleSubmit = async () => {
try {
console.log('原始数据', cardList.value);
//
const submitData = {
results: [
{
title: '人工干预', //
contentList: cardList.value.map((card) => ({
problemTitle: card.title,
text: card.text,
problemDesc: card.content,
isPosition: card.isPosition,
accord: card.accord,
})),
},
],
};
console.log('封装后的提交数据', submitData);
// TODO:
const res = await modifyContractReview(currentId.value, submitData);
emit('reload');
//
closeModal();
//
Modal.success({
title: '提示',
content: '提交成功!',
});
} catch (error) {
//
console.log('提交失败', error);
Modal.error({
title: '错误',
content: '提交失败,请重试!',
});
}
};
</script>
<style scoped>
/* 添加新的样式 */
.modal-content {
height: 100%;
display: flex;
flex-direction: column;
}
.grid {
flex: 1;
min-height: 0; /* 重要:防止grid溢出 */
}
.pdf-container {
height: 100%;
overflow: hidden;
}
.review-container {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.review-content {
flex: 1;
min-height: 0; /* 重要:防止内容溢出 */
overflow: hidden;
}
/* 确保showResultCard组件占满剩余空间 */
.review-content :deep(.tabs-container) {
height: 100%;
display: flex;
flex-direction: column;
}
/* 调整tabs内容区域的高度 */
.review-content :deep(.ant-tabs) {
height: 100%;
display: flex;
flex-direction: column;
}
.review-content :deep(.ant-tabs-content) {
flex: 1;
height: 100%;
overflow: hidden;
}
.review-content :deep(.ant-tabs-tabpane) {
height: 100%;
display: flex;
flex-direction: column;
}
.review-content :deep(.scroll-container) {
flex: 1;
overflow-y: auto;
}
.pdf-container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background: #f0f2f5;
}
.pdf-wrapper {
flex: 1;
overflow: auto;
padding: 20px;
display: flex;
justify-content: center;
background: #f0f2f5;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 16px;
color: #666;
}
.pdf-controls {
padding: 16px;
display: flex;
justify-content: center;
align-items: center;
background: #fff;
border-top: 1px solid #f0f0f0;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
}
:deep(.vue3-pdf) {
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 20px;
}
:deep(.vue3-pdf canvas) {
max-width: 100%;
height: auto !important;
}
/* 合同文本容器样式 */
.contract-text-container {
height: 100%;
display: flex;
flex-direction: column;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.contract-text-header {
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
}
.contract-text-header h3 {
margin: 0;
color: #333;
font-size: 16px;
}
.contract-text-content {
flex: 1;
padding: 16px;
overflow: hidden;
}
.contract-text-scroll {
height: 100%;
overflow-y: auto;
}
.contract-text-scroll pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
font-family: 'Source Han Sans CN', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
}
</style>

255
src/views/contractReview/ContractualTasks/ContractualShowModal.vue

@ -3,18 +3,20 @@
v-bind="$attrs"
title="title"
:canFullscreen="true"
:defaultFullscreen="true"
:defaultFullscreen="true"
@ok="submit"
@register="registerInnerModal"
>
<div class="modal-content">
<div class="grid grid-cols-5 gap-4 h-full">
<div class="col-span-3 pdf-container">
<PdfViewer :id="currentId" :key="pdfViewerkey" v-if="currentId!=0"/>
<div class="pdf-container">
<vue-office-docx :src="pdfUrl" />
</div>
</div>
<div class="col-span-2 review-container">
<div class="review-content">
<showResultCard v-model:cardList="cardList"/>
<showResultCard v-model:cardList="cardList" />
</div>
</div>
</div>
@ -22,22 +24,24 @@
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, onMounted, reactive, h } from 'vue';
import { ref, onMounted, reactive, h, nextTick } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import VueOfficeDocx from '@vue-office/pdf';
import { Tabs, TabPane, Card, Modal } from 'ant-design-vue';
import { useRouter } from 'vue-router';
import CanvasEditor from '@/views/CanvasEditor/index.vue';
import { getContractualResultById } from '@/api/contractReview/JyjcontractualTaskBatch';
import { modifyContractReview } from '@/api/documentReview/DocumentTaskResults';
import PdfViewer from "./PdfViewer.vue"
import { getPdfFile } from '@/api/contractReview/JyjcontractualTaskBatch';
import showResultCard from './showResultCard.vue';
import { cloneDeep } from 'lodash-es';
const emit = defineEmits(['register', 'reload']);
const activeKey = ref('1');
const pdfUrl = ref<string>('');
const currentId = ref<any>(0);
const pdfViewerkey = ref(0);
const scale = ref(1.5); //
//
const cardList = ref<any>([]);
const selectedCard = ref(''); //
@ -55,6 +59,7 @@
cardList.value = [];
const { record } = data;
const res = await getContractualResultById(record.id);
// const content = await getContractulContent(record.id);
pdfViewerkey.value += 1;
currentId.value = record.id;
for (const item of res.results) {
@ -68,6 +73,18 @@
});
}
}
const response = await getPdfFile(record.id);
if (response?.data) {
console.log('PDF加载成功2:', pdfUrl.value);
const blob = new Blob([response.data], { type: 'application/pdf' });
pdfUrl.value = URL.createObjectURL(blob);
console.log('PDF加载成功3:', pdfUrl.value);
// PDF
nextTick(() => {
setTimeout(fitToWidth, 500);
});
}
//
initialCardList.value = cloneDeep(cardList.value);
modalLoading(false);
@ -87,9 +104,18 @@
getEditorContent();
view.value = 'parent';
});
function handleSaveContent1(){
console.log('保存内容',cardList.value);
}
//
const fitToWidth = () => {
const pdfContainer = document.querySelector('.pdf-wrapper');
if (pdfContainer) {
const containerWidth = pdfContainer.clientWidth;
const pdfCanvas = document.querySelector('.vue3-pdf canvas') as HTMLCanvasElement;
if (pdfCanvas) {
const originalWidth = pdfCanvas.width / scale.value;
scale.value = +((containerWidth / originalWidth) * 0.95).toFixed(1); // 95%
}
}
};
//
const getEditorContent = () => {
parentContent = {
@ -153,10 +179,11 @@
//
const initialLength = initialCardList.value.length;
const currentLength = cardList.value.length;
//
const hasContentChanged = JSON.stringify(initialCardList.value) !== JSON.stringify(cardList.value);
const hasContentChanged =
JSON.stringify(initialCardList.value) !== JSON.stringify(cardList.value);
if (!hasContentChanged) {
//
closeModal();
@ -170,13 +197,13 @@
content: h('div', [
'当前合同已无异常点, 状态将变为',
h('span', { style: { color: '#52c41a', fontWeight: 'bold' } }, '审核通过'),
',是否确认?'
',是否确认?',
]),
okText: '确认',
cancelText: '取消',
async onOk() {
await handleSubmit();
}
},
});
return;
}
@ -188,13 +215,13 @@
content: h('div', [
'当前合同新增异常点, 状态将变为',
h('span', { style: { color: '#ff4d4f', fontWeight: 'bold' } }, '审核不合格'),
',是否确认?'
',是否确认?',
]),
okText: '确认',
cancelText: '取消',
async onOk() {
await handleSubmit();
}
},
});
return;
}
@ -207,7 +234,7 @@
cancelText: '取消',
async onOk() {
await handleSubmit();
}
},
});
};
@ -215,106 +242,152 @@
const handleSubmit = async () => {
try {
console.log('原始数据', cardList.value);
//
const submitData = {
results: [
{
title: "人工干预", //
contentList: cardList.value.map(card => ({
title: '人工干预', //
contentList: cardList.value.map((card) => ({
problemTitle: card.title,
text: card.text,
problemDesc: card.content,
isPosition: card.isPosition,
accord: card.accord
}))
}
]
accord: card.accord,
})),
},
],
};
console.log('封装后的提交数据', submitData);
// TODO:
const res = await modifyContractReview(currentId.value,submitData);
const res = await modifyContractReview(currentId.value, submitData);
emit('reload');
//
closeModal();
//
Modal.success({
title: '提示',
content: '提交成功!'
content: '提交成功!',
});
} catch (error) {
//
console.log('提交失败',error);
console.log('提交失败', error);
Modal.error({
title: '错误',
content: '提交失败,请重试!'
content: '提交失败,请重试!',
});
}
};
</script>
<style scoped>
/* 添加新的样式 */
.modal-content {
height: 100%;
display: flex;
flex-direction: column;
}
.grid {
flex: 1;
min-height: 0; /* 重要:防止grid溢出 */
}
.pdf-container {
height: 100%;
overflow: hidden;
}
.review-container {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.review-content {
flex: 1;
min-height: 0; /* 重要:防止内容溢出 */
overflow: hidden;
}
/* 确保showResultCard组件占满剩余空间 */
.review-content :deep(.tabs-container) {
height: 100%;
display: flex;
flex-direction: column;
}
/* 调整tabs内容区域的高度 */
.review-content :deep(.ant-tabs) {
height: 100%;
display: flex;
flex-direction: column;
}
.review-content :deep(.ant-tabs-content) {
flex: 1;
height: 100%;
overflow: hidden;
}
.review-content :deep(.ant-tabs-tabpane) {
height: 100%;
display: flex;
flex-direction: column;
}
.review-content :deep(.scroll-container) {
flex: 1;
overflow-y: auto;
}
/* 添加新的样式 */
.modal-content {
height: 100%;
display: flex;
flex-direction: column;
}
.grid {
flex: 1;
min-height: 0; /* 重要:防止grid溢出 */
}
.pdf-container {
height: 100%;
overflow: hidden;
}
.review-container {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.review-content {
flex: 1;
min-height: 0; /* 重要:防止内容溢出 */
overflow: hidden;
}
/* 确保showResultCard组件占满剩余空间 */
.review-content :deep(.tabs-container) {
height: 100%;
display: flex;
flex-direction: column;
}
/* 调整tabs内容区域的高度 */
.review-content :deep(.ant-tabs) {
height: 100%;
display: flex;
flex-direction: column;
}
.review-content :deep(.ant-tabs-content) {
flex: 1;
height: 100%;
overflow: hidden;
}
.review-content :deep(.ant-tabs-tabpane) {
height: 100%;
display: flex;
flex-direction: column;
}
.review-content :deep(.scroll-container) {
flex: 1;
overflow-y: auto;
}
.pdf-container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background: #f0f2f5;
}
.pdf-wrapper {
flex: 1;
overflow: auto;
padding: 20px;
display: flex;
justify-content: center;
background: #f0f2f5;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 16px;
color: #666;
}
.pdf-controls {
padding: 16px;
display: flex;
justify-content: center;
align-items: center;
background: #fff;
border-top: 1px solid #f0f0f0;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
}
:deep(.vue3-pdf) {
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 20px;
}
:deep(.vue3-pdf canvas) {
max-width: 100%;
height: auto !important;
}
</style>

20
src/views/contractReview/ContractualTasks/ContractualTasks.data.ts

@ -3,7 +3,7 @@ import { FormSchema } from '@/components/Form';
import { getDictOptions } from '@/utils/dict';
import { useRender } from '@/hooks/component/useRender';
import { uploadDocument } from '@/api/documentReview/DocumentTasks';
import { useUserStore } from '@/store/modules/user';
import { getRequirementTypeDict } from '@/api/contractReview/ContractualAuditConfig';
export const formSchemas: FormSchema[] = [
{
label: '文档名称',
@ -26,6 +26,11 @@ export const formSchemas: FormSchema[] = [
options: getDictOptions('jyj_contract_result_type'),
},
},
{
label: '问题点',
field: 'problemPoint',
component:"Input"
},
{
label: '批次名称',
field: 'batchName',
@ -64,6 +69,10 @@ export const columns: BasicColumn[] = [
dataIndex: 'resultType',
customRender: ({ value }) => renderDict(value, 'jyj_contract_result_type'),
},
{
title: '问题点',
dataIndex: 'problemPoint',
},
{
title: '状态',
dataIndex: 'progressStatus',
@ -98,4 +107,13 @@ export const modalSchemas: FormSchema[] = [
beforeUploadPrompt:"严禁在本互联网非涉密平台处理、传输国家秘密。请再次确认您上传的文件资料不涉及国家秘密。"
},
},
{
label: '审核类型',
field: 'requirementType',
required: true,
component: 'ApiSelect',
componentProps:{
api: getRequirementTypeDict
},
},
];

38
src/views/contractReview/ContractualTasks/ContractualTasksTable.vue

@ -70,25 +70,25 @@
// },
// onClick: handleDownload.bind(null, record),
// },
{
label: '终止任务',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
ifShow: () => {
if (record.progressStatus == 'PENDING') {
return true;
} else {
return false;
}
},
popConfirm: {
placement: 'left',
title: '是否终止当前任务?',
confirm: handleStop.bind(null, record),
},
},
// {
// label: '',
// icon: IconEnum.DELETE,
// type: 'primary',
// danger: true,
// ghost: true,
// ifShow: () => {
// if (record.progressStatus == 'PENDING') {
// return true;
// } else {
// return false;
// }
// },
// popConfirm: {
// placement: 'left',
// title: '?',
// confirm: handleStop.bind(null, record),
// },
// },
]"
/>
</template>

89
src/views/contractReview/ContractualTasks/PdfViewer.vue

@ -9,11 +9,9 @@
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
//VueOfficeDocx
import VueOfficeDocx from '@vue-office/pdf'
//
import { message } from 'ant-design-vue';
import { MinusOutlined, PlusOutlined } from '@ant-design/icons-vue';
import { getPdfFile } from '@/api/contractReview/JyjcontractualTaskBatch';
// Props
interface Props {
@ -21,51 +19,9 @@ interface Props {
}
const props = defineProps<Props>();
const pdfUrl = ref<string>('');
const currentPage = ref(1);
const pageCount = ref(0);
const documentLoaded = ref(false);
const scale = ref(1.5); //
//
const fitToWidth = () => {
const pdfContainer = document.querySelector('.pdf-wrapper');
if (pdfContainer) {
const containerWidth = pdfContainer.clientWidth;
const pdfCanvas = document.querySelector('.vue3-pdf canvas') as HTMLCanvasElement;
if (pdfCanvas) {
const originalWidth = pdfCanvas.width / scale.value;
scale.value = +(containerWidth / originalWidth * 0.95).toFixed(1); // 95%
}
}
};
// PDF
const loadPdf = async () => {
try {
if(props.id==0){
pdfUrl.value='';
return;
}
console.log('PDF加载成功1:', pdfUrl.value);
const response = await getPdfFile(props.id);
if (response?.data) {
console.log('PDF加载成功2:', pdfUrl.value);
const blob = new Blob([response.data], { type: 'application/pdf' });
pdfUrl.value = URL.createObjectURL(blob);
console.log('PDF加载成功3:', pdfUrl.value);
// PDF
nextTick(() => {
setTimeout(fitToWidth, 500);
});
}
} catch (error) {
console.error('PDF加载失败:', error);
}
};
const handleError = (error: Error) => {
message.error('PDF显示出错:' + error.message);
@ -78,50 +34,5 @@ onMounted(() => {
</script>
<style scoped>
.pdf-container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background: #f0f2f5;
}
.pdf-wrapper {
flex: 1;
overflow: auto;
padding: 20px;
display: flex;
justify-content: center;
background: #f0f2f5;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 16px;
color: #666;
}
.pdf-controls {
padding: 16px;
display: flex;
justify-content: center;
align-items: center;
background: #fff;
border-top: 1px solid #f0f0f0;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
}
:deep(.vue3-pdf) {
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 20px;
}
:deep(.vue3-pdf canvas) {
max-width: 100%;
height: auto !important;
}
</style>

2
src/views/contractReview/ContractualTasks/showResultCard.vue

@ -5,7 +5,7 @@
<div class="action-buttons">
<Button type="primary" class="action-button add-button" @click="handleAdd">
<template #icon><PlusOutlined /></template>
新增审查
新增问题
</Button>
</div>
<div class="scroll-container">

12
src/views/contractReview/JyjcontractualTaskBatch/JyjcontractualTaskBatch.data.ts

@ -2,7 +2,9 @@ import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
import { getDictOptions } from '@/utils/dict';
import { useRender } from '@/hooks/component/useRender';
const { renderDict } = useRender();
import { uploadContractual } from '@/api/contractReview/JyjcontractualTaskBatch';
import { getRequirementTypeDict } from '@/api/contractReview/ContractualAuditConfig';
export const formSchemas: FormSchema[] = [
{
@ -25,7 +27,6 @@ export const formSchemas: FormSchema[] = [
},
];
const { renderDict } = useRender();
export const columns: BasicColumn[] = [
{
title: '文件名称',
@ -94,6 +95,15 @@ export const modalSchemas: FormSchema[] = [
api: uploadContractual,
},
},
{
label: '审核类型',
field: 'requirementType',
required: true,
component: 'ApiSelect',
componentProps:{
api: getRequirementTypeDict
},
},
{
label: '批次名称',
field: 'batchName',

66
src/views/contractReview/RequirementContractualNormal/RequirementContractualNormal.data.ts

@ -0,0 +1,66 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
export const formSchemas: FormSchema[] = [
{
label: '合同审核配置表ID',
field: 'configId',
component: 'Input',
},
{
label: '检查项',
field: 'checkItem',
component: 'Input',
},
{
label: '要求描述',
field: 'requirementDesc',
component: 'InputTextArea',
},
];
export const columns: BasicColumn[] = [
{
title: '主键ID',
dataIndex: 'id',
},
{
title: '合同审核配置表ID',
dataIndex: 'configId',
},
{
title: '检查项',
dataIndex: 'checkItem',
},
{
title: '要求描述',
dataIndex: 'requirementDesc',
},
];
export const modalSchemas: FormSchema[] = [
{
label: '主键ID',
field: 'id',
required: false,
component: 'Input',
show: false,
},
{
label: '合同审核配置表ID',
field: 'configId',
required: true,
component: 'Input',
},
{
label: '检查项',
field: 'checkItem',
required: true,
component: 'Input',
},
{
label: '要求描述',
field: 'requirementDesc',
required: true,
component: 'InputTextArea',
},
];

68
src/views/contractReview/RequirementContractualNormal/RequirementContractualNormalModal.vue

@ -0,0 +1,68 @@
<template>
<BasicModal
v-bind="$attrs"
:title="title"
@register="registerInnerModal"
@ok="handleSubmit"
@cancel="resetForm"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { computed, ref, unref } from 'vue';
import { RequirementContractualNormalInfo, RequirementContractualNormalAdd, RequirementContractualNormalUpdate } from '@/api/contractReview/RequirementContractualNormal';
import { modalSchemas } from './RequirementContractualNormal.data';
defineOptions({ name: 'RequirementContractualNormalModal' });
const emit = defineEmits(['register', 'reload']);
const isUpdate = ref<boolean>(false);
const title = computed<string>(() => {
return isUpdate.value ? '编辑合同常规要求' : '新增合同常规要求';
});
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable; update: boolean }) => {
modalLoading(true);
const { record, update } = data;
isUpdate.value = update;
if (update && record) {
const ret = await RequirementContractualNormalInfo(record.id);
await setFieldsValue(ret);
}
modalLoading(false);
},
);
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({
labelWidth: 100,
showActionButtonGroup: false,
baseColProps: { span: 24 },
schemas: modalSchemas,
});
async function handleSubmit() {
try {
modalLoading(true);
const data = await validate();
if (unref(isUpdate)) {
await RequirementContractualNormalUpdate(data);
} else {
await RequirementContractualNormalAdd(data);
}
emit('reload');
closeModal();
await resetForm();
} catch (e) {
} finally {
modalLoading(false);
}
}
</script>
<style scoped></style>

115
src/views/contractReview/RequirementContractualNormal/index.vue

@ -0,0 +1,115 @@
<template>
<PageWrapper dense>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button
@click="downloadExcel(RequirementContractualNormalExport, '合同常规要求数据', getForm().getFieldsValue())"
v-auth="'productManagement:RequirementContractualNormal:export'"
>导出</a-button
>
<a-button
type="primary"
danger
@click="multipleRemove(RequirementContractualNormalRemove)"
:disabled="!selected"
v-auth="'productManagement:RequirementContractualNormal:remove'"
>删除</a-button
>
<a-button
type="primary"
@click="handleAdd"
v-auth="'productManagement:RequirementContractualNormal:add'"
>新增</a-button
>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
stopButtonPropagation
:actions="[
{
label: '修改',
icon: IconEnum.EDIT,
type: 'primary',
ghost: true,
auth: 'productManagement:RequirementContractualNormal:edit',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
auth: 'productManagement:RequirementContractualNormal:remove',
popConfirm: {
placement: 'left',
title: '是否删除合同常规要求[' + record.id + ']?',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<RequirementContractualNormalModal @register="registerModal" @reload="reload" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { RequirementContractualNormalList, RequirementContractualNormalExport, RequirementContractualNormalRemove } from '@/api/contractReview/RequirementContractualNormal';
import { downloadExcel } from '@/utils/file/download';
import { useModal } from '@/components/Modal';
import RequirementContractualNormalModal from './RequirementContractualNormalModal.vue';
import { formSchemas, columns } from './RequirementContractualNormal.data';
import { IconEnum } from '@/enums/appEnum';
defineOptions({ name: 'RequirementContractualNormal' });
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({
rowSelection: {
type: 'checkbox',
},
title: '合同常规要求列表',
api: RequirementContractualNormalList,
showIndexColumn: false,
rowKey: 'id',
useSearchForm: true,
formConfig: {
schemas: formSchemas,
baseColProps: {
xs: 24,
sm: 24,
md: 24,
lg: 6,
},
},
columns: columns,
actionColumn: {
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
},
});
const [registerModal, { openModal }] = useModal();
function handleEdit(record: Recordable) {
openModal(true, { record, update: true });
}
function handleAdd() {
openModal(true, { update: false });
}
async function handleDelete(record: Recordable) {
await RequirementContractualNormalRemove([record.id]);
await reload();
}
</script>
<style scoped></style>

81
src/views/contractReview/RequirementCpu/RequirementCpu.data.ts

@ -0,0 +1,81 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
export const formSchemas: FormSchema[] = [
{
label: '合同审核配置表ID',
field: 'configId',
component: 'Input',
},
{
label: '名称',
field: 'name',
component: 'Input',
},
{
label: '型号',
field: 'model',
component: 'Input',
},
{
label: '版本号',
field: 'versionStr',
component: 'Input',
},
];
export const columns: BasicColumn[] = [
{
title: '主键ID',
dataIndex: 'id',
},
{
title: '合同审核配置表ID',
dataIndex: 'configId',
},
{
title: '名称',
dataIndex: 'name',
},
{
title: '型号',
dataIndex: 'model',
},
{
title: '版本号',
dataIndex: 'versionStr',
},
];
export const modalSchemas: FormSchema[] = [
{
label: '主键ID',
field: 'id',
required: false,
component: 'Input',
show: false,
},
{
label: '合同审核配置表ID',
field: 'configId',
required: true,
component: 'Input',
},
{
label: '名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '型号',
field: 'model',
required: false,
component: 'Input',
},
{
label: '版本号',
field: 'versionStr',
required: false,
component: 'Input',
},
];

68
src/views/contractReview/RequirementCpu/RequirementCpuModal.vue

@ -0,0 +1,68 @@
<template>
<BasicModal
v-bind="$attrs"
:title="title"
@register="registerInnerModal"
@ok="handleSubmit"
@cancel="resetForm"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { computed, ref, unref } from 'vue';
import { RequirementCpuInfo, RequirementCpuAdd, RequirementCpuUpdate } from '@/api/contractReview/RequirementCpu';
import { modalSchemas } from './RequirementCpu.data';
defineOptions({ name: 'RequirementCpuModal' });
const emit = defineEmits(['register', 'reload']);
const isUpdate = ref<boolean>(false);
const title = computed<string>(() => {
return isUpdate.value ? '编辑CPU要求' : '新增CPU要求';
});
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable; update: boolean }) => {
modalLoading(true);
const { record, update } = data;
isUpdate.value = update;
if (update && record) {
const ret = await RequirementCpuInfo(record.id);
await setFieldsValue(ret);
}
modalLoading(false);
},
);
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({
labelWidth: 100,
showActionButtonGroup: false,
baseColProps: { span: 24 },
schemas: modalSchemas,
});
async function handleSubmit() {
try {
modalLoading(true);
const data = await validate();
if (unref(isUpdate)) {
await RequirementCpuUpdate(data);
} else {
await RequirementCpuAdd(data);
}
emit('reload');
closeModal();
await resetForm();
} catch (e) {
} finally {
modalLoading(false);
}
}
</script>
<style scoped></style>

115
src/views/contractReview/RequirementCpu/index.vue

@ -0,0 +1,115 @@
<template>
<PageWrapper dense>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button
@click="downloadExcel(RequirementCpuExport, 'CPU要求数据', getForm().getFieldsValue())"
v-auth="'productManagement:RequirementCpu:export'"
>导出</a-button
>
<a-button
type="primary"
danger
@click="multipleRemove(RequirementCpuRemove)"
:disabled="!selected"
v-auth="'productManagement:RequirementCpu:remove'"
>删除</a-button
>
<a-button
type="primary"
@click="handleAdd"
v-auth="'productManagement:RequirementCpu:add'"
>新增</a-button
>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
stopButtonPropagation
:actions="[
{
label: '修改',
icon: IconEnum.EDIT,
type: 'primary',
ghost: true,
auth: 'productManagement:RequirementCpu:edit',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
auth: 'productManagement:RequirementCpu:remove',
popConfirm: {
placement: 'left',
title: '是否删除CPU要求[' + record.id + ']?',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<RequirementCpuModal @register="registerModal" @reload="reload" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { RequirementCpuList, RequirementCpuExport, RequirementCpuRemove } from '@/api/contractReview/RequirementCpu';
import { downloadExcel } from '@/utils/file/download';
import { useModal } from '@/components/Modal';
import RequirementCpuModal from './RequirementCpuModal.vue';
import { formSchemas, columns } from './RequirementCpu.data';
import { IconEnum } from '@/enums/appEnum';
defineOptions({ name: 'RequirementCpu' });
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({
rowSelection: {
type: 'checkbox',
},
title: 'CPU要求列表',
api: RequirementCpuList,
showIndexColumn: false,
rowKey: 'id',
useSearchForm: true,
formConfig: {
schemas: formSchemas,
baseColProps: {
xs: 24,
sm: 24,
md: 24,
lg: 6,
},
},
columns: columns,
actionColumn: {
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
},
});
const [registerModal, { openModal }] = useModal();
function handleEdit(record: Recordable) {
openModal(true, { record, update: true });
}
function handleAdd() {
openModal(true, { update: false });
}
async function handleDelete(record: Recordable) {
await RequirementCpuRemove([record.id]);
await reload();
}
</script>
<style scoped></style>

81
src/views/contractReview/RequirementDatabase/RequirementDatabase.data.ts

@ -0,0 +1,81 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
export const formSchemas: FormSchema[] = [
{
label: '合同审核配置表ID',
field: 'configId',
component: 'Input',
},
{
label: '名称',
field: 'name',
component: 'Input',
},
{
label: '型号',
field: 'model',
component: 'Input',
},
{
label: '版本号',
field: 'versionStr',
component: 'Input',
},
];
export const columns: BasicColumn[] = [
{
title: '主键ID',
dataIndex: 'id',
},
{
title: '合同审核配置表ID',
dataIndex: 'configId',
},
{
title: '名称',
dataIndex: 'name',
},
{
title: '型号',
dataIndex: 'model',
},
{
title: '版本号',
dataIndex: 'versionStr',
},
];
export const modalSchemas: FormSchema[] = [
{
label: '主键ID',
field: 'id',
required: false,
component: 'Input',
show: false,
},
{
label: '合同审核配置表ID',
field: 'configId',
required: true,
component: 'Input',
},
{
label: '名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '型号',
field: 'model',
required: false,
component: 'Input',
},
{
label: '版本号',
field: 'versionStr',
required: false,
component: 'Input',
},
];

68
src/views/contractReview/RequirementDatabase/RequirementDatabaseModal.vue

@ -0,0 +1,68 @@
<template>
<BasicModal
v-bind="$attrs"
:title="title"
@register="registerInnerModal"
@ok="handleSubmit"
@cancel="resetForm"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { computed, ref, unref } from 'vue';
import { RequirementDatabaseInfo, RequirementDatabaseAdd, RequirementDatabaseUpdate } from '@/api/contractReview/RequirementDatabase';
import { modalSchemas } from './RequirementDatabase.data';
defineOptions({ name: 'RequirementDatabaseModal' });
const emit = defineEmits(['register', 'reload']);
const isUpdate = ref<boolean>(false);
const title = computed<string>(() => {
return isUpdate.value ? '编辑数据库要求' : '新增数据库要求';
});
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable; update: boolean }) => {
modalLoading(true);
const { record, update } = data;
isUpdate.value = update;
if (update && record) {
const ret = await RequirementDatabaseInfo(record.id);
await setFieldsValue(ret);
}
modalLoading(false);
},
);
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({
labelWidth: 100,
showActionButtonGroup: false,
baseColProps: { span: 24 },
schemas: modalSchemas,
});
async function handleSubmit() {
try {
modalLoading(true);
const data = await validate();
if (unref(isUpdate)) {
await RequirementDatabaseUpdate(data);
} else {
await RequirementDatabaseAdd(data);
}
emit('reload');
closeModal();
await resetForm();
} catch (e) {
} finally {
modalLoading(false);
}
}
</script>
<style scoped></style>

115
src/views/contractReview/RequirementDatabase/index.vue

@ -0,0 +1,115 @@
<template>
<PageWrapper dense>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button
@click="downloadExcel(RequirementDatabaseExport, '数据库要求数据', getForm().getFieldsValue())"
v-auth="'productManagement:RequirementDatabase:export'"
>导出</a-button
>
<a-button
type="primary"
danger
@click="multipleRemove(RequirementDatabaseRemove)"
:disabled="!selected"
v-auth="'productManagement:RequirementDatabase:remove'"
>删除</a-button
>
<a-button
type="primary"
@click="handleAdd"
v-auth="'productManagement:RequirementDatabase:add'"
>新增</a-button
>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
stopButtonPropagation
:actions="[
{
label: '修改',
icon: IconEnum.EDIT,
type: 'primary',
ghost: true,
auth: 'productManagement:RequirementDatabase:edit',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
auth: 'productManagement:RequirementDatabase:remove',
popConfirm: {
placement: 'left',
title: '是否删除数据库要求[' + record.id + ']?',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<RequirementDatabaseModal @register="registerModal" @reload="reload" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { RequirementDatabaseList, RequirementDatabaseExport, RequirementDatabaseRemove } from '@/api/contractReview/RequirementDatabase';
import { downloadExcel } from '@/utils/file/download';
import { useModal } from '@/components/Modal';
import RequirementDatabaseModal from './RequirementDatabaseModal.vue';
import { formSchemas, columns } from './RequirementDatabase.data';
import { IconEnum } from '@/enums/appEnum';
defineOptions({ name: 'RequirementDatabase' });
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({
rowSelection: {
type: 'checkbox',
},
title: '数据库要求列表',
api: RequirementDatabaseList,
showIndexColumn: false,
rowKey: 'id',
useSearchForm: true,
formConfig: {
schemas: formSchemas,
baseColProps: {
xs: 24,
sm: 24,
md: 24,
lg: 6,
},
},
columns: columns,
actionColumn: {
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
},
});
const [registerModal, { openModal }] = useModal();
function handleEdit(record: Recordable) {
openModal(true, { record, update: true });
}
function handleAdd() {
openModal(true, { update: false });
}
async function handleDelete(record: Recordable) {
await RequirementDatabaseRemove([record.id]);
await reload();
}
</script>
<style scoped></style>

96
src/views/contractReview/RequirementEntity/RequirementEntity.data.ts

@ -0,0 +1,96 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
export const formSchemas: FormSchema[] = [
{
label: '合同审核配置表ID',
field: 'configId',
component: 'Input',
},
{
label: '主体名称',
field: 'entityName',
component: 'Input',
},
{
label: '父主体ID,0表示顶级主体',
field: 'parentId',
component: 'Input',
},
{
label: '层级,1表示顶级',
field: 'level',
component: 'Input',
},
{
label: '层级路径,格式如:0,1,2',
field: 'path',
component: 'Input',
},
];
export const columns: BasicColumn[] = [
{
title: '主键ID',
dataIndex: 'id',
},
{
title: '合同审核配置表ID',
dataIndex: 'configId',
},
{
title: '主体名称',
dataIndex: 'entityName',
},
{
title: '父主体ID,0表示顶级主体',
dataIndex: 'parentId',
},
{
title: '层级,1表示顶级',
dataIndex: 'level',
},
{
title: '层级路径,格式如:0,1,2',
dataIndex: 'path',
},
];
export const modalSchemas: FormSchema[] = [
{
label: '主键ID',
field: 'id',
required: false,
component: 'Input',
show: false,
},
{
label: '合同审核配置表ID',
field: 'configId',
required: true,
component: 'Input',
},
{
label: '主体名称',
field: 'entityName',
required: true,
component: 'Input',
},
{
label: '父主体ID,0表示顶级主体',
field: 'parentId',
required: true,
component: 'Input',
},
{
label: '层级,1表示顶级',
field: 'level',
required: true,
component: 'Input',
},
{
label: '层级路径,格式如:0,1,2',
field: 'path',
required: true,
component: 'Input',
},
];

68
src/views/contractReview/RequirementEntity/RequirementEntityModal.vue

@ -0,0 +1,68 @@
<template>
<BasicModal
v-bind="$attrs"
:title="title"
@register="registerInnerModal"
@ok="handleSubmit"
@cancel="resetForm"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { computed, ref, unref } from 'vue';
import { RequirementEntityInfo, RequirementEntityAdd, RequirementEntityUpdate } from '@/api/contractReview/RequirementEntity';
import { modalSchemas } from './RequirementEntity.data';
defineOptions({ name: 'RequirementEntityModal' });
const emit = defineEmits(['register', 'reload']);
const isUpdate = ref<boolean>(false);
const title = computed<string>(() => {
return isUpdate.value ? '编辑主体要求' : '新增主体要求';
});
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable; update: boolean }) => {
modalLoading(true);
const { record, update } = data;
isUpdate.value = update;
if (update && record) {
const ret = await RequirementEntityInfo(record.id);
await setFieldsValue(ret);
}
modalLoading(false);
},
);
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({
labelWidth: 100,
showActionButtonGroup: false,
baseColProps: { span: 24 },
schemas: modalSchemas,
});
async function handleSubmit() {
try {
modalLoading(true);
const data = await validate();
if (unref(isUpdate)) {
await RequirementEntityUpdate(data);
} else {
await RequirementEntityAdd(data);
}
emit('reload');
closeModal();
await resetForm();
} catch (e) {
} finally {
modalLoading(false);
}
}
</script>
<style scoped></style>

115
src/views/contractReview/RequirementEntity/index.vue

@ -0,0 +1,115 @@
<template>
<PageWrapper dense>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button
@click="downloadExcel(RequirementEntityExport, '主体要求数据', getForm().getFieldsValue())"
v-auth="'productManagement:RequirementEntity:export'"
>导出</a-button
>
<a-button
type="primary"
danger
@click="multipleRemove(RequirementEntityRemove)"
:disabled="!selected"
v-auth="'productManagement:RequirementEntity:remove'"
>删除</a-button
>
<a-button
type="primary"
@click="handleAdd"
v-auth="'productManagement:RequirementEntity:add'"
>新增</a-button
>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
stopButtonPropagation
:actions="[
{
label: '修改',
icon: IconEnum.EDIT,
type: 'primary',
ghost: true,
auth: 'productManagement:RequirementEntity:edit',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
auth: 'productManagement:RequirementEntity:remove',
popConfirm: {
placement: 'left',
title: '是否删除主体要求[' + record.id + ']?',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<RequirementEntityModal @register="registerModal" @reload="reload" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { RequirementEntityList, RequirementEntityExport, RequirementEntityRemove } from '@/api/contractReview/RequirementEntity';
import { downloadExcel } from '@/utils/file/download';
import { useModal } from '@/components/Modal';
import RequirementEntityModal from './RequirementEntityModal.vue';
import { formSchemas, columns } from './RequirementEntity.data';
import { IconEnum } from '@/enums/appEnum';
defineOptions({ name: 'RequirementEntity' });
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({
rowSelection: {
type: 'checkbox',
},
title: '主体要求列表',
api: RequirementEntityList,
showIndexColumn: false,
rowKey: 'id',
useSearchForm: true,
formConfig: {
schemas: formSchemas,
baseColProps: {
xs: 24,
sm: 24,
md: 24,
lg: 6,
},
},
columns: columns,
actionColumn: {
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
},
});
const [registerModal, { openModal }] = useModal();
function handleEdit(record: Recordable) {
openModal(true, { record, update: true });
}
function handleAdd() {
openModal(true, { update: false });
}
async function handleDelete(record: Recordable) {
await RequirementEntityRemove([record.id]);
await reload();
}
</script>
<style scoped></style>

81
src/views/contractReview/RequirementOs/RequirementOs.data.ts

@ -0,0 +1,81 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
export const formSchemas: FormSchema[] = [
{
label: '合同审核配置表ID',
field: 'configId',
component: 'Input',
},
{
label: '名称',
field: 'name',
component: 'Input',
},
{
label: '型号',
field: 'model',
component: 'Input',
},
{
label: '版本号',
field: 'versionStr',
component: 'Input',
},
];
export const columns: BasicColumn[] = [
{
title: '主键ID',
dataIndex: 'id',
},
{
title: '合同审核配置表ID',
dataIndex: 'configId',
},
{
title: '名称',
dataIndex: 'name',
},
{
title: '型号',
dataIndex: 'model',
},
{
title: '版本号',
dataIndex: 'versionStr',
},
];
export const modalSchemas: FormSchema[] = [
{
label: '主键ID',
field: 'id',
required: false,
component: 'Input',
show: false,
},
{
label: '合同审核配置表ID',
field: 'configId',
required: true,
component: 'Input',
},
{
label: '名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '型号',
field: 'model',
required: false,
component: 'Input',
},
{
label: '版本号',
field: 'versionStr',
required: false,
component: 'Input',
},
];

68
src/views/contractReview/RequirementOs/RequirementOsModal.vue

@ -0,0 +1,68 @@
<template>
<BasicModal
v-bind="$attrs"
:title="title"
@register="registerInnerModal"
@ok="handleSubmit"
@cancel="resetForm"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { computed, ref, unref } from 'vue';
import { RequirementOsInfo, RequirementOsAdd, RequirementOsUpdate } from '@/api/contractReview/RequirementOs';
import { modalSchemas } from './RequirementOs.data';
defineOptions({ name: 'RequirementOsModal' });
const emit = defineEmits(['register', 'reload']);
const isUpdate = ref<boolean>(false);
const title = computed<string>(() => {
return isUpdate.value ? '编辑操作系统要求' : '新增操作系统要求';
});
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable; update: boolean }) => {
modalLoading(true);
const { record, update } = data;
isUpdate.value = update;
if (update && record) {
const ret = await RequirementOsInfo(record.id);
await setFieldsValue(ret);
}
modalLoading(false);
},
);
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({
labelWidth: 100,
showActionButtonGroup: false,
baseColProps: { span: 24 },
schemas: modalSchemas,
});
async function handleSubmit() {
try {
modalLoading(true);
const data = await validate();
if (unref(isUpdate)) {
await RequirementOsUpdate(data);
} else {
await RequirementOsAdd(data);
}
emit('reload');
closeModal();
await resetForm();
} catch (e) {
} finally {
modalLoading(false);
}
}
</script>
<style scoped></style>

115
src/views/contractReview/RequirementOs/index.vue

@ -0,0 +1,115 @@
<template>
<PageWrapper dense>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button
@click="downloadExcel(RequirementOsExport, '操作系统要求数据', getForm().getFieldsValue())"
v-auth="'productManagement:RequirementOs:export'"
>导出</a-button
>
<a-button
type="primary"
danger
@click="multipleRemove(RequirementOsRemove)"
:disabled="!selected"
v-auth="'productManagement:RequirementOs:remove'"
>删除</a-button
>
<a-button
type="primary"
@click="handleAdd"
v-auth="'productManagement:RequirementOs:add'"
>新增</a-button
>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
stopButtonPropagation
:actions="[
{
label: '修改',
icon: IconEnum.EDIT,
type: 'primary',
ghost: true,
auth: 'productManagement:RequirementOs:edit',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
auth: 'productManagement:RequirementOs:remove',
popConfirm: {
placement: 'left',
title: '是否删除操作系统要求[' + record.id + ']?',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<RequirementOsModal @register="registerModal" @reload="reload" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { RequirementOsList, RequirementOsExport, RequirementOsRemove } from '@/api/contractReview/RequirementOs';
import { downloadExcel } from '@/utils/file/download';
import { useModal } from '@/components/Modal';
import RequirementOsModal from './RequirementOsModal.vue';
import { formSchemas, columns } from './RequirementOs.data';
import { IconEnum } from '@/enums/appEnum';
defineOptions({ name: 'RequirementOs' });
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({
rowSelection: {
type: 'checkbox',
},
title: '操作系统要求列表',
api: RequirementOsList,
showIndexColumn: false,
rowKey: 'id',
useSearchForm: true,
formConfig: {
schemas: formSchemas,
baseColProps: {
xs: 24,
sm: 24,
md: 24,
lg: 6,
},
},
columns: columns,
actionColumn: {
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
},
});
const [registerModal, { openModal }] = useModal();
function handleEdit(record: Recordable) {
openModal(true, { record, update: true });
}
function handleAdd() {
openModal(true, { update: false });
}
async function handleDelete(record: Recordable) {
await RequirementOsRemove([record.id]);
await reload();
}
</script>
<style scoped></style>

81
src/views/contractReview/RequirementProductAttribute/RequirementProductAttribute.data.ts

@ -0,0 +1,81 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Form';
export const formSchemas: FormSchema[] = [
{
label: '合同审核配置表ID',
field: 'configId',
component: 'Input',
},
{
label: '属性名称',
field: 'attributeName',
component: 'Input',
},
{
label: '要求',
field: 'requirement',
component: 'Input',
},
{
label: '产品类型',
field: 'productType',
component: 'Select',
},
];
export const columns: BasicColumn[] = [
{
title: '主键ID',
dataIndex: 'id',
},
{
title: '合同审核配置表ID',
dataIndex: 'configId',
},
{
title: '属性名称',
dataIndex: 'attributeName',
},
{
title: '要求',
dataIndex: 'requirement',
},
{
title: '产品类型',
dataIndex: 'productType',
},
];
export const modalSchemas: FormSchema[] = [
{
label: '主键ID',
field: 'id',
required: false,
component: 'Input',
show: false,
},
{
label: '合同审核配置表ID',
field: 'configId',
required: true,
component: 'Input',
},
{
label: '属性名称',
field: 'attributeName',
required: true,
component: 'Input',
},
{
label: '要求',
field: 'requirement',
required: true,
component: 'Input',
},
{
label: '产品类型',
field: 'productType',
required: true,
component: 'Select',
},
];

68
src/views/contractReview/RequirementProductAttribute/RequirementProductAttributeModal.vue

@ -0,0 +1,68 @@
<template>
<BasicModal
v-bind="$attrs"
:title="title"
@register="registerInnerModal"
@ok="handleSubmit"
@cancel="resetForm"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { computed, ref, unref } from 'vue';
import { RequirementProductAttributeInfo, RequirementProductAttributeAdd, RequirementProductAttributeUpdate } from '@/api/contractReview/RequirementProductAttribute';
import { modalSchemas } from './RequirementProductAttribute.data';
defineOptions({ name: 'RequirementProductAttributeModal' });
const emit = defineEmits(['register', 'reload']);
const isUpdate = ref<boolean>(false);
const title = computed<string>(() => {
return isUpdate.value ? '编辑产品属性要求' : '新增产品属性要求';
});
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner(
async (data: { record?: Recordable; update: boolean }) => {
modalLoading(true);
const { record, update } = data;
isUpdate.value = update;
if (update && record) {
const ret = await RequirementProductAttributeInfo(record.id);
await setFieldsValue(ret);
}
modalLoading(false);
},
);
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({
labelWidth: 100,
showActionButtonGroup: false,
baseColProps: { span: 24 },
schemas: modalSchemas,
});
async function handleSubmit() {
try {
modalLoading(true);
const data = await validate();
if (unref(isUpdate)) {
await RequirementProductAttributeUpdate(data);
} else {
await RequirementProductAttributeAdd(data);
}
emit('reload');
closeModal();
await resetForm();
} catch (e) {
} finally {
modalLoading(false);
}
}
</script>
<style scoped></style>

115
src/views/contractReview/RequirementProductAttribute/index.vue

@ -0,0 +1,115 @@
<template>
<PageWrapper dense>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button
@click="downloadExcel(RequirementProductAttributeExport, '产品属性要求数据', getForm().getFieldsValue())"
v-auth="'productManagement:RequirementProductAttribute:export'"
>导出</a-button
>
<a-button
type="primary"
danger
@click="multipleRemove(RequirementProductAttributeRemove)"
:disabled="!selected"
v-auth="'productManagement:RequirementProductAttribute:remove'"
>删除</a-button
>
<a-button
type="primary"
@click="handleAdd"
v-auth="'productManagement:RequirementProductAttribute:add'"
>新增</a-button
>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
stopButtonPropagation
:actions="[
{
label: '修改',
icon: IconEnum.EDIT,
type: 'primary',
ghost: true,
auth: 'productManagement:RequirementProductAttribute:edit',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
icon: IconEnum.DELETE,
type: 'primary',
danger: true,
ghost: true,
auth: 'productManagement:RequirementProductAttribute:remove',
popConfirm: {
placement: 'left',
title: '是否删除产品属性要求[' + record.id + ']?',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<RequirementProductAttributeModal @register="registerModal" @reload="reload" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { RequirementProductAttributeList, RequirementProductAttributeExport, RequirementProductAttributeRemove } from '@/api/contractReview/RequirementProductAttribute';
import { downloadExcel } from '@/utils/file/download';
import { useModal } from '@/components/Modal';
import RequirementProductAttributeModal from './RequirementProductAttributeModal.vue';
import { formSchemas, columns } from './RequirementProductAttribute.data';
import { IconEnum } from '@/enums/appEnum';
defineOptions({ name: 'RequirementProductAttribute' });
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({
rowSelection: {
type: 'checkbox',
},
title: '产品属性要求列表',
api: RequirementProductAttributeList,
showIndexColumn: false,
rowKey: 'id',
useSearchForm: true,
formConfig: {
schemas: formSchemas,
baseColProps: {
xs: 24,
sm: 24,
md: 24,
lg: 6,
},
},
columns: columns,
actionColumn: {
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
},
});
const [registerModal, { openModal }] = useModal();
function handleEdit(record: Recordable) {
openModal(true, { record, update: true });
}
function handleAdd() {
openModal(true, { update: false });
}
async function handleDelete(record: Recordable) {
await RequirementProductAttributeRemove([record.id]);
await reload();
}
</script>
<style scoped></style>

374
src/views/workbench/index.vue

@ -1,76 +1,344 @@
<template>
<PageWrapper>
<template #headerContent> <WorkbenchHeader /> </template>
<template #headerContent>
<WorkbenchHeader />
</template>
<div class="workbench">
<div class="h-1/4">
<div class="grid grid-cols-5 gap-4">
<div class="col-span-3 h-full">
<ProjectCard :loading="loading" class="enter-y" />
<!-- 平台信息卡片 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-4">
<a-card title="平台信息" :bordered="false" class="enter-y">
<template #extra>
<a-tag color="processing">系统状态: 正常运行</a-tag>
</template>
<div class="grid grid-cols-2 gap-4">
<div v-for="(item, index) in platformInfo" :key="index" class="flex items-start">
<div class="mr-2">
<component :is="item.icon" class="text-xl text-primary" />
</div>
<div>
<div class="text-secondary text-sm">{{ item.label }}</div>
<div class="font-medium">{{ item.value }}</div>
</div>
</div>
</div>
<div class="col-span-2 h-full">
<QuickNav :loading="loading" class="enter-y" />
</a-card>
<!-- 任务概况卡片 - 调整为更加饱满的布局 -->
<a-card title="任务概况" :bordered="false" class="enter-y task-stats-card">
<template #extra>
<a-button type="link" @click="refreshTaskStats">
<ReloadOutlined />
</a-button>
</template>
<div class="task-stats-container">
<!-- 第一行正在执行已完成总任务数 -->
<div class="task-stats-row">
<div v-for="(stat, index) in taskStatsRow1" :key="index" class="task-stat-item">
<div class="task-stat-title">{{ stat.title }}</div>
<div class="task-stat-value">
<component :is="stat.icon" :style="{color: stat.color}" class="task-stat-icon" />
<span :style="{color: stat.color}">{{ stat.value }}</span>
</div>
</div>
</div>
<!-- 第二行审核通过审核未通过 -->
<div class="task-stats-row second-row">
<div v-for="(stat, index) in taskStatsRow2" :key="index" class="task-stat-item review-stat">
<div class="task-stat-title">{{ stat.title }}</div>
<div class="task-stat-value">
<component :is="stat.icon" :style="{color: stat.color}" class="task-stat-icon" />
<span :style="{color: stat.color}">{{ stat.value }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="h-1/2 mt-4">
<Card title="任务看板" :bordered="false" style="width: 100%; height: 100%">
<template #extra>
<!-- 将按钮改为下拉菜单 -->
<Dropdown>
<a-button>
更多
<DownOutlined />
</a-button>
<template #overlay>
<Menu @click="handleMenuClick" mode="vertical">
<MenuItem v-for="item in groupItems"
:key="item.path"
>
{{ item.title }}
</MenuItem>
</Menu>
</template>
</Dropdown>
</template>
<DocumentTasksTable
:show-table-setting="false"
:show-toolbar="false"
:use-search-form="false"
:pagination="false"
/>
</Card>
</div>
</a-card>
</div>
<!-- 单一图表展示区域 - 使用 useECharts 钩子 -->
<div class="mb-4">
<a-card title="单位合同统计" :loading="loading" class="enter-y">
<div ref="chartRef" style="width: 100%; height: 320px;"></div>
</a-card>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ref, reactive, onMounted, watch, Ref } from 'vue';
import { PageWrapper } from '@/components/Page';
import WorkbenchHeader from './components/WorkbenchHeader.vue';
import { Card, Dropdown, Menu, MenuItem } from 'ant-design-vue';
import ProjectCard from './components/ProjectCard.vue';
import QuickNav from './components/QuickNav.vue';
import DocumentTasksTable from '@/views/documentReview/DocumentTasks/DocumentTasksTable.vue';
import { useRouter } from 'vue-router';
import { DownOutlined, UserOutlined } from '@ant-design/icons-vue';
import { groupItems } from './components/data';
import { useECharts } from '@/hooks/web/useECharts';
import {
ReloadOutlined,
ClockCircleOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
AppstoreOutlined,
CloudServerOutlined,
DatabaseOutlined,
HddOutlined,
LaptopOutlined,
RobotOutlined,
CodeOutlined
} from '@ant-design/icons-vue';
import { Card, Button, Tag, Radio } from 'ant-design-vue';
//
const ACard = Card;
const AButton = Button;
const ATag = Tag;
const ARadioGroup = Radio.Group;
const ARadioButton = Radio.Button;
const router = useRouter();
//
const loading = ref(false);
const handleMenuClick = ({ key }) => {
console.log(key);
router.push({ path: key });
//
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
//
const platformInfo = ref([
{
label: 'CPU',
value: 'Kunpeng 920 24Core@2.6GHz x2',
icon: CloudServerOutlined
},
{
label: 'NPU',
value: 'Atlas 300I Duo 96GB-150W x4',
icon: HddOutlined
},
{
label: '内存',
value: '64GB×8 DDR4',
icon: HddOutlined
},
{
label: '存储',
value: '3T SSD',
icon: DatabaseOutlined
},
{
label: '数据库',
value: 'OceanBase',
icon: DatabaseOutlined
},
{
label: '操作系统',
value: 'UOS Server 20',
icon: LaptopOutlined
},
{
label: '内置模型',
value: 'OCR: Qwen2-VL-7B / NLP: QwQ-32B',
icon: RobotOutlined
},
{
label: '模型框架',
value: 'MindIE/PyTorch',
icon: CodeOutlined
}
]);
//
const taskStatsRow1 = ref([
{
title: '正在执行',
value: 12,
icon: ClockCircleOutlined,
color: '#1890ff'
},
{
title: '已完成',
value: 56,
icon: CheckCircleOutlined,
color: '#52c41a'
},
{
title: '总任务数',
value: 78,
icon: AppstoreOutlined,
color: '#722ed1'
}
]);
const taskStatsRow2 = ref([
{
title: '审核通过',
value: 42,
icon: CheckCircleOutlined,
color: '#52c41a'
},
{
title: '审核未通过',
value: 14,
icon: CloseCircleOutlined,
color: '#f5222d'
}
]);
//
const contractChartOption = {
title: {
text: '',
},
tooltip: {
trigger: 'axis',
formatter: '{b}个单位: {c}份合同'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
name: '单位数',
nameLocation: 'middle',
nameGap: 30,
nameTextStyle: {
fontWeight: 'bold'
},
data: ['5', '10', '15', '20', '25', '30', '35']
},
yAxis: {
type: 'value',
name: '合同数',
nameLocation: 'middle',
nameGap: 40,
nameTextStyle: {
fontWeight: 'bold'
}
},
series: [
{
name: '合同数',
type: 'line',
smooth: true,
symbolSize: 8,
lineStyle: {
width: 3
},
itemStyle: {
color: '#1890ff'
},
data: [10, 22, 30, 42, 56, 68, 75]
}
]
};
// -
const renderChart = () => {
setOptions(contractChartOption);
};
//
const refreshTaskStats = () => {
loading.value = true;
//
setTimeout(() => {
taskStatsRow1.value[0].value += 2;
taskStatsRow1.value[1].value += 1;
taskStatsRow1.value[2].value += 3;
taskStatsRow2.value[0].value += 1;
taskStatsRow2.value[1].value += 0;
loading.value = false;
}, 800);
};
//
const fetchData = async () => {
loading.value = true;
// API
setTimeout(() => {
renderChart();
loading.value = false;
}, 1500);
};
//
const fetchTaskList = async () => {};
//
onMounted(() => {
fetchTaskList();
fetchData();
});
setTimeout(() => {
loading.value = false;
}, 1500);
</script>
<style lang="less" scoped>
.workbench {
margin-top: 12px;
}
.text-primary {
color: #1890ff;
}
.text-secondary {
color: rgba(0, 0, 0, 0.45);
}
.task-stats-card {
:deep(.ant-card-body) {
height: calc(100% - 58px);
padding: 16px;
}
}
.task-stats-container {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.task-stats-row {
display: flex;
justify-content: space-around;
width: 100%;
padding: 8px 0;
}
.second-row {
justify-content: space-evenly;
width: 90%;
margin: 0 auto;
}
.task-stat-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px 20px;
min-width: 110px;
}
.review-stat {
padding: 12px 36px;
}
.task-stat-title {
font-size: 15px;
color: rgba(0, 0, 0, 0.45);
margin-bottom: 8px;
white-space: nowrap;
}
.task-stat-value {
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
font-weight: bold;
}
.task-stat-icon {
font-size: 24px;
margin-right: 8px;
}
</style>

Loading…
Cancel
Save