diff --git a/package.json b/package.json index 9eba78c6512b7ca39601c4dd25d8226b0f4e2da1..404b72cf4ed2894f543920db93cf6034ce8087bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "知识库", - "version": "1.0.0", + "version": "0.10.1", "private": true, "type": "module", "scripts": { diff --git a/src/api/apiType.ts b/src/api/apiType.ts index 8fc4a640b5695a3deba6b2ab541525f0706ea686..420617fb1551d23b646e63b01dc5c10f41ada11c 100644 --- a/src/api/apiType.ts +++ b/src/api/apiType.ts @@ -1,21 +1,51 @@ export interface CreateKbRequest { - defaultChunkSize: number; - defaultParseMethod: string; + // 基本设置 + kbName: string; description: string; - docTypes: any[]; + // 解析设置 + defaultParseMethod: string; + chunkMethod: string; // semantic | mark + chunkIdentifier: string; // 分块标识符 + defaultChunkSize: number; + uploadCountLimit: number; + // 向量化设置 embeddingModel: string; - kbName: string; + tokenizer: string; + // 检索设置 + enableReranker: boolean; + rerankerModel: string; + topK: number; + enableCompression: boolean; + enableDocumentCategory: boolean; + enableContextAssociation: boolean; + // 其他 + docTypes: any[]; [property: string]: any; } export interface UpdateKbRequest { - defaultChunkSize: number; - defaultParseMethod: string; + // 基本设置 + kbName: string; description: string; - docTypes: object[]; + // 解析设置 + defaultParseMethod: string; + chunkMethod: string; // semantic | mark + chunkIdentifier: string; // 分块标识符 + defaultChunkSize: number; + uploadCountLimit: number; + // 向量化设置 embeddingModel: string; + tokenizer: string; + // 检索设置 + enableReranker: boolean; + rerankerModel: string; + topK: number; + enableCompression: boolean; + enableDocumentCategory: boolean; + enableContextAssociation: boolean; + // 其他 + docTypes: object[]; teamId?: string; - kbName: string; [property: string]: any; } diff --git a/src/api/group.ts b/src/api/group.ts index 0968bb8c74dbcdd0347c75d7fe18d282b4484fa8..ff24b7ef911f441098806a8a4fd4357e8407293b 100644 --- a/src/api/group.ts +++ b/src/api/group.ts @@ -37,6 +37,189 @@ class GroupAPI { params: data, }); } + + /** 获取团队用户列表接口*/ + static getTeamUsers(data: { + teamId: string; + userSub?: string; + userName?: string; + page: number; + pageSize: number; + }) { + return request({ + url: `/team/usr`, + method: 'post', + data, + }); + } + + /** 获取团队消息/动态接口*/ + static getTeamMessages(data: { + teamId: string; + page: number; + pageSize: number; + language: string; + }) { + return request({ + url: `/team/msg`, + method: 'post', + data, + }); + } + + /** 获取当前用户角色信息接口*/ + static getCurrentUserRole(teamId: string) { + return request({ + url: `/role`, + method: 'get', + params: { teamId }, + }); + } + + /** 获取角色列表接口*/ + static getRoleList(data: { + teamId: string; + isEditable: boolean; + page: number; + pageSize: number; + language: string; + }) { + return request({ + url: `/role/list`, + method: 'post', + data, + }); + } + + /** 获取角色权限操作列表接口*/ + static getRoleActions(language?: string) { + return request({ + url: `/role/action`, + method: 'get', + params: language ? { language } : undefined, + }); + } + + /** 创建角色接口*/ + static createRole(data: { + roleName: string; + actions: string[]; + }, teamId: string) { + return request({ + url: `/role`, + method: 'post', + params: { teamId }, + data, + }); + } + + /** 更新角色接口*/ + static updateRole(data: { + roleName: string; + actions: string[]; + }, roleId: string) { + return request({ + url: `/role`, + method: 'put', + params: { roleId }, + data, + }); + } + + /** 删除角色接口*/ + static deleteRole(roleId: string) { + return request({ + url: `/role`, + method: 'delete', + params: { roleId }, + }); + } + + /** 批量删除角色接口*/ + static batchDeleteRoles(roleIds: string[]) { + // 如果后端支持批量删除,可以使用这个接口 + // 否则前端循环调用单个删除接口 + const deletePromises = roleIds.map(roleId => this.deleteRole(roleId)); + return Promise.all(deletePromises); + } + + /** 删除团队成员接口*/ + static deleteTeamMember(data: { + teamId: string; + userSub: string; + }) { + return request({ + url: `/team/usr`, + method: 'delete', + data, + }); + } + + /** 批量删除团队成员接口*/ + static batchDeleteTeamMembers(data: { + teamId: string; + userSubs: string[]; + }) { + return request({ + url: `/team/usr/batch`, + method: 'delete', + data, + }); + } + + /** 更新团队成员角色接口*/ + static updateMemberRole(data: { + teamId: string; + targetUserSub: string; + roleId: string; + }) { + return request({ + url: `/team/usr/role`, + method: 'put', + data, + }); + } + + /** 邀请团队成员接口*/ + static inviteMembers(data: { + teamId: string; + members: Array<{ + userSub: string; + roleName: string; + }>; + }) { + return request({ + url: `/team/usr/invite`, + method: 'post', + data, + }); + } + + /** 移交团队所有权接口*/ + static transferTeamOwnership(data: { + teamId: string; + newOwnerUserSub: string; + }) { + return request({ + url: `/team/transfer`, + method: 'post', + data, + }); + } + + /** 获取用户列表接口(用于邀请成员)*/ + static getUserList(data: { + userSub?: string; + userName?: string; + page: number; + pageSize: number; + }) { + return request({ + url: `/user/list`, + method: 'post', + data, + }); + } } export default GroupAPI; diff --git a/src/api/kbApp.ts b/src/api/kbApp.ts index 2bb90b5c7c711990a1df243a74f6380be455ddad..2e9d1e1a79a8b7e6cb72e81dbc1ab78c7efcf94f 100644 --- a/src/api/kbApp.ts +++ b/src/api/kbApp.ts @@ -167,6 +167,14 @@ class KbAppAPI { }); } + /** 获取Reranker模型列表 */ + static queryRerankerList() { + return request({ + url: `/other/reranker`, + method: 'get', + }); + } + /** 是否启用文件*/ static addUserModel(data: ModelForm) { return request({ diff --git a/src/assets/fonts/HarmonyOS/HarmonyOS_font.css b/src/assets/fonts/HarmonyOS/HarmonyOS_font.css index b03886c71bf1435dac35e43aa77b6796e83f9ece..33dc6afe2667e85ff77391cf07a42ea6f62626b9 100644 --- a/src/assets/fonts/HarmonyOS/HarmonyOS_font.css +++ b/src/assets/fonts/HarmonyOS/HarmonyOS_font.css @@ -14,8 +14,8 @@ @font-face { font-family: 'HarmonyOS Sans SC Regular'; src: - url(./HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff2) format('woff2'), - url(./HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff) format('woff'); + var(--font-harmony-woff2, url(./HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff2)) format('woff2'), + var(--font-harmony-woff, url(./HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff)) format('woff'); font-weight: 400; font-style: normal; } diff --git a/src/assets/svg/default_avatar.svg b/src/assets/svg/default_avatar.svg new file mode 100644 index 0000000000000000000000000000000000000000..6bfbe09371269d9ea2053187967b70c71a198619 --- /dev/null +++ b/src/assets/svg/default_avatar.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/InviteMemberDialog/MemberCard.vue b/src/components/InviteMemberDialog/MemberCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..0e6def723d98b102ba3903674a0540e87571985a --- /dev/null +++ b/src/components/InviteMemberDialog/MemberCard.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/src/components/InviteMemberDialog/index.vue b/src/components/InviteMemberDialog/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..68dd4ac6b41d3d980e37dc497103b237c041b9d0 --- /dev/null +++ b/src/components/InviteMemberDialog/index.vue @@ -0,0 +1,373 @@ + + + + + diff --git a/src/components/KnowledgeForm/index.vue b/src/components/KnowledgeForm/index.vue index 2e4fe49bd163a0dd9e78d1044d0074fb91ace830..bfbd7d29f26336939d4d673eb57c370136f2cf36 100644 --- a/src/components/KnowledgeForm/index.vue +++ b/src/components/KnowledgeForm/index.vue @@ -1,233 +1,300 @@ \ No newline at end of file + + + \ No newline at end of file diff --git a/src/components/LangSelect/index.vue b/src/components/LangSelect/index.vue index ef04637eaf9739acd6fcde328fda9d7a4a3c7030..5a29f8301c400ca8606124982b4b8b3da546487b 100644 --- a/src/components/LangSelect/index.vue +++ b/src/components/LangSelect/index.vue @@ -8,10 +8,10 @@
+ :src="getImageUrl('language-zh.svg')" /> + :src="getImageUrl('language-en.svg')" />
{{ languageText }} @@ -38,9 +38,11 @@ import { LanguageEnum } from '@/enums/LanguageEnum'; import defaultSettings from '@/settings'; import AuthAPI from '@/api/auth'; import { IconSuccess } from '@computing/opendesign-icons'; +import { useAssets } from '@/composables/useAssets'; const languageText = ref(); const language = ref(); const userInfo = ref(); +const { getImageUrl } = useAssets(); defineProps({ size: { type: String, diff --git a/src/components/RoleDialog/PermissionGroupCard.vue b/src/components/RoleDialog/PermissionGroupCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..e758f76024738082d9ae942a3a9e92d506e6213f --- /dev/null +++ b/src/components/RoleDialog/PermissionGroupCard.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/src/components/RoleDialog/index.vue b/src/components/RoleDialog/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..bbf27125bebfaab3b6ddc1a90128ef570e91ee44 --- /dev/null +++ b/src/components/RoleDialog/index.vue @@ -0,0 +1,535 @@ + + + + + diff --git a/src/composables/useAssets.ts b/src/composables/useAssets.ts new file mode 100644 index 0000000000000000000000000000000000000000..88b09b2bd7d0ead6d25a75899a868abc01abdff8 --- /dev/null +++ b/src/composables/useAssets.ts @@ -0,0 +1,49 @@ +/** + * 静态资源处理组合式函数 + */ +import { resolveAssetUrl } from '@/utils'; + +export function useAssets() { + /** + * 解析静态资源 URL + * @param assetPath 静态资源路径 + * @returns 处理后的完整 URL + */ + const resolveAsset = (assetPath: string): string => { + return resolveAssetUrl(assetPath); + }; + + /** + * 批量解析静态资源 URL + * @param assetPaths 静态资源路径数组 + * @returns 处理后的完整 URL 数组 + */ + const resolveAssets = (assetPaths: string[]): string[] => { + return assetPaths.map(path => resolveAssetUrl(path)); + }; + + /** + * 获取图片 URL + * @param imagePath 图片路径 + * @returns 处理后的图片 URL + */ + const getImageUrl = (imagePath: string): string => { + return resolveAssetUrl(`/assets/images/${imagePath}`); + }; + + /** + * 获取 SVG 图标 URL + * @param iconPath SVG 图标路径 + * @returns 处理后的图标 URL + */ + const getSvgUrl = (iconPath: string): string => { + return resolveAssetUrl(`/assets/svg/${iconPath}`); + }; + + return { + resolveAsset, + resolveAssets, + getImageUrl, + getSvgUrl + }; +} diff --git a/src/lang/package/en.ts b/src/lang/package/en.ts index 48378f384a19b21cdb00790facce903d3936e5a2..e4b24970ea70c35a42da0052ab62d33da2ef8fd9 100644 --- a/src/lang/package/en.ts +++ b/src/lang/package/en.ts @@ -69,6 +69,7 @@ export default { teamAuth: 'Team Permission', isPublic: 'isPublic', creator: 'Creator', + owner: 'Owner', createTime: 'Create Time', noData: 'No data.', teamDetail: 'Team Details', @@ -79,10 +80,18 @@ export default { create: 'Create', }, groupDetail: { + memberPermission: 'Member Management', teamSet: 'Team Settings', teamNews: 'Team News', - roleManage: 'Role Management', + roleManage: 'Role/Permission', MemberAuthority: 'Member/Authority', + transferTeam: 'Transfer Team', + transfer: 'Transfer', + transferTip: 'Transfer team ownership to other members', + transferDialog: 'Transfer Team', + selectReceiver: 'Select Receiver', + pleaseSelectReceiver: 'Please select receiver', + transferMessage: 'Transfer team ownership to other members', dissolveTeam: 'Dissolve Team', dissolve: 'Dissolve', dissolveTip: @@ -95,6 +104,132 @@ export default { createTeamSuccess: 'Team created successfully.', updateTeamSuccess: 'Team updated successfully.', inviteMember: 'Invite Member', + placeholderMember: 'Enter member ID/name', + memberId: 'Member ID', + memberName: 'Member Name', + roleAuth: 'Role', + // Role management related + createRole: 'Create Role', + batchDeleteRole: 'Batch Delete', + roleFilter: 'Enter role name', + roleName: 'Role Name', + permissionAssignment: 'Permission Assignment', + createTime: 'Create Time', + operation: 'Operation', + editRole: 'Edit', + deleteRole: 'Delete', + // Member management related + batchDeleteMember: 'Batch Delete', + memberFilter: 'Enter member ID/name', + editMember: 'Edit', + deleteMember: 'Delete', + // Role dialog + newRole: 'Create Role', + editRoleTitle: 'Edit Role', + roleNamePlaceholderEdit: 'Role name', + roleNamePlaceholderNew: 'Custom role', + permissionAllocation: 'Permission Allocation', + restoreDefault: 'Restore Default', + reset: 'Reset', + teamPermission: 'Team', + assetPermissions: 'Asset', + knowledgePermission: 'Knowledge', + documentPermission: 'Document', + datasetPermission: 'Dataset', + testingPermission: 'Testing', + evaluationPermissions: 'Evaluation', + modifyTeamInfo: 'Modify team info', + inviteMembers: 'Invite members', + manageRoles: 'Manage roles', + createKnowledge: 'Create knowledge', + editKnowledge: 'Edit knowledge', + deleteKnowledge: 'Delete knowledge', + uploadDocument: 'Upload document', + editDocument: 'Edit document', + deleteDocument: 'Delete document', + createDataset: 'Create dataset', + editDataset: 'Edit dataset', + deleteDataset: 'Delete dataset', + createTesting: 'Create testing', + editTesting: 'Edit testing', + deleteTesting: 'Delete testing', + // Role dialog related + newRoleDialog: 'Create Role', + editRoleDialog: 'Edit Role', + roleNameLabel: 'Role Name', + roleNamePlaceholder: 'Custom role', + permissionConfig: 'Permission Config', + resetToDefault: 'Restore Default', + resetToInitial: 'Reset', + roleCreateSuccess: 'Role created successfully', + roleEditSuccess: 'Role edited successfully', + roleCreateFailed: 'Role creation failed', + roleEditFailed: 'Role edit failed', + roleNameRequired: 'Please enter role name', + roleNameLength: 'Role name length should be 1-20 characters', + // Specific permission items + permModifyTeamInfo: 'Modify team info', + permInviteMembers: 'Invite members', + permManageRoles: 'Manage roles', + permViewAsset: 'View asset content', + permImportAsset: 'Import asset', + permExportAsset: 'Export asset', + permCreateAsset: 'Create asset', + permModifyAsset: 'Modify asset info', + permDeleteAsset: 'Delete asset', + permUploadDocument: 'Upload document', + permImportDocument: 'Import document', + permDeleteDocument: 'Delete document', + permModifyDocument: 'Modify document info', + permEditDocument: 'Edit document content', + permDownloadDocument: 'Download document', + permViewDataset: 'View dataset details', + permGenerateDataset: 'Generate dataset', + permExportDataset: 'Export dataset', + permDeleteDataset: 'Delete dataset', + permModifyDataset: 'Modify dataset info', + permEditDataset: 'Edit dataset content', + permCreateTesting: 'Create testing', + permViewTesting: 'View testing', + permDeleteTesting: 'Delete testing', + permModifyTesting: 'Modify testing info', + permDownloadReport: 'Download report', + permViewTask: 'View evaluation task details', + // Team permission items + permModifyRoleInfo: 'Modify role info', + permDeleteMembers: 'Delete members', + permModifyMemberRoles: 'Modify member roles', + permAddRoles: 'Add roles', + permTransferTeam: 'Transfer team', + permDissolveTeam: 'Dissolve team', + // Asset permission items + permAccessAsset: 'Access asset content', + // Document permission items + permAccessDocumentAnalysis: 'Access document analysis results', + permParseDocument: 'Parse document', + permBatchUseDocument: 'Enable/disable document', + permBatchParseDocument: 'Enable/disable document analysis results', + // Member role editing related + pleaseSelectRole: 'Please select role', + confirmChangeRole: 'Confirm Role Change', + // Invite member related + addMember: 'Add', + pendingMembers: 'Pending Members', + defaultRole: 'member', + // Dataset permission items + permAccessDatasetDetails: 'Access dataset details', + permExportImport: 'Import/export', + permModifySingleData: 'Modify single data', + // Testing permission items + permCreateTest: 'Create test', + permViewTest: 'View test', + permDeleteTest: 'Delete test', + permModifyTest: 'Modify test', + // NoData + noPermissionGroups: 'No Permissions Could be Assigned', + // Role deletion related messages + systemRoleCannotDelete: 'This role is a system fixed role and cannot be deleted', + confirmDeleteRole: 'Are you sure to delete role {roleName}?', }, assetLibrary: { assetLibrary: 'Asset Libraries', @@ -121,6 +256,8 @@ export default { embeddedModel: 'Embedded Model', analyticMethod: 'Parsing Method', fileChunkSize: 'File Block Size', + uploadCountLimit: 'Max documents per upload', + character: 'character', configCategory: 'Document Categories', fileConfigCategory: 'Document Information Categories', supAddCategoris: 'A maximum of 10 document categories can be added.', @@ -385,5 +522,22 @@ export default { datasetDesc: 'This is a dataset description', testingName: 'Test Data 01', }, + chunkMethod: { + chunkSettings: 'Chunk Settings', + chunkMethod: 'Chunk Method', + default: 'Default', + customDelimiter: 'Custom Delimiter', + chunkIdentifier: 'Chunk Identifier', + pleaseSelectChunkMethod: 'Please select chunk method', + pleaseEnterChunkIdentifier: 'Please enter chunk identifier', + }, + tabs: { + basicSettings: 'Basic Settings', + parseSettings: 'Parse Settings', + retrievalSettings: 'Retrieval Settings', + }, + vectorization: { + vectorizationSettings: 'Vectorization Settings', + }, copyright: 'Copyright© Huawei Software Technologies Co., Ltd.2024. All rights reserved.', }; diff --git a/src/lang/package/zh-cn.ts b/src/lang/package/zh-cn.ts index 6c7a273ed2ec2505530c01e55ba3b0ddd4da82f5..3d5e895110ce188c3cdffe6248cd4a7244999377 100644 --- a/src/lang/package/zh-cn.ts +++ b/src/lang/package/zh-cn.ts @@ -75,6 +75,7 @@ export default { teamAuth: '团队权限', isPublic: '是否公开', creator: '创建人', + owner: '所有者', createTime: '创建时间', noData: '暂无数据', teamDetail: '团队详情', @@ -85,10 +86,17 @@ export default { create: '创建', }, groupDetail: { - teamSet: '团队设置', + memberPermission: '成员管理', + roleManage: '角色/权限', teamNews: '团队动态', - roleManage: '角色管理', - MemberAuthority: '成员/权限', + teamSet: '团队设置', + transferTeam: '移交团队', + transfer: '移交', + transferTip: '将团队的所有者权限移交给其他成员', + transferDialog: '移交团队', + selectReceiver: '选择接收人', + pleaseSelectReceiver: '请选择接收人', + transferMessage: '将团队的所有者权限移交给其他成员', dissolveTeam: '解散团队', dissolve: '解散', dissolveTip: '解散团队后相关资源都会被释放,请谨慎操作', @@ -103,7 +111,134 @@ export default { placeholderMember: '请输入成员ID/姓名', memberId: '成员ID', memberName: '成员名称', - roleAuth: '角色权限', + roleAuth: '角色', + // 角色管理相关 + createRole: '新建角色', + batchDeleteRole: '批量删除', + roleFilter: '请输入角色名称', + roleName: '角色名称', + permissionAssignment: '权限分配', + createTime: '创建时间', + operation: '操作', + editRole: '编辑', + deleteRole: '删除', + // 成员管理相关 + batchDeleteMember: '批量删除', + memberFilter: '请输入成员ID/名称', + editMember: '编辑', + deleteMember: '删除', + // 角色对话框 + newRole: '新建角色', + editRoleTitle: '编辑角色', + roleNamePlaceholderEdit: '角色名称', + roleNamePlaceholderNew: '自定义角色', + permissionAllocation: '权限分配', + restoreDefault: '恢复默认', + reset: '重置', + teamPermission: '团队', + assetPermissions: '资产', + knowledgePermission: '知识库', + documentPermission: '文档管理', + datasetPermission: '数据集管理', + testingPermission: '评测管理', + evaluationPermissions: '评估任务', + modifyTeamInfo: '修改团队信息', + inviteMembers: '邀请成员', + manageRoles: '管理角色', + createKnowledge: '创建知识库', + editKnowledge: '编辑知识库', + deleteKnowledge: '删除知识库', + uploadDocument: '上传文档', + editDocument: '编辑文档', + deleteDocument: '删除文档', + createDataset: '创建数据集', + editDataset: '编辑数据集', + deleteDataset: '删除数据集', + createTesting: '创建评测', + editTesting: '编辑评测', + deleteTesting: '删除评测', + // 角色对话框相关 + newRoleDialog: '新建角色', + editRoleDialog: '编辑角色', + roleNameLabel: '角色名称', + roleNamePlaceholder: '自定义角色', + permissionConfig: '权限配置', + resetToDefault: '恢复默认', + resetToInitial: '重置', + roleCreateSuccess: '角色创建成功', + roleEditSuccess: '角色编辑成功', + roleCreateFailed: '角色创建失败', + roleEditFailed: '角色编辑失败', + roleNameRequired: '请输入角色名称', + roleNameLength: '角色名称长度为1-20个字符', + // 权限组名称 + teamPermissions: '团队', + documentPermissions: '文档', + datasetPermissions: '数据集', + testingPermissions: '评测管理', + // 具体权限项 + permModifyTeamInfo: '修改团队信息', + permInviteMembers: '邀请成员', + permManageRoles: '管理角色', + permViewAsset: '查询资产内容', + permImportAsset: '导入资产', + permExportAsset: '导出资产', + permCreateAsset: '创建资产', + permModifyAsset: '修改资产或信息', + permDeleteAsset: '删除资产', + permUploadDocument: '上传文档到信息新增', + permImportDocument: '导入文档', + permDeleteDocument: '删除文档', + permModifyDocument: '修改文档信息', + permEditDocument: '编辑文档内容', + permDownloadDocument: '下载文档', + permViewDataset: '访问数据集数据详情', + permGenerateDataset: '生成数据集', + permExportDataset: '导出数据集', + permDeleteDataset: '删除数据集', + permModifyDataset: '修改数据集信息', + permEditDataset: '编辑数据集内容', + permCreateTesting: '创建数据集评测', + permViewTesting: '查看数据集评测', + permDeleteTesting: '删除数据集评测', + permModifyTesting: '修改数据集评测信息', + permDownloadReport: '下载数据集评测报告', + permViewTask: '查询评估任务详情', + // 团队权限项 + permModifyRoleInfo: '修改角色信息', + permDeleteMembers: '删除成员', + permModifyMemberRoles: '修改成员角色', + permAddRoles: '新增角色', + permTransferTeam: '移交团队', + permDissolveTeam: '解散团队', + // 资产权限项 + permAccessAsset: '访问资产内容', + // 文档权限项 + permAccessDocumentAnalysis: '访问文档解析结果', + permParseDocument: '解析文档', + permBatchUseDocument: '启用/禁用文档', + permBatchParseDocument: '启用/禁用文档解析结果', + // 成员角色编辑相关 + pleaseSelectRole: '请选择角色', + confirmChangeRole: '确认修改角色', + // 邀请成员相关 + addMember: '添加', + pendingMembers: '待邀请成员', + defaultRole: '成员', + // 数据集权限项 + permAccessDatasetDetails: '访问数据集详情', + permExportImport: '导入/导出', + permModifySingleData: '修改单条数据', + // 评测权限项 + permCreateTest: '创建测试', + permViewTest: '查看测试', + permDeleteTest: '删除测试', + permModifyTest: '修改测试', + // NoData + noPermissionGroups: '无可配置权限', + // 角色删除相关提示 + systemRoleCannotDelete: '该角色为系统固定角色,不可删除', + confirmDeleteRole: '确定删除角色 {roleName} 吗?', }, assetLibrary: { assetLibrary: '资产库', @@ -130,6 +265,8 @@ export default { embeddedModel: '嵌入模型', analyticMethod: '文档解析器', fileChunkSize: '文件分块大小', + uploadCountLimit: '单次文档上传个数上限', + character: '字符', configCategory: '文档类别', fileConfigCategory: '文档信息类别', supAddCategoris: '最多支持添加10个文档信息类别', @@ -389,5 +526,22 @@ export default { datasetDesc: '这是一段数据集简介', testingName: '测试数据01', }, + chunkMethod: { + chunkSettings: '分块设置', + chunkMethod: '分块方法', + default: '默认', + customDelimiter: '自定义分隔符', + chunkIdentifier: '分块标识符', + pleaseSelectChunkMethod: '请选择分块方法', + pleaseEnterChunkIdentifier: '请输入分块标识符', + }, + tabs: { + basicSettings: '基本设置', + parseSettings: '解析设置', + retrievalSettings: '检索设置', + }, + vectorization: { + vectorizationSettings: '向量化设置', + }, copyright: '版权所有© 华为软件技术有限公司2024。保留一切权利', }; diff --git a/src/main.ts b/src/main.ts index ae30564fb1998b419ea6fb84988a8a9cef8a3def..0f0a0cd0158c298a7bee2c0e999217e4127839d4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import '@computing/opendesign2/themes/es/css'; import zhCn from 'element-plus/es/locale/lang/zh-cn'; import App from './App.vue'; import setupPlugins from '@/plugins'; +import { initCssAssetResolver } from '@/utils/cssAssetResolver'; // 本地SVG图标 import 'virtual:svg-icons-register'; @@ -30,4 +31,8 @@ app.use(ElementPlus, { }); app.use(setupPlugins); app.use(opendesign2); + +// 初始化 CSS 静态资源解析器 +initCssAssetResolver(); + app.mount('#app'); diff --git a/src/plugins/assetResolver.ts b/src/plugins/assetResolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..3bcddebfcb7b8b1326f8589096b4fd49f16c0157 --- /dev/null +++ b/src/plugins/assetResolver.ts @@ -0,0 +1,19 @@ +/** + * 静态资源解析插件 + * 用于解决 iframe 环境下的跨域静态资源访问问题 + */ +import type { App } from 'vue'; +import { resolveAssetUrl } from '@/utils'; + +export default { + install(app: App) { + // 全局属性:解析静态资源 URL + app.config.globalProperties.$resolveAsset = resolveAssetUrl; + + // 全局方法:提供给组合式 API 使用 + app.provide('resolveAsset', resolveAssetUrl); + } +}; + +// 导出工具函数供直接使用 +export { resolveAssetUrl }; diff --git a/src/plugins/cssAssetResolver.ts b/src/plugins/cssAssetResolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..a00ce14c50326a1f5b9ecc8a11ba8da6d6edf528 --- /dev/null +++ b/src/plugins/cssAssetResolver.ts @@ -0,0 +1,113 @@ +/** + * CSS 静态资源解析插件 + * 用于在 iframe 环境下动态替换 CSS 中的静态资源 URL + */ +import type { Plugin } from 'vite'; + +export function cssAssetResolverPlugin(): Plugin { + return { + name: 'css-asset-resolver', + generateBundle(options, bundle) { + // 在生产构建时处理 CSS 文件 + Object.keys(bundle).forEach(fileName => { + const chunk = bundle[fileName]; + if (chunk.type === 'asset' && fileName.endsWith('.css')) { + let cssContent = chunk.source as string; + + // 替换 CSS 中的静态资源路径 + cssContent = cssContent.replace( + /url\(['"]?\/src\/assets\/(.*?)['"]?\)/g, + "url('#{INTEGRATION_ORIGIN}#{BASE_URL}assets/$1')" + ); + + cssContent = cssContent.replace( + /url\(['"]?@\/assets\/(.*?)['"]?\)/g, + "url('#{INTEGRATION_ORIGIN}#{BASE_URL}assets/$1')" + ); + + chunk.source = cssContent; + } + }); + }, + transformIndexHtml: { + enforce: 'post', + transform(html) { + // 在 HTML 中注入动态替换脚本 + const script = ` +`; + + return html.replace('', script + ''); + } + } + }; +} diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 9aafdebe8a6c5cccbd45e22b485e61d0b33c861c..65b9ab1ed55677adcc10c05a4a051fc688f2dfcb 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -3,6 +3,7 @@ import { setupRouter } from '@/router'; import { setupStore } from '@/store'; import type { App } from 'vue'; import { setupElIcons } from './icons'; +import assetResolver from './assetResolver'; export default { install(app: App) { @@ -14,5 +15,7 @@ export default { setupElIcons(app); // 状态管理(store) setupStore(app); + // 静态资源解析器 + app.use(assetResolver); }, }; diff --git a/src/styles/app.scss b/src/styles/app.scss index 4f3f02b6a5839efafacccd7b0af950097a1a2a3c..61260a411d7c06274a85327ccd58622702ec9807 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -14,7 +14,7 @@ body, width: 100%; min-width: 600px; overflow-y: auto; - background: url('/src/assets/images/login-background-light.webp') center/100% 100%; + background: var(--login-bg-light, url('/src/assets/images/login-background-light.webp')) center/100% 100%; } .main-container { @@ -28,5 +28,5 @@ body, } html.dark .euler-copilot-container { - background: url('@/assets/images/login-background-dark.jpg') no-repeat center right; + background: var(--login-bg-dark, url('@/assets/images/login-background-dark.jpg')) no-repeat center right; } diff --git a/src/styles/dataSet.scss b/src/styles/dataSet.scss index 82bc77e70945a737a9bd088d815051f31c016c7e..86db879596073879adcf5e8f58bbc5236b88f269 100644 --- a/src/styles/dataSet.scss +++ b/src/styles/dataSet.scss @@ -138,7 +138,7 @@ background-repeat: no-repeat !important; background-size: 100% !important; display: inline-block; - background: url('/src/assets/icons/taskLoading.png'); + background: var(--task-loading, url('/src/assets/icons/taskLoading.png')); } .icon-box { @@ -333,7 +333,7 @@ background-repeat: no-repeat !important; background-size: 100% !important; display: inline-block; - background: url('/src/assets/icons/taskLoading.png'); + background: var(--task-loading, url('/src/assets/icons/taskLoading.png')); } .icon-box { diff --git a/src/styles/grouInfo.scss b/src/styles/grouInfo.scss index ef9951de0a4b0d2b5393d225be34f5f821be765a..e8a7104ded71e23b76f706ac6c5661f60b372c6b 100644 --- a/src/styles/grouInfo.scss +++ b/src/styles/grouInfo.scss @@ -85,7 +85,6 @@ } .group-info-content{ width: 100%; - height: calc(100vh - 88px); border-radius: 8px; background-color: var(--o-bg-color-base); margin-top: 24px; diff --git a/src/styles/group.scss b/src/styles/group.scss index cf2eb4cda137050878f2513fe7182698e6127812..b5bf4424e0db1129c477ac504318bcd67df297f5 100644 --- a/src/styles/group.scss +++ b/src/styles/group.scss @@ -24,7 +24,7 @@ justify-content: space-between; .group-btn { border-radius: 4px !important; - width: 96px; + min-width: 96px; background-color: rgb(99, 149, 253); } .group-right-btn { @@ -101,8 +101,6 @@ } .group-tabs-content { margin-top: 16px; - display: flex; - justify-content: space-between; display: grid; grid-template-columns: repeat(auto-fill, 339px); gap: 16px; diff --git a/src/styles/knowledgeFile.scss b/src/styles/knowledgeFile.scss index 044d1187a661a9687426def08c0597a1ee7bf044..e2a7f1a0a632bb57e525910aaabf5f9c75fef300 100644 --- a/src/styles/knowledgeFile.scss +++ b/src/styles/knowledgeFile.scss @@ -256,7 +256,7 @@ background-repeat: no-repeat !important; background-size: 100% !important; display: inline-block; - background: url('/src/assets/icons/taskLoading.png'); + background: var(--task-loading, url('/src/assets/icons/taskLoading.png')); } .icon-box { diff --git a/src/styles/knowledgeForm.scss b/src/styles/knowledgeForm.scss index e7d50e784847e2557cc32553d1baf627db65b68b..e9a2b495f3cdb116621793f0c934b7111dd97aa9 100644 --- a/src/styles/knowledgeForm.scss +++ b/src/styles/knowledgeForm.scss @@ -18,7 +18,7 @@ } .deleteConfig:hover { - color: rgb(99, 149, 253); + color: var(--o-color-primary); } } } @@ -36,8 +36,8 @@ } .el-form-item { - gap: 24px; - margin-bottom: 24px; + gap: 20px; + margin-bottom: 20px !important; // 统一间距 display: flex; align-items: center; @@ -65,7 +65,7 @@ .icon-help{ margin-left: 4px; margin-top: 0px !important; - color: #8d98aa; + color: var(--o-text-color-secondary); } } @@ -76,7 +76,7 @@ .el-input-number__decrease { background: transparent; .el-icon { - color: #8d98aa; + color: var(--o-text-color-secondary); } } .el-input__wrapper { @@ -98,12 +98,13 @@ } .el-form-item__content { - flex: unset !important; + max-width: 450px; // 防止内容超出边界 .el-input, .el-select__wrapper, .el-textarea__inner { width: 352px !important; + max-width: 352px; // 确保不会超出 font-size: 12px; } @@ -136,6 +137,8 @@ padding: 0 !important; font-size: 12px !important; color: var(--o-text-color-secondary) !important; + min-width: 140px; // 确保label有足够宽度,避免换行 + white-space: nowrap; // 防止文字换行 .el-icon { margin-top: 2px; @@ -144,7 +147,7 @@ } .el-icon:hover { - color: rgb(82 163 255); + color: var(--o-color-primary); } } @@ -198,47 +201,148 @@ margin-left: 8px; font-size: 12px; line-height: 22px; - color: rgb(141 152 170); + color: var(--o-text-color-secondary); } } .kl-ruleForm{ .el-form-item__label{ - max-width: 128px; + min-width: 140px; // 确保label宽度一致 + max-width: 140px; } .config-form{ margin-bottom: 8px !important; } - & :nth-last-child(2) { - margin-bottom: 0 !important; + + // 确保选择器不超出对话框边界 + .el-form-item__content { + .el-input, + .el-select__wrapper { + width: 320px !important; // 在对话框中使用更小的宽度 + max-width: 320px !important; + } } } .analyticMethodSelect { box-sizing: border-box; - .el-select-dropdown { - // width: 100% !important; - // max-width: unset !important; - } .el-select-dropdown__item.is-hovering { color: #fff; } } -.create-dialog { - height: 600px; - position: relative; - padding: 0 !important; - overflow: hidden; +// 自适应高度对话框 - 重写CSS变量 +.adaptive-height-dialog { + // 覆盖所有可能的CSS变量 + --el-dialog-height: auto !important; + --el-dialog-max-height: 90vh !important; + --o-dialog-body-max-height: none !important; + --el-dialog-body-max-height: none !important; + --o-dialog-max-height: 90vh !important; + + // 强制覆盖Element Plus的内联样式和计算样式 + .el-dialog__body { + max-height: none !important; + height: auto !important; + overflow: visible !important; + } +} +.el-overlay .adaptive-height-dialog, +.adaptive-height-dialog.el-dialog, +div.adaptive-height-dialog { + height: auto !important; // 强制覆盖固定高度 + max-height: 90vh !important; // 最大高度为视口的90% + min-height: auto !important; // 自适应最小高度 + .el-dialog__body { - height: 464px !important; - max-height: unset !important; - min-height: unset !important; - padding-left: 16px !important; + height: auto !important; // 强制覆盖固定高度 + max-height: none !important; // 移除最大高度限制,让内容完全展开 + min-height: auto !important; + padding: 16px 20px 20px 16px !important; // 增加底部padding margin-right: 0 !important; - padding-bottom: 0; + overflow: visible !important; // 让内容完全可见 + + // 在body层面也覆盖CSS变量 + --o-dialog-body-max-height: none !important; + --el-dialog-body-max-height: none !important; + } + + .el-dialog__header { + padding-bottom: 16px !important; + } +} + +// 更强的覆盖,针对Element Plus的内部类 +.el-dialog.adaptive-height-dialog, +.el-dialog__wrapper .adaptive-height-dialog { + height: auto !important; + max-height: 90vh !important; + + .el-dialog__body { + max-height: none !important; + overflow: visible !important; + height: auto !important; + } +} + +// 最强选择器 - 直接覆盖Element Plus的计算样式 +.el-overlay-dialog .adaptive-height-dialog .el-dialog__body, +.adaptive-height-dialog .el-dialog__body, +div[class*="adaptive-height-dialog"] .el-dialog__body { + max-height: none !important; + overflow: visible !important; + height: auto !important; + + // 重置所有相关CSS变量 + --o-dialog-body-max-height: none !important; + --el-dialog-body-max-height: none !important; +} + +// 保留原有的create-dialog样式作为备用 +.create-dialog { + position: relative; + padding: 0 !important; + + // 在对话框中特别处理表单布局 + .knowledge-form-container { + width: 100%; + max-width: 100%; + overflow: hidden; + + .kl-ruleForm { + .el-form-item { + gap: 16px; // 减少gap避免超出 + + .el-form-item__label { + min-width: 120px; // 在对话框中使用更小的label宽度 + max-width: 120px; + flex-shrink: 0; + } + + .el-form-item__content { + flex: 1; + .el-input, + .el-select__wrapper, + .el-textarea__inner { + width: 100% !important; + max-width: 300px !important; // 确保不超出对话框 + } + } + } + } + + // 特殊处理分块设置和向量化设置中的长标签 + .chunk-settings, + .vectorization-settings { + .el-form-item { + .el-form-item__label { + min-width: 140px !important; // 为长标签提供更多空间 + max-width: 140px !important; + } + } + } } .analyticMethod { @@ -260,7 +364,7 @@ .el-input-number__decrease { background: transparent; .el-icon { - color: #8d98aa; + color: var(--o-text-color-secondary); } } @@ -269,22 +373,24 @@ } .kl-ops-btn { - height: 24px !important; - margin-top: 32px; + height: auto !important; + margin-top: 24px; margin-bottom: 0; + padding-top: 16px; // 增加顶部间距 + border-top: 1px solid var(--o-border-color-light); // 添加分隔线 .el-form-item__content { display: flex; justify-content: center; width: 100% !important; - height: 24px !important; min-height: unset !important; margin-left: 0 !important; - line-height: 24px !important; + gap: 12px; // 按钮间距 } .resetBtn { - height: 24px !important; + height: 32px !important; // 增加按钮高度使其更易点击 + padding: 0 20px; } } @@ -307,6 +413,15 @@ font-size: 12px; } + .tip-message { + margin: 0 0 24px 0; + padding: 0; + font-size: 14px; + line-height: 1.5; + color: var(--o-text-color-primary); + font-weight: 400; + } + .tip-ops-btn { display: flex; justify-content: center; @@ -325,9 +440,8 @@ } .kl-create-ops-btn { - position: absolute; - bottom: 24px; - left: 190px; + // 移除绝对定位,使用正常文档流 + position: static; } .assetIdClass { @@ -346,7 +460,7 @@ transform: rotateZ(180deg); } .copydocument:hover { - color: rgb(99, 149, 253); + color: var(--o-color-primary); } } @@ -365,3 +479,145 @@ min-width: unset !important; padding: 8px !important; } + +// Section导航样式 +.knowledge-form-container { + .section-navigation { + display: flex; + margin-bottom: 20px; + border-bottom: 1px solid var(--o-border-color-light); + background: var(--o-bg-color-light); + border-radius: 6px 6px 0 0; + + .section-tab { + position: relative; + padding: 12px 16px; + cursor: pointer; + transition: all 0.3s ease; + border-radius: 6px 6px 0 0; + display: flex; + align-items: center; + gap: 8px; + + .section-title { + font-size: 12px; + color: var(--o-text-color-secondary); + font-weight: 400; + } + + .check-icon { + font-size: 14px; + color: var(--o-color-success); + } + + &:hover { + background: var(--o-bg-color-base); + + .section-title { + color: var(--o-text-color-primary); + } + } + + &.active { + background: var(--o-bg-color-base); + border-bottom: 2px solid var(--o-color-primary); + + .section-title { + color: var(--o-color-primary); + font-weight: 500; + } + } + + &.completed:not(.active) { + .section-title { + color: var(--o-color-success); + } + } + } + } + + .form-section { + min-height: 180px; + padding: 16px 0; + + .el-form-item { + margin-bottom: 20px !important; // 统一间距 + } + } + + .chunk-settings, + .vectorization-settings { + margin-top: 20px; + padding: 16px; + background: var(--o-bg-color-light); + border-radius: 6px; + border: 1px solid var(--o-border-color-light); + + .chunk-header, + .vectorization-header { + font-size: 13px; + font-weight: 500; + color: var(--o-text-color-primary); + margin-bottom: 16px; + padding-bottom: 8px; + border-bottom: 1px solid var(--o-border-color-light); + } + + .el-form-item { + margin-bottom: 16px !important; // 内部统一间距 + + &:last-child { + margin-bottom: 0 !important; + } + } + } + + .slider-input-group { + display: flex; + align-items: center; + gap: 12px; + width: 100%; // 使用100%宽度 + max-width: 300px; // 限制最大宽度 + + .top-k-slider { + flex: 1; + min-width: 0; // 允许slider收缩 + margin-right: 0; + max-width: 220px; // 限制slider最大宽度 + } + + .top-k-input { + width: 60px; + flex-shrink: 0; // 防止数字输入框被压缩 + + .el-input-number__increase, + .el-input-number__decrease { + width: 20px; + } + } + } + + .form-right-tip { + margin-left: 8px; + font-size: 12px; + color: var(--o-text-color-secondary); + } + + // 特殊处理较长的label + .el-form-item { + &:has([label*="单次文档上传个数上限"]) { + .el-form-item__label { + min-width: 160px; + max-width: 160px; + line-height: 1.2; + } + } + + &:has([label*="文件分块大小"]) { + .el-form-item__label { + min-width: 120px; + max-width: 120px; + } + } + } +} diff --git a/src/styles/knowledgeLibrary.scss b/src/styles/knowledgeLibrary.scss index 66374ccde47bddecfb981d10583a46dd15f15574..3557794085842d07a2479f9f5837f6124c85e214 100644 --- a/src/styles/knowledgeLibrary.scss +++ b/src/styles/knowledgeLibrary.scss @@ -200,14 +200,14 @@ cursor: pointer; width: 24px; height: 24px; - background-image: url(@/assets/svg/more.svg); + background-image: var(--more-icon, url(@/assets/svg/more.svg)); background-repeat: no-repeat; background-size: 100% 100%; &:hover { - background-image: url(@/assets/svg/more_hover.svg); + background-image: var(--more-hover-icon, url(@/assets/svg/more_hover.svg)); } &:active,&:focus { - background-image: url(@/assets/svg/more_active.svg); + background-image: var(--more-active-icon, url(@/assets/svg/more_active.svg)); } } } @@ -539,7 +539,7 @@ background-repeat: no-repeat !important; background-size: 100% !important; display: inline-block; - background: url('/src/assets/icons/taskLoading.png'); + background: var(--task-loading, url('/src/assets/icons/taskLoading.png')); } .icon-box { @@ -653,6 +653,15 @@ color: var(--o-text-color-secondary); } + .tip-message { + margin: 0 0 24px 0; + padding: 0; + font-size: 14px; + line-height: 1.5; + color: #606266; + font-weight: 400; + } + .iconAlarmOrange { svg { width: 28px; diff --git a/src/styles/reset.scss b/src/styles/reset.scss index d3a88dbefca9db23ed5876ea264a8c87d225088a..839b47c2d40d239122dffcfad19d45cac8a9306c 100644 --- a/src/styles/reset.scss +++ b/src/styles/reset.scss @@ -1,8 +1,8 @@ @font-face { font-family: 'HarmonyOS Sans SC Regular'; src: - url(./HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff2) format('woff2'), - url(./HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff) format('woff'); + var(--font-harmony-woff2, url(./HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff2)) format('woff2'), + var(--font-harmony-woff, url(./HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff)) format('woff'); font-weight: 400; font-style: normal; } @@ -285,7 +285,6 @@ div:focus { .el-dialog{ border-radius: 8px !important; background-color: var(--o-bg-dialog) !important; - width: 544px !important; } .el-dialog__header { .el-dialog__title { @@ -1002,7 +1001,6 @@ div:focus { border-radius: 4px !important; // border: 1px solid var(--o-input-border-color) !important; // box-shadow: 0 0 0 0 !important; - padding: 8px 16px !important; background-color: var(--o-bg-color-base) !important; } .el-select__wrapper.is-focused{ diff --git a/src/styles/theme.scss b/src/styles/theme.scss index b1343d89d8d2ee48a88eeca67876699d7351ae18..947a1ac28bf87453b692c0be1c012a61a1de6320 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -1,6 +1,6 @@ body[theme='dark'] { - --o-main-bg: url('/src/assets/images/dark_bg_1.webp'); + --o-main-bg: var(--bg-dark, url('/src/assets/images/dark_bg_1.webp')); --o-bg-dialog:var(--o-bg-color-base); --o-button-bg:rgb(99 149 253); --o-tag-text-color:rgb(99 149 253); @@ -15,16 +15,16 @@ body[theme='dark'] { --o-tag-code-bg:rgb(168 67 205 0.3); --o-tag-qa-color:rgb(235 175 0); --o-tag-qa-bg:rgb(66 53 0); - --o-empty-img-pending: url('/src/assets/images/empty_pending_dark.svg'); - --o-empty-img-failed: url('/src/assets/images/empty_failed_dark.svg'); - --o-empty-img-running: url('/src/assets/images/empty_running_dark.svg'); - --o-task-list-icon: url('/src/assets/svg/dark_taskList.svg'); - --o-task-score-icon: url('/src/assets/svg/dark_taskScore.svg'); + --o-empty-img-pending: var(--empty-pending-dark, url('/src/assets/images/empty_pending_dark.svg')); + --o-empty-img-failed: var(--empty-failed-dark, url('/src/assets/images/empty_failed_dark.svg')); + --o-empty-img-running: var(--empty-running-dark, url('/src/assets/images/empty_running_dark.svg')); + --o-task-list-icon: var(--task-list-dark, url('/src/assets/svg/dark_taskList.svg')); + --o-task-score-icon: var(--task-score-dark, url('/src/assets/svg/dark_taskScore.svg')); --o-task-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.3); } body[theme='light'] { - --o-main-bg: url('/src/assets/images/light_bg_1.webp'); + --o-main-bg: var(--bg-light, url('/src/assets/images/light_bg_1.webp')); --o-bg-dialog:var(--o-bg-color-base); --o-button-bg:rgb(99 149 253); --o-tag-text-color:rgb(99 149 253); @@ -39,10 +39,10 @@ body[theme='light'] { --o-tag-code-bg:rgb(189 69 232 0.3); --o-tag-qa-color:rgb(235 175 0); --o-tag-qa-bg:rgb(249 235 184); - --o-empty-img-pending: url('/src/assets/images/empty_pending.svg'); - --o-empty-img-failed: url('/src/assets/images/empty_failed.svg'); - --o-empty-img-running: url('/src/assets/images/empty_running.svg'); - --o-task-list-icon: url('/src/assets/svg/light_taskList.svg'); - --o-task-score-icon: url('/src/assets/svg/light_taskScore.svg'); + --o-empty-img-pending: var(--empty-pending-light, url('/src/assets/images/empty_pending.svg')); + --o-empty-img-failed: var(--empty-failed-light, url('/src/assets/images/empty_failed.svg')); + --o-empty-img-running: var(--empty-running-light, url('/src/assets/images/empty_running.svg')); + --o-task-list-icon: var(--task-list-light, url('/src/assets/svg/light_taskList.svg')); + --o-task-score-icon: var(--task-score-light, url('/src/assets/svg/light_taskScore.svg')); --o-task-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.3); } \ No newline at end of file diff --git a/src/styles/upload.scss b/src/styles/upload.scss index 5e5e36a1e644046f6322ce5adaa81bc5fb63420e..17a11a44e3dab9bc7ba171a878792b27926094da 100644 --- a/src/styles/upload.scss +++ b/src/styles/upload.scss @@ -14,7 +14,7 @@ } } .el-alert--info .el-alert__icon{ - background-image: url('@/assets/svg/icon_info.svg'); + background-image: var(--info-icon, url('@/assets/svg/icon_info.svg')); width: 22.3px; height: 14px; } diff --git a/src/types/env.d.ts b/src/types/env.d.ts index 24247c486b94c8039bf29cc7ee511318160e78ed..8c248f32a59a07a7712047e8130f9c622560afac 100644 --- a/src/types/env.d.ts +++ b/src/types/env.d.ts @@ -17,6 +17,10 @@ interface ImportMetaEnv { VITE_APP_API_URL: string; /** 是否开启 Mock 服务 */ VITE_MOCK_DEV_SERVER: boolean; + /** 集成来源域名 - 用于解决 iframe 跨域静态资源访问问题 */ + VITE_INTEGRATION_ORIGIN?: string; + /** 是否在 iframe 环境中运行 */ + VITE_IS_IFRAME_MODE?: boolean; } interface ImportMeta { diff --git a/src/utils/cssAssetResolver.ts b/src/utils/cssAssetResolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..5d37d43eaffc2d7878e2af02a981bc01e6b03d7d --- /dev/null +++ b/src/utils/cssAssetResolver.ts @@ -0,0 +1,72 @@ +/** + * CSS 静态资源动态解析工具 + * 在运行时动态替换 CSS 变量中的静态资源路径 + */ +import { isInIframe, getIntegrationOrigin } from './index'; + +/** + * 初始化 CSS 静态资源解析 + * 在应用启动时调用,动态设置 CSS 变量 + */ +export function initCssAssetResolver() { + // 检测是否需要处理静态资源 + const isIframeMode = String(import.meta.env.VITE_IS_IFRAME_MODE) === 'true' || isInIframe(); + + if (!isIframeMode) { + return; + } + + const integrationOrigin = getIntegrationOrigin(); + const basePath = import.meta.env.BASE_URL || '/witchaind/'; + + // 构建静态资源基础路径 + const assetBaseUrl = `${integrationOrigin}${basePath.replace(/\/$/, '')}/assets`; + + // 设置 CSS 变量 + const root = document.documentElement; + + // 设置图片资源路径 + root.style.setProperty('--asset-base-url', assetBaseUrl); + root.style.setProperty('--asset-images-url', `${assetBaseUrl}/images`); + root.style.setProperty('--asset-svg-url', `${assetBaseUrl}/svg`); + root.style.setProperty('--asset-icons-url', `${assetBaseUrl}/icons`); + + // 设置具体的静态资源路径 + root.style.setProperty('--bg-light', `url('${assetBaseUrl}/images/light_bg_1.webp')`); + root.style.setProperty('--bg-dark', `url('${assetBaseUrl}/images/dark_bg_1.webp')`); + + // 空状态图标 + root.style.setProperty('--empty-pending-light', `url('${assetBaseUrl}/images/empty_pending.svg')`); + root.style.setProperty('--empty-pending-dark', `url('${assetBaseUrl}/images/empty_pending_dark.svg')`); + root.style.setProperty('--empty-failed-light', `url('${assetBaseUrl}/images/empty_failed.svg')`); + root.style.setProperty('--empty-failed-dark', `url('${assetBaseUrl}/images/empty_failed_dark.svg')`); + root.style.setProperty('--empty-running-light', `url('${assetBaseUrl}/images/empty_running.svg')`); + root.style.setProperty('--empty-running-dark', `url('${assetBaseUrl}/images/empty_running_dark.svg')`); + + // 任务图标 + root.style.setProperty('--task-list-light', `url('${assetBaseUrl}/svg/light_taskList.svg')`); + root.style.setProperty('--task-list-dark', `url('${assetBaseUrl}/svg/dark_taskList.svg')`); + root.style.setProperty('--task-score-light', `url('${assetBaseUrl}/svg/light_taskScore.svg')`); + root.style.setProperty('--task-score-dark', `url('${assetBaseUrl}/svg/dark_taskScore.svg')`); + + // 加载图标 + root.style.setProperty('--task-loading', `url('${assetBaseUrl}/icons/taskLoading.png')`); + + // 更多操作图标 + root.style.setProperty('--more-icon', `url('${assetBaseUrl}/svg/more.svg')`); + root.style.setProperty('--more-hover-icon', `url('${assetBaseUrl}/svg/more_hover.svg')`); + root.style.setProperty('--more-active-icon', `url('${assetBaseUrl}/svg/more_active.svg')`); + + // 登录背景 + root.style.setProperty('--login-bg-light', `url('${assetBaseUrl}/images/login-background-light.webp')`); + root.style.setProperty('--login-bg-dark', `url('${assetBaseUrl}/images/login-background-dark.jpg')`); + + // 信息图标 + root.style.setProperty('--info-icon', `url('${assetBaseUrl}/svg/icon_info.svg')`); + + // 字体文件 + root.style.setProperty('--font-harmony-woff2', `url('${assetBaseUrl}/fonts/HarmonyOS/HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff2')`); + root.style.setProperty('--font-harmony-woff', `url('${assetBaseUrl}/fonts/HarmonyOS/HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.woff')`); + + console.log('CSS 静态资源路径已动态设置:', assetBaseUrl); +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 26e138674f723faf71f4dba659e47da2dcf962c9..3933e5a7f7ec9273d7496f7d85c5da88ec5f09a5 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -39,3 +39,71 @@ export function isExternal(path: string) { const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path); return isExternal; } + +/** + * 检测是否在 iframe 环境中运行 + * @returns {boolean} + */ +export function isInIframe(): boolean { + try { + return window.self !== window.top; + } catch (e) { + return true; + } +} + +/** + * 获取集成来源的 origin + * @returns {string} + */ +export function getIntegrationOrigin(): string { + // 优先使用环境变量配置 + const configuredOrigin = import.meta.env.VITE_INTEGRATION_ORIGIN; + if (configuredOrigin) { + return configuredOrigin; + } + + // 如果在 iframe 中,尝试从 parent 获取 + if (isInIframe()) { + try { + return window.parent.location.origin; + } catch (e) { + // 跨域时无法访问 parent.location,使用 referrer + if (document.referrer) { + const url = new URL(document.referrer); + return url.origin; + } + } + } + + // 默认返回当前 origin + return window.location.origin; +} + +/** + * 处理静态资源 URL,解决 iframe 跨域问题 + * @param {string} assetPath - 静态资源路径 + * @returns {string} - 处理后的完整 URL + */ +export function resolveAssetUrl(assetPath: string): string { + // 如果已经是完整 URL,直接返回 + if (isExternal(assetPath)) { + return assetPath; + } + + // 如果不在 iframe 环境中,使用默认处理 + const isIframeMode = import.meta.env.VITE_IS_IFRAME_MODE === 'true' || isInIframe(); + if (!isIframeMode) { + return assetPath; + } + + // 在 iframe 环境中,使用集成来源的 origin + const integrationOrigin = getIntegrationOrigin(); + const basePath = import.meta.env.BASE_URL || '/witchaind/'; + + // 确保路径以 / 开头 + const normalizedPath = assetPath.startsWith('/') ? assetPath : `/${assetPath}`; + + // 构建完整 URL + return `${integrationOrigin}${basePath.replace(/\/$/, '')}${normalizedPath}`; +} diff --git a/src/views/dataSet/craeteEvaluate.vue b/src/views/dataSet/craeteEvaluate.vue index 15217ef643a8b0c3c7f485b856aeef17131e311e..ef8163e738a2469e341deb1112720b6bf70e2f65 100644 --- a/src/views/dataSet/craeteEvaluate.vue +++ b/src/views/dataSet/craeteEvaluate.vue @@ -19,7 +19,7 @@ @@ -81,9 +81,11 @@ import "@/styles/evaluate.scss" import KbAppAPI from '@/api/kbApp'; import dataSetAPI from '@/api/dataSet'; import EvaluateAPI from '@/api/evaluate'; +import { useAssets } from '@/composables/useAssets'; import { useGroupStore } from '@/store/modules/group'; import { IconCaretDown } from '@computing/opendesign-icons'; const { t, } = useI18n(); +const { getSvgUrl } = useAssets(); const store = useGroupStore(); const { handleKnowledgeTab } = store; const currentLlmOption = computed(() => diff --git a/src/views/dataSet/index.vue b/src/views/dataSet/index.vue index 6cb85212313eb2b14dec510825982949992cea92..10b6b596c160b779ba7c090a300906e1f429ab7f 100644 --- a/src/views/dataSet/index.vue +++ b/src/views/dataSet/index.vue @@ -71,12 +71,12 @@ - fail icon + fail icon - success icon + success icon {{ $t('exportTask.exportSuccess') }} @@ -531,8 +531,10 @@ import DataSetDrawer from './dataSetDrawer.vue'; import CreateEvaluate from '@/views/dataSet/craeteEvaluate.vue'; import dataSetAPI from '@/api/dataSet'; import CustomLoading from '@/components/CustomLoading/index.vue'; +import { useAssets } from '@/composables/useAssets'; const route = useRoute(); const { t } = useI18n(); +const { getSvgUrl } = useAssets(); import { ref } from 'vue'; import '@/styles/dataSet.scss'; diff --git a/src/views/group/index.vue b/src/views/group/index.vue index 2071fb5eb2b03afeb55a9e41989c947c437fa85c..9d6b9b5c3e46b7d2ce5e5baf93de39c2638b1fa5 100644 --- a/src/views/group/index.vue +++ b/src/views/group/index.vue @@ -63,7 +63,7 @@
@{{ item.authorName }} - + {{ item.memberCount }}{{ $t('group.people') }}
@@ -106,7 +106,7 @@ {{ $t('group.private') }} - + \ No newline at end of file + + + + + \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 971a0bae05670f3d85b201c60c4ccd8661cc656a..0dcd8e519e213c90e81c5f1f029e4e59d8fce2c8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -25,8 +25,15 @@ const __APP_INFO__ = { const pathSrc = path.resolve(__dirname, './src'); export default defineConfig(({ mode }: ConfigEnv): UserConfig => { const env = loadEnv(mode, process.cwd()); + + // 根据环境和集成模式动态设置 base URL + let baseUrl = '/witchaind/'; + if (mode === 'development') { + baseUrl = env.VITE_IS_IFRAME_MODE === 'true' ? '/witchaind/' : '/'; + } + return { - base: '/witchaind/', // 设置打包路径 + base: baseUrl, // 动态设置打包路径 resolve: { alias: { '@': pathSrc, @@ -42,14 +49,20 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { additionalData: ` @use "@/styles/variables.scss" as *; `, + // 抑制 Sass 弃用警告 + silenceDeprecations: ['legacy-js-api', 'global-builtin', 'new-global', 'color-functions'], + // 静默第三方依赖的警告 + quietDeps: true, + // 忽略来自 node_modules 的 @import 警告 + warnRuleAsWarning: false, }, }, }, server: { // 允许IP访问 - host: 'localhost', - port: 3002, - origin: 'http://localhost:3002', + host: env.VITE_HOST, + port: Number(env.VITE_PORT), + origin: env.VITE_ORIGIN, // 运行是否自动打开浏览器 headers: { 'Access-control-allow-origin': '*', @@ -57,11 +70,11 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { open: true, proxy: { '/witchaind/api': { - target: 'https://euler-copilot-master.test.osinfra.cn/witchaind', + target: env.VITE_BASE_PROXY_URL, changeOrigin: true, ws: false, secure: false, - rewrite: (path) => path.replace(/^\/witchaind\/api/, '/api'), + rewrite: (path) => path.replace(/^\/witchaind\/api/, ''), }, }, },