From c32e59cf64f318f0f92ed5d3cd892be3e022d43e Mon Sep 17 00:00:00 2001 From: JEECG <445654970@qq.com> Date: Tue, 28 Apr 2026 15:28:00 +0800 Subject: [PATCH] =?UTF-8?q?v3.9.2=20=E7=8E=8B=E7=82=B8=EF=BC=81=E5=A4=A7?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeecgboot-vue3/.claudeignore | 57 ++ jeecgboot-vue3/.env.development | 3 + jeecgboot-vue3/.env.production | 3 + jeecgboot-vue3/build/vite/plugin/pwa.ts | 23 +- jeecgboot-vue3/index.html | 14 +- .../src/assets/icons/js/iconfont2.js | 1 + .../src/components/Application/index.ts | 17 +- .../Form/src/components/ApiSelect.vue | 4 +- .../src/jeecg/components/JAreaLinkage.vue | 96 ++- .../Form/src/jeecg/components/JCascader.vue | 368 +++++++++ .../components/LinkTableListModal.vue | 4 +- .../JLinkTableCard/hooks/useTableColumns.ts | 119 ++- .../src/jeecg/components/JSelectMultiple.vue | 72 +- .../src/jeecg/components/JSelectSingle.vue | 323 ++++++++ .../components/modal/DeptSelectModal.vue | 2 +- .../modal/JSelectUserByDepartmentModal.vue | 93 ++- .../hooks/useSelectMultipleScrollLoad.ts | 182 +++++ .../src/components/Form/src/types/index.ts | 2 + .../src/components/Form/src/utils/Area.ts | 54 +- .../src/components/JVxeCustom/index.ts | 4 + .../src/components/JVxeLinkTableCell.vue | 105 +++ .../src/components/JVxePopupCell.vue | 4 +- .../components/JVxeSelectDictSearchCell.ts | 8 +- .../src/components/Menu/src/BasicMenu.vue | 6 + .../src/components/Tinymce/src/Editor.vue | 36 +- .../jeecg/JTabsSelectUser/SelectUserModal.vue | 722 ++++++++++++++++++ .../component/PostRankRelation.vue | 79 ++ .../jeecg/JTabsSelectUser/index.vue | 174 +++++ .../jeecg/JTabsSelectUser/useSelectUser.ts | 101 +++ .../jeecg/JVxeTable/src/JVxeTable.ts | 27 +- .../jeecg/JVxeTable/src/componentMap.ts | 23 +- .../jeecg/JVxeTable/src/componentMapStore.ts | 20 + .../src/components/JVxeReloadEffect.ts | 2 +- .../cells/JVxeCategorySelectCell.vue | 190 +++++ .../components/cells/JVxeTreeSelectCell.vue | 244 ++++++ .../jeecg/JVxeTable/src/hooks/useColumns.ts | 5 + .../jeecg/JVxeTable/src/hooks/useData.ts | 7 +- .../JVxeTable/src/hooks/useDataSource.ts | 5 +- .../jeecg/JVxeTable/src/hooks/useDragSort.ts | 161 ++-- .../JVxeTable/src/hooks/useFinallyProps.ts | 3 - .../JVxeTable/src/hooks/useJVxeComponent.ts | 6 +- .../jeecg/JVxeTable/src/hooks/useMethods.ts | 9 +- .../components/jeecg/JVxeTable/src/install.ts | 9 +- .../jeecg/JVxeTable/src/style/index.less | 15 +- .../jeecg/JVxeTable/src/types/JVxeTypes.ts | 8 + .../JVxeTable/src/utils/registerUtils.ts | 7 + .../jeecg/OnLine/types/onlineConfig.ts | 1 + jeecgboot-vue3/src/design/index.less | 3 + .../src/hooks/system/useListPage.ts | 4 +- jeecgboot-vue3/src/hooks/web/usePrintJS.ts | 8 +- .../src/layouts/default/content/index.vue | 26 +- .../layouts/default/header/MultipleHeader.vue | 4 + .../components/user-dropdown/DepartSelect.vue | 8 +- .../src/layouts/default/header/index.less | 5 + .../src/layouts/default/header/index.vue | 4 +- jeecgboot-vue3/src/layouts/default/index.vue | 64 +- .../src/layouts/default/tabs/index.vue | 8 +- .../default/tabs/tabs.theme.simple.less | 237 ++++++ .../src/layouts/page/components/EmptyPage.vue | 174 +++++ jeecgboot-vue3/src/layouts/page/index.vue | 150 +--- jeecgboot-vue3/src/qiankun/apps.ts | 6 +- jeecgboot-vue3/src/qiankun/index.ts | 49 +- .../src/qiankun/micro/qiankunMicro.ts | 2 +- jeecgboot-vue3/src/qiankun/route.ts | 45 ++ .../src/settings/registerThirdComp.ts | 32 +- .../utils/factory/createAsyncComponent.tsx | 3 +- jeecgboot-vue3/src/utils/iconfont2.ts | 4 + .../src/views/demo/editor/tinymce/index.vue | 20 +- .../src/views/demo/feat/icon/index.vue | 34 +- .../demo/jeecg/JVxeTableDemo/JVxeDemo1.vue | 40 + .../demo/jeecg/JVxeTableDemo/JVxeDemo3.vue | 1 - .../src/views/demo/jeecg/JeecgComponents.vue | 6 +- .../views/demo/jeecg/jeecgComponents.data.ts | 19 +- .../src/views/demo/vextable/index3.vue | 46 ++ .../vextable/jvxetable/JVxeTableModal.vue | 3 +- .../monitor/datalog/DataLogCompareModal.vue | 441 +++++++---- .../src/views/monitor/datalog/datalog.data.ts | 76 +- .../src/views/monitor/datalog/index.vue | 52 +- .../src/views/monitor/log/index.vue | 37 +- .../src/views/monitor/log/log.data.ts | 38 +- .../src/views/monitor/mynews/DetailModal.vue | 21 +- .../src/views/monitor/mynews/XssWhiteList.ts | 8 +- .../src/views/monitor/quartz/quartz.data.ts | 2 +- .../src/views/monitor/redis/index.vue | 371 +++++---- .../src/views/monitor/redis/redis.data.ts | 17 +- .../src/views/openapi/OpenApi.data.ts | 189 ++++- .../src/views/openapi/OpenApiAuth.data.ts | 98 ++- .../src/views/openapi/OpenApiAuthList.vue | 267 +++---- .../src/views/openapi/OpenApiList.vue | 54 +- .../views/openapi/components/AuthDrawer.vue | 185 +++++ .../openapi/components/OpenApiAuthDrawer.vue | 75 ++ .../openapi/components/OpenApiAuthForm.vue | 14 +- .../openapi/components/OpenApiDrawer.vue | 269 +++++++ .../views/openapi/components/OpenApiModal.vue | 1 - .../src/views/super/airag/aiapp/AiApp.api.ts | 2 +- .../views/super/airag/aiapp/chat/AiChat.vue | 1 + .../src/views/super/airag/aiapp/chat/chat.vue | 100 +-- .../super/airag/aiapp/chat/chatMessage.vue | 2 +- .../views/super/airag/aiapp/chat/chatText.vue | 2 +- .../jeecg-tags/jeecg-chart/ChartRender.api.ts | 43 ++ .../jeecg-tags/jeecg-chart/ChartRender.vue | 461 ++++++++--- .../jeecg-tags/tool-exec/JeecgToolExec.vue | 2 + .../aiapp/chat/portal/LeftPortalSession.vue | 7 + .../components/AiAppPromptMarketModal.vue | 3 + .../aiapp/components/AiAppSettingModal.vue | 37 + .../aiapp/components/AiUserVariablesModal.vue | 2 - .../super/airag/aicloth/AiClothChange.data.ts | 91 +++ .../super/airag/aicloth/AiClothChange.less | 372 +++++++++ .../super/airag/aicloth/AiClothChange.vue | 284 +++++++ .../airag/aiknowledge/AiKnowledgeBase.data.ts | 193 ++++- .../airag/aiknowledge/AiKnowledgeBaseList.vue | 8 +- .../components/AiKnowledgeBaseModal.vue | 52 +- .../components/AiragKnowledgeDocListModal.vue | 29 +- .../components/AiragKnowledgeDocTextModal.vue | 211 ++++- .../views/super/airag/aimodel/AiModelList.vue | 22 +- .../airag/aimodel/components/AiModelModal.vue | 116 ++- .../super/airag/aimodel/components/model.json | 43 ++ .../views/super/airag/aimodel/icon/gemini.png | Bin 0 -> 9537 bytes .../super/airag/aimodel/icon/imstdio.png | Bin 0 -> 43709 bytes .../views/super/airag/aimodel/icon/vllm.png | Bin 0 -> 89976 bytes .../super/airag/aimodel/icon/xinference.svg | 25 + .../views/super/airag/aimodel/model.data.ts | 35 +- .../views/super/airag/aiposter/AiPainting.vue | 414 ++++++++++ .../super/airag/aiposter/AiPoster.data.ts | 259 ++++++- .../views/super/airag/aiposter/AiPoster.vue | 121 ++- .../super/airag/aiprompts/AiragPrompts.api.ts | 8 + .../airag/aiprompts/AiragPrompts.data.ts | 3 +- .../views/super/airag/aivideo/AiVideo.api.ts | 37 + .../views/super/airag/aivideo/AiVideo.data.ts | 76 ++ .../views/super/airag/aivideo/AiVideo.less | 375 +++++++++ .../src/views/super/airag/aivideo/AiVideo.vue | 415 ++++++++++ .../views/super/airag/aivideo2/AiVideo.api.ts | 22 + .../super/airag/aivideo2/AiVideo.data.ts | 50 ++ .../views/super/airag/aivideo2/AiVideo.vue | 418 ++++++++++ .../views/super/airag/aivoice/AiVoice.api.ts | 34 + .../views/super/airag/aivoice/AiVoice.data.ts | 117 +++ .../views/super/airag/aivoice/AiVoice.less | 382 +++++++++ .../src/views/super/airag/aivoice/AiVoice.vue | 332 ++++++++ .../src/views/sys/login/LoginForm.vue | 6 +- .../src/views/system/depart/depart.api.ts | 4 +- .../src/views/system/examples/demo/index.vue | 1 + .../views/system/loginmini/MiniCodelogin.vue | 6 +- .../src/views/system/loginmini/MiniLogin.vue | 35 +- .../message/components/SysMessageList.vue | 9 + .../message/components/SysMessageModal.vue | 9 + .../src/views/system/notice/DetailModal.vue | 4 +- .../src/views/system/ossfile/index.vue | 5 +- .../src/views/system/role/role.data.ts | 4 +- .../src/views/system/ugroup/SysUgroupList.vue | 176 +++++ .../ugroup/components/GroupUserTable.vue | 176 +++++ .../ugroup/components/SysUgroupModal.vue | 77 ++ .../src/views/system/ugroup/ugroup.api.ts | 107 +++ .../src/views/system/ugroup/ugroup.data.ts | 93 +++ .../system/usersetting/WeChatDingSetting.vue | 6 +- jeecgboot-vue3/tsconfig.json | 3 +- jeecgboot-vue3/types/global.d.ts | 2 + jeecgboot-vue3/types/unplugin-icons.d.ts | 10 + 157 files changed, 11253 insertions(+), 1324 deletions(-) create mode 100644 jeecgboot-vue3/.claudeignore create mode 100644 jeecgboot-vue3/src/assets/icons/js/iconfont2.js create mode 100644 jeecgboot-vue3/src/components/Form/src/jeecg/components/JCascader.vue create mode 100644 jeecgboot-vue3/src/components/Form/src/jeecg/components/JSelectSingle.vue create mode 100644 jeecgboot-vue3/src/components/Form/src/jeecg/hooks/useSelectMultipleScrollLoad.ts create mode 100644 jeecgboot-vue3/src/components/JVxeCustom/src/components/JVxeLinkTableCell.vue create mode 100644 jeecgboot-vue3/src/components/jeecg/JTabsSelectUser/SelectUserModal.vue create mode 100644 jeecgboot-vue3/src/components/jeecg/JTabsSelectUser/component/PostRankRelation.vue create mode 100644 jeecgboot-vue3/src/components/jeecg/JTabsSelectUser/index.vue create mode 100644 jeecgboot-vue3/src/components/jeecg/JTabsSelectUser/useSelectUser.ts create mode 100644 jeecgboot-vue3/src/components/jeecg/JVxeTable/src/componentMapStore.ts create mode 100644 jeecgboot-vue3/src/components/jeecg/JVxeTable/src/components/cells/JVxeCategorySelectCell.vue create mode 100644 jeecgboot-vue3/src/components/jeecg/JVxeTable/src/components/cells/JVxeTreeSelectCell.vue create mode 100644 jeecgboot-vue3/src/layouts/default/tabs/tabs.theme.simple.less create mode 100644 jeecgboot-vue3/src/layouts/page/components/EmptyPage.vue create mode 100644 jeecgboot-vue3/src/qiankun/route.ts create mode 100644 jeecgboot-vue3/src/utils/iconfont2.ts create mode 100644 jeecgboot-vue3/src/views/demo/vextable/index3.vue create mode 100644 jeecgboot-vue3/src/views/openapi/components/AuthDrawer.vue create mode 100644 jeecgboot-vue3/src/views/openapi/components/OpenApiAuthDrawer.vue create mode 100644 jeecgboot-vue3/src/views/openapi/components/OpenApiDrawer.vue create mode 100644 jeecgboot-vue3/src/views/super/airag/aiapp/chat/jeecg-tags/jeecg-chart/ChartRender.api.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aicloth/AiClothChange.data.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aicloth/AiClothChange.less create mode 100644 jeecgboot-vue3/src/views/super/airag/aicloth/AiClothChange.vue create mode 100644 jeecgboot-vue3/src/views/super/airag/aimodel/icon/gemini.png create mode 100644 jeecgboot-vue3/src/views/super/airag/aimodel/icon/imstdio.png create mode 100644 jeecgboot-vue3/src/views/super/airag/aimodel/icon/vllm.png create mode 100644 jeecgboot-vue3/src/views/super/airag/aimodel/icon/xinference.svg create mode 100644 jeecgboot-vue3/src/views/super/airag/aiposter/AiPainting.vue create mode 100644 jeecgboot-vue3/src/views/super/airag/aivideo/AiVideo.api.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aivideo/AiVideo.data.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aivideo/AiVideo.less create mode 100644 jeecgboot-vue3/src/views/super/airag/aivideo/AiVideo.vue create mode 100644 jeecgboot-vue3/src/views/super/airag/aivideo2/AiVideo.api.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aivideo2/AiVideo.data.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aivideo2/AiVideo.vue create mode 100644 jeecgboot-vue3/src/views/super/airag/aivoice/AiVoice.api.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aivoice/AiVoice.data.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aivoice/AiVoice.less create mode 100644 jeecgboot-vue3/src/views/super/airag/aivoice/AiVoice.vue create mode 100644 jeecgboot-vue3/src/views/system/ugroup/SysUgroupList.vue create mode 100644 jeecgboot-vue3/src/views/system/ugroup/components/GroupUserTable.vue create mode 100644 jeecgboot-vue3/src/views/system/ugroup/components/SysUgroupModal.vue create mode 100644 jeecgboot-vue3/src/views/system/ugroup/ugroup.api.ts create mode 100644 jeecgboot-vue3/src/views/system/ugroup/ugroup.data.ts create mode 100644 jeecgboot-vue3/types/unplugin-icons.d.ts diff --git a/jeecgboot-vue3/.claudeignore b/jeecgboot-vue3/.claudeignore new file mode 100644 index 000000000..dd1e0e9f0 --- /dev/null +++ b/jeecgboot-vue3/.claudeignore @@ -0,0 +1,57 @@ +# Git +.git/ +.gitignore +.gitmodules + +# SVN +.svn/ + +# IntelliJ IDEA +.idea/ +*.iml +*.iws +*.ipr +out/ + +# Eclipse +.classpath +.project +.settings/ + +# VS Code +.vscode/ + +# Maven / Gradle build output +target/ +build/ +!.mvn/wrapper/maven-wrapper.jar + +# OS files +.DS_Store +Thumbs.db +desktop.ini + +# Logs +*.log +logs/ + +# Node (frontend artifacts if any) +node_modules/ +dist/ + +# Docker volumes / data +docker/data/ + +# Compiled classes +*.class + +# Custom +*.qqy +代码修改.log +代码修改日志 +*.zip +backup/ +.history/ +.cursor/ +doc/ +docs/ diff --git a/jeecgboot-vue3/.env.development b/jeecgboot-vue3/.env.development index fb3498c90..97118e296 100644 --- a/jeecgboot-vue3/.env.development +++ b/jeecgboot-vue3/.env.development @@ -31,3 +31,6 @@ VITE_APP_SUB_jeecg-app-1 = '//localhost:8092' # 在线文档编辑版本。可选属性:wps, offlineWps(离线版) ,onlyoffice VITE_GLOB_ONLINE_DOCUMENT_VERSION=wps + +# icon组件的iconify图标库使用在线还是本地。可选属性:online, local +VITE_GLOB_ICONIFY_USE_TYPE=online diff --git a/jeecgboot-vue3/.env.production b/jeecgboot-vue3/.env.production index f3553726f..ddb4d4bb5 100644 --- a/jeecgboot-vue3/.env.production +++ b/jeecgboot-vue3/.env.production @@ -32,3 +32,6 @@ VITE_GLOB_API_URL_PREFIX= # 在线文档编辑版本。可选属性:wps, offlineWps(离线版), onlyoffice VITE_GLOB_ONLINE_DOCUMENT_VERSION=wps + +# icon组件的iconify图标库使用在线还是本地。可选属性:online, local +VITE_GLOB_ICONIFY_USE_TYPE=local diff --git a/jeecgboot-vue3/build/vite/plugin/pwa.ts b/jeecgboot-vue3/build/vite/plugin/pwa.ts index 2251b3e98..41d69ddd1 100644 --- a/jeecgboot-vue3/build/vite/plugin/pwa.ts +++ b/jeecgboot-vue3/build/vite/plugin/pwa.ts @@ -96,9 +96,10 @@ export function configPwaPlugin(isBuild: boolean): PluginOption | PluginOption[] }, }, }, - // 图片资源 + //update-begin---author:scott ---date:20260417 for:[issues/9564]PWA图片规则过宽导致/filereview/等业务接口走缓存----------- + // 图片资源(仅缓存构建产物,避免命中 /filereview/、/jeecgboot/ 等业务接口返回的图片) { - urlPattern: /\.(?:png|jpg|jpeg|svg|gif|webp)$/, + urlPattern: /\/(?:assets|img|static|resource)\/.*\.(?:png|jpg|jpeg|svg|gif|webp)$/i, handler: 'CacheFirst', options: { cacheName: 'image-cache', @@ -108,22 +109,12 @@ export function configPwaPlugin(isBuild: boolean): PluginOption | PluginOption[] }, }, }, - // API 请求 + // API 请求(JeecgBoot 实际前缀是 /jeecgboot/,原 /api/ 规则未生效) { - urlPattern: /\/api\/.*/i, - handler: 'NetworkFirst', - options: { - cacheName: 'api-cache', - networkTimeoutSeconds: 10, - expiration: { - maxEntries: 50, - maxAgeSeconds: 60 * 5, - }, - cacheableResponse: { - statuses: [0, 200], - }, - }, + urlPattern: /\/jeecgboot\/.*/i, + handler: 'NetworkOnly', }, + //update-end---author:scott ---date:20260417 for:[issues/9564]PWA图片规则过宽导致/filereview/等业务接口走缓存----------- ], // 启用立即更新:新 SW 立即激活并接管页面 skipWaiting: true, diff --git a/jeecgboot-vue3/index.html b/jeecgboot-vue3/index.html index 245a4a9d2..29726acd7 100644 --- a/jeecgboot-vue3/index.html +++ b/jeecgboot-vue3/index.html @@ -168,13 +168,15 @@ diff --git a/jeecgboot-vue3/src/assets/icons/js/iconfont2.js b/jeecgboot-vue3/src/assets/icons/js/iconfont2.js new file mode 100644 index 000000000..b098acff6 --- /dev/null +++ b/jeecgboot-vue3/src/assets/icons/js/iconfont2.js @@ -0,0 +1 @@ +!function(c){var a,t,e,i,l,n,o='',d=(d=document.getElementsByTagName("script"))[d.length-1].getAttribute("data-injectcss");if(d&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function h(){l||(l=!0,e())}a=function(){var c,a,t,e;(e=document.createElement("div")).innerHTML=o,o=null,(t=e.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",c=t,(a=document.body).firstChild?(e=c,(t=a.firstChild).parentNode.insertBefore(e,t)):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),a()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(e=a,i=c.document,l=!1,(n=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(n,50)}h()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,h())})}(window); \ No newline at end of file diff --git a/jeecgboot-vue3/src/components/Application/index.ts b/jeecgboot-vue3/src/components/Application/index.ts index d7c513306..01c02bf7b 100644 --- a/jeecgboot-vue3/src/components/Application/index.ts +++ b/jeecgboot-vue3/src/components/Application/index.ts @@ -1,15 +1,10 @@ import { withInstall } from '/@/utils'; - -import appLogo from './src/AppLogo.vue'; -import appProvider from './src/AppProvider.vue'; -import appSearch from './src/search/AppSearch.vue'; -import appLocalePicker from './src/AppLocalePicker.vue'; -import appDarkModeToggle from './src/AppDarkModeToggle.vue'; +import { defineAsyncComponent } from 'vue'; export { useAppProviderContext } from './src/useAppContext'; -export const AppLogo = withInstall(appLogo); -export const AppProvider = withInstall(appProvider); -export const AppSearch = withInstall(appSearch); -export const AppLocalePicker = withInstall(appLocalePicker); -export const AppDarkModeToggle = withInstall(appDarkModeToggle); +export const AppLogo = withInstall(defineAsyncComponent(() => import('./src/AppLogo.vue'))); +export const AppProvider = withInstall(defineAsyncComponent(() => import('./src/AppProvider.vue'))); +export const AppSearch = withInstall(defineAsyncComponent(() => import('./src/search/AppSearch.vue'))); +export const AppLocalePicker = withInstall(defineAsyncComponent(() => import('./src/AppLocalePicker.vue'))); +export const AppDarkModeToggle = withInstall(defineAsyncComponent(() => import('./src/AppDarkModeToggle.vue'))); diff --git a/jeecgboot-vue3/src/components/Form/src/components/ApiSelect.vue b/jeecgboot-vue3/src/components/Form/src/components/ApiSelect.vue index 3686ea8e5..58ce857c2 100644 --- a/jeecgboot-vue3/src/components/Form/src/components/ApiSelect.vue +++ b/jeecgboot-vue3/src/components/Form/src/components/ApiSelect.vue @@ -114,7 +114,9 @@ if (next) { const value = next[valueField]; prev.push({ - ...omit(next, [labelField, valueField]), + // update-begin--author:liaozhiyang---date:20260226---for:【issues/9326】ApiSelect组件,返回数据中包含 options 这个名称导致组件渲染失败 + ...omit(next, [labelField, valueField, 'options']), + // update-end--author:liaozhiyang---date:20260226---for:【issues/9326】ApiSelect组件,返回数据中包含 options 这个名称导致组件渲染失败 label: next[labelField], value: numberToString ? `${value}` : value, }); diff --git a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JAreaLinkage.vue b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JAreaLinkage.vue index 40e10a7c6..734308e0b 100644 --- a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JAreaLinkage.vue +++ b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JAreaLinkage.vue @@ -1,11 +1,11 @@ + + + diff --git a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/components/LinkTableListModal.vue b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/components/LinkTableListModal.vue index a368117e8..548a4afdd 100644 --- a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/components/LinkTableListModal.vue +++ b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/components/LinkTableListModal.vue @@ -36,8 +36,8 @@
- @@ -44,6 +32,7 @@ import { computed, defineComponent, unref } from 'vue'; import FrameLayout from '/@/layouts/iframe/index.vue'; + import EmptyPage from './components/EmptyPage.vue'; import { useRootSetting } from '/@/hooks/setting/useRootSetting'; @@ -52,11 +41,10 @@ import { getTransitionName } from './transition'; import { useMultipleTabStore } from '/@/store/modules/multipleTab'; - import { useEmpty } from './useEmpty'; export default defineComponent({ name: 'PageLayout', - components: { FrameLayout }, + components: { FrameLayout, EmptyPage }, setup() { const { getShowMultipleTab } = useMultipleTabSetting(); const tabStore = useMultipleTabStore(); @@ -73,8 +61,6 @@ } return tabStore.getCachedTabList; }); - // 代码逻辑说明: 【QQYUN-13593】空白页美化 - const { pageTip, getPageTip, effectVars } = useEmpty(); return { getTransitionName, openCache, @@ -82,123 +68,7 @@ getBasicTransition, getCaches, getCanEmbedIFramePage, - pageTip, - getPageTip, - effectVars, }; }, }); - diff --git a/jeecgboot-vue3/src/qiankun/apps.ts b/jeecgboot-vue3/src/qiankun/apps.ts index a6f035892..aa7031433 100644 --- a/jeecgboot-vue3/src/qiankun/apps.ts +++ b/jeecgboot-vue3/src/qiankun/apps.ts @@ -1,3 +1,5 @@ +// export const containerId = 'qiankun-content' +// // /** // *微应用apps // * @name: 微应用名称 - 具有唯一性 @@ -6,14 +8,14 @@ // * @activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用 // */ // //子应用列表 -// const _apps: object[] = []; +// const _apps: Recordable[] = []; // for (const key in import.meta.env) { // if (key.includes('VITE_APP_SUB_')) { // const name = key.split('VITE_APP_SUB_')[1]; // const obj = { // name, // entry: import.meta.env[key], -// container: '#content', +// container: '#' + containerId, // activeRule: name, // }; // _apps.push(obj); diff --git a/jeecgboot-vue3/src/qiankun/index.ts b/jeecgboot-vue3/src/qiankun/index.ts index 23b1a4f6f..37b4a048f 100644 --- a/jeecgboot-vue3/src/qiankun/index.ts +++ b/jeecgboot-vue3/src/qiankun/index.ts @@ -1,9 +1,17 @@ // /** // * qiankun配置 // */ -// import { registerMicroApps, setDefaultMountApp, start, runAfterFirstMounted, addGlobalUncaughtErrorHandler } from 'qiankun'; -// import { apps } from './apps'; +// import { +// start, +// registerMicroApps, +// runAfterFirstMounted, +// addGlobalUncaughtErrorHandler +// } from 'qiankun'; +// import { apps, containerId } from './apps'; // import { getProps, initGlState } from './state'; +// import { registerQiankunRouter } from './route'; +// +// registerQiankunRouter(); // // /** // * 重构apps @@ -11,6 +19,7 @@ // function filterApps() { // apps.forEach((item) => { // //主应用需要传递给微应用的数据。 +// // @ts-ignore // item.props = getProps(); // //微应用触发的路由规则 // // @ts-ignore @@ -27,34 +36,62 @@ // return (location) => location.pathname.startsWith(routerPrefix); // } // +// let retryCount = 0; +// // /** // * 微应用注册 // */ // function registerApps() { +// const container = document.querySelector('#' + containerId); +// if (!container) { +// // 如果容器不存在,递归尝试注册应用,最多尝试10次,每次间隔500毫秒 +// if (retryCount < 10) { +// retryCount++; +// setTimeout(() => registerApps(), 500); +// } +// } else { +// registerAppsNow(); +// } +// } +// +// registerApps['containerId'] = containerId; +// +// function registerAppsNow() { +// if (window.qiankunStarted) { +// return; +// } +// window.qiankunStarted = true; // const _apps = filterApps(); +// // @ts-ignore // registerMicroApps(_apps, { // beforeLoad: [ // // @ts-ignore // (loadApp) => { -// console.log('before load', loadApp); +// console.log('[qiankun] before load', loadApp); // }, // ], // beforeMount: [ // // @ts-ignore // (mountApp) => { -// console.log('before mount', mountApp); +// console.log('[qiankun] before mount', mountApp); // }, // ], // afterMount: [ // // @ts-ignore // (mountApp) => { -// console.log('before mount', mountApp); +// console.log('[qiankun] after mount', mountApp); +// }, +// ], +// beforeUnmount: [ +// // @ts-ignore +// (unloadApp) => { +// console.log('[qiankun] before unmount', unloadApp); // }, // ], // afterUnmount: [ // // @ts-ignore // (unloadApp) => { -// console.log('after unload', unloadApp); +// console.log('[qiankun] after unmount', unloadApp); // }, // ], // }); diff --git a/jeecgboot-vue3/src/qiankun/micro/qiankunMicro.ts b/jeecgboot-vue3/src/qiankun/micro/qiankunMicro.ts index 9e82f554f..4e9d5c191 100644 --- a/jeecgboot-vue3/src/qiankun/micro/qiankunMicro.ts +++ b/jeecgboot-vue3/src/qiankun/micro/qiankunMicro.ts @@ -6,7 +6,7 @@ import type {MainAppProps} from "#/main"; import {destroyStore} from "@/store"; import {destroyRouter} from "@/router"; -import {clearComponent} from "@/components/jeecg/JVxeTable/src/componentMap"; +import { clearComponent } from '@/components/jeecg/JVxeTable/src/componentMapStore'; import {renderWithQiankun} from 'vite-plugin-qiankun/dist/helper'; diff --git a/jeecgboot-vue3/src/qiankun/route.ts b/jeecgboot-vue3/src/qiankun/route.ts new file mode 100644 index 000000000..30ee8ff9e --- /dev/null +++ b/jeecgboot-vue3/src/qiankun/route.ts @@ -0,0 +1,45 @@ +// import { router } from "@/router"; +// import { apps } from './apps'; +// +// export const {registerQiankunRouter} = (function () { +// +// let registered = false; +// +// /** +// * 注册qiankun路由 +// */ +// function registerQiankunRouter() { +// if (!router) { +// // 如果路由对象不存在,递归调用,直到路由对象可用 +// setTimeout(() => registerQiankunRouter(), 1); +// } else { +// registerQiankunRouterNow(); +// } +// } +// +// function registerQiankunRouterNow() { +// if (registered) { +// return; +// } +// registered = true; +// const checkQiankunRoute = (path: string) => apps.some(app => path.startsWith('/' + app.name)); +// // 添加路由守卫 +// // 路由守卫,判断是否是qiankun子应用路由 +// router.beforeEach(async (to, from, next) => { +// const isQiankunRoute = checkQiankunRoute(to.path); +// if (isQiankunRoute) { +// // 如果是qiankun子应用路由,设置meta属性 +// to.meta.isQiankunRoute = true; +// } else { +// // 如果不是qiankun子应用路由,清除meta属性 +// delete to.meta.isQiankunRoute; +// } +// next(); +// }); +// } +// +// +// return { +// registerQiankunRouter, +// } +// })(); diff --git a/jeecgboot-vue3/src/settings/registerThirdComp.ts b/jeecgboot-vue3/src/settings/registerThirdComp.ts index 2866b79d9..1e21719fc 100644 --- a/jeecgboot-vue3/src/settings/registerThirdComp.ts +++ b/jeecgboot-vue3/src/settings/registerThirdComp.ts @@ -8,12 +8,36 @@ import relativeTime from 'dayjs/plugin/relativeTime'; import customParseFormat from 'dayjs/plugin/customParseFormat'; import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; +// JVxeTable 按需加载:仅首次渲染时注册一次 +let jvxeRegistered = false; + export async function registerThirdComp(app: App) { //--------------------------------------------------------------------- - // 注册 JVxeTable 组件 - registerJVxeTable(app); - // 注册 JVxeTable 自定义组件 - await registerJVxeCustom(); + // update-begin--author:liaozhiyang---date:20260210---for:【QQYUN-13658】Jvxetable、vxetable按需加载 + // 注册 JVxeTable 组件(按需加载:首次使用 时才加载 vxe-table 与 JVxeTable) + app.component( + 'JVxeTable', + createAsyncComponent( + () => { + return import('/@/components/jeecg/JVxeTable/src/JVxeTable').then(async (m) => { + if (!jvxeRegistered) { + if (app._context.components.VxeTable) { + // 已全局注册 + } else { + const { registerJVxeTable } = await import('/@/components/jeecg/JVxeTable/src/install'); + await registerJVxeTable(app); + const { registerJVxeCustom } = await import('/@/components/JVxeCustom'); + await registerJVxeCustom(); + jvxeRegistered = true; + } + } + return m.default; + }); + }, + { loading: true } + ) + ); + // update-end--author:liaozhiyang---date:20260209---for:【QQYUN-13658】Jvxetable、vxetable按需加载 //--------------------------------------------------------------------- // 注册全局聊天表情包 // 代码逻辑说明: 【QQYUN-8241】emoji-mart-vue-fast库异步加载 diff --git a/jeecgboot-vue3/src/utils/factory/createAsyncComponent.tsx b/jeecgboot-vue3/src/utils/factory/createAsyncComponent.tsx index 4f668adee..52ffc4c19 100644 --- a/jeecgboot-vue3/src/utils/factory/createAsyncComponent.tsx +++ b/jeecgboot-vue3/src/utils/factory/createAsyncComponent.tsx @@ -3,8 +3,7 @@ import { // FunctionalComponent, CSSProperties } from 'vue'; import { Spin } from 'ant-design-vue'; -import { noop } from '/@/utils/index'; - +const noop = () => {}; // const Loading: FunctionalComponent<{ size: 'small' | 'default' | 'large' }> = (props) => { // const style: CSSProperties = { // position: 'absolute', diff --git a/jeecgboot-vue3/src/utils/iconfont2.ts b/jeecgboot-vue3/src/utils/iconfont2.ts new file mode 100644 index 000000000..6056c4355 --- /dev/null +++ b/jeecgboot-vue3/src/utils/iconfont2.ts @@ -0,0 +1,4 @@ +import { createFromIconfontCN } from '@ant-design/icons-vue'; +import '/@/assets/icons/js/iconfont2.js'; + +export const IconFont = createFromIconfontCN({}); diff --git a/jeecgboot-vue3/src/views/demo/editor/tinymce/index.vue b/jeecgboot-vue3/src/views/demo/editor/tinymce/index.vue index 9bba89bf7..006402f45 100644 --- a/jeecgboot-vue3/src/views/demo/editor/tinymce/index.vue +++ b/jeecgboot-vue3/src/views/demo/editor/tinymce/index.vue @@ -1,21 +1,29 @@ diff --git a/jeecgboot-vue3/src/views/demo/feat/icon/index.vue b/jeecgboot-vue3/src/views/demo/feat/icon/index.vue index 7e3d50f83..4213c7a0f 100644 --- a/jeecgboot-vue3/src/views/demo/feat/icon/index.vue +++ b/jeecgboot-vue3/src/views/demo/feat/icon/index.vue @@ -1,5 +1,25 @@ + + + + diff --git a/jeecgboot-vue3/src/views/demo/vextable/jvxetable/JVxeTableModal.vue b/jeecgboot-vue3/src/views/demo/vextable/jvxetable/JVxeTableModal.vue index 36b140b5f..1c4104a57 100644 --- a/jeecgboot-vue3/src/views/demo/vextable/jvxetable/JVxeTableModal.vue +++ b/jeecgboot-vue3/src/views/demo/vextable/jvxetable/JVxeTableModal.vue @@ -77,13 +77,12 @@ - diff --git a/jeecgboot-vue3/src/views/monitor/datalog/datalog.data.ts b/jeecgboot-vue3/src/views/monitor/datalog/datalog.data.ts index 07de0969a..85fe0cb53 100644 --- a/jeecgboot-vue3/src/views/monitor/datalog/datalog.data.ts +++ b/jeecgboot-vue3/src/views/monitor/datalog/datalog.data.ts @@ -1,31 +1,80 @@ import { BasicColumn, FormSchema } from '/@/components/Table'; +import { h } from 'vue'; +import { Tag, Tooltip } from 'ant-design-vue'; export const columns: BasicColumn[] = [ { title: '表名', dataIndex: 'dataTable', - width: 150, + width: 120, align: 'left', + customRender: ({ text }) => { + return h(Tag, { color: 'blue' }, () => text); + }, }, { title: '数据ID', dataIndex: 'dataId', - width: 350, + width: 260, + align: 'left', + ellipsis: true, + customRender: ({ text }) => { + return h( + 'span', + { style: 'font-family: Consolas, Monaco, monospace; font-size: 12px; color: #595959' }, + text + ); + }, }, { title: '版本号', dataIndex: 'dataVersion', - width: 100, + width: 70, + align: 'center', + customRender: ({ text }) => { + return h(Tag, { color: 'green' }, () => 'V' + text); + }, }, { title: '数据内容', dataIndex: 'dataContent', + ellipsis: true, + customRender: ({ text }) => { + if (!text) return '--'; + // 尝试格式化 JSON 显示关键字段 + try { + const obj = JSON.parse(text); + const keys = Object.keys(obj); + const preview = keys + .slice(0, 3) + .map((k) => { + const v = obj[k]; + const val = v === null || v === undefined || v === '' ? '--' : String(v); + return `${k}: ${val.length > 20 ? val.substring(0, 20) + '...' : val}`; + }) + .join(' | '); + const suffix = keys.length > 3 ? ` (+${keys.length - 3} 字段)` : ''; + return h( + Tooltip, + { title: JSON.stringify(obj, null, 2), overlayStyle: { maxWidth: '500px', whiteSpace: 'pre-wrap', fontFamily: 'Consolas, monospace', fontSize: '12px' } }, + () => h('span', { style: 'font-size: 12px; color: #595959' }, preview + suffix) + ); + } catch { + return text; + } + }, }, { title: '创建人', dataIndex: 'createBy', sorter: true, - width: 200, + width: 90, + }, + { + title: '创建时间', + dataIndex: 'createTime', + width: 120, + sorter: true, }, ]; @@ -34,12 +83,27 @@ export const searchFormSchema: FormSchema[] = [ field: 'dataTable', label: '表名', component: 'Input', - colProps: { span: 8 }, + componentProps: { + placeholder: '请输入表名', + }, + colProps: { span: 6 }, }, { field: 'dataId', label: '数据ID', component: 'Input', - colProps: { span: 8 }, + componentProps: { + placeholder: '请输入数据ID', + }, + colProps: { span: 6 }, + }, + { + field: 'createBy', + label: '创建人', + component: 'Input', + componentProps: { + placeholder: '请输入创建人', + }, + colProps: { span: 6 }, }, ]; diff --git a/jeecgboot-vue3/src/views/monitor/datalog/index.vue b/jeecgboot-vue3/src/views/monitor/datalog/index.vue index 77bd99f2b..5089a0129 100644 --- a/jeecgboot-vue3/src/views/monitor/datalog/index.vue +++ b/jeecgboot-vue3/src/views/monitor/datalog/index.vue @@ -2,34 +2,38 @@
+ diff --git a/jeecgboot-vue3/src/views/monitor/log/index.vue b/jeecgboot-vue3/src/views/monitor/log/index.vue index 96e6f3019..7582060e9 100644 --- a/jeecgboot-vue3/src/views/monitor/log/index.vue +++ b/jeecgboot-vue3/src/views/monitor/log/index.vue @@ -27,9 +27,9 @@ >
-
- - 异常堆栈:{{ record.requestParam }} +
+
异常堆栈:
+
{{ record.requestParam }}
@@ -105,19 +105,42 @@ } diff --git a/jeecgboot-vue3/src/views/monitor/log/log.data.ts b/jeecgboot-vue3/src/views/monitor/log/log.data.ts index f6233ebc8..e4973776d 100644 --- a/jeecgboot-vue3/src/views/monitor/log/log.data.ts +++ b/jeecgboot-vue3/src/views/monitor/log/log.data.ts @@ -4,8 +4,9 @@ export const columns: BasicColumn[] = [ { title: '日志内容', dataIndex: 'logContent', - width: 100, + width: 200, align: 'left', + ellipsis: true, }, { title: '操作人ID', @@ -20,12 +21,12 @@ export const columns: BasicColumn[] = [ { title: 'IP', dataIndex: 'ip', - width: 80, + width: 60, }, { title: '耗时(毫秒)', dataIndex: 'costTime', - width: 80, + width: 50, }, { title: '创建时间', @@ -36,7 +37,7 @@ export const columns: BasicColumn[] = [ { title: '客户端类型', dataIndex: 'clientType_dictText', - width: 60, + width: 50, }, ]; @@ -56,30 +57,35 @@ export const exceptionColumns: BasicColumn[] = [ { title: '异常标题', dataIndex: 'logContent', - width: 100, + width: 200, align: 'left', + ellipsis: true, }, { title: '请求地址', dataIndex: 'requestUrl', - width: 100, + width: 140, + align: 'left', + ellipsis: true, }, { - title: '请求参数', + title: '请求方法', dataIndex: 'method', - width: 60, + width: 120, + align: 'left', + ellipsis: true, }, { title: '操作人', dataIndex: 'username', - width: 60, + width: 80, customRender: ({ record }) => { - let pname = record.username; - let pid = record.userid; - if(!pname && !pid){ - return ""; + const pname = record.username; + const pid = record.userid; + if (!pname && !pid) { + return ''; } - return pname + " (账号: "+ pid + " )"; + return pname + ' (' + pid + ')'; }, }, { @@ -91,12 +97,12 @@ export const exceptionColumns: BasicColumn[] = [ title: '创建时间', dataIndex: 'createTime', sorter: true, - width: 60, + width: 80, }, { title: '客户端类型', dataIndex: 'clientType_dictText', - width: 60, + width: 50, }, ]; diff --git a/jeecgboot-vue3/src/views/monitor/mynews/DetailModal.vue b/jeecgboot-vue3/src/views/monitor/mynews/DetailModal.vue index bef81e513..fe9085374 100644 --- a/jeecgboot-vue3/src/views/monitor/mynews/DetailModal.vue +++ b/jeecgboot-vue3/src/views/monitor/mynews/DetailModal.vue @@ -33,7 +33,7 @@ -
+
前往办理
@@ -81,6 +81,7 @@ import { getToken } from '@/utils/auth'; import {defHttp} from "@/utils/http/axios"; import {$electron} from "@/electron"; + import { removeSpecialTags } from '@/utils/index'; const router = useRouter(); const glob = useGlobSetting(); const isUpdate = ref(true); @@ -279,7 +280,9 @@ function handleViewFile(filePath) { if (filePath) { console.log('glob.onlineUrl', glob.viewUrl); - let url = encodeURIComponent(encryptByBase64(filePath)); + //update-begin-author:scott---date:2026-04-16--for: 【Github #8855】修复文件预览路径处理问题,filePath需要先拼接完整URL再编码 + let url = encodeURIComponent(encryptByBase64(getFileAccessHttpUrl(filePath))); + //update-end-author:scott---date:2026-04-16--for: 【Github #8855】修复文件预览路径处理问题,filePath需要先拼接完整URL再编码 let previewUrl = `${glob.viewUrl}?url=` + url; //update-begin-author:liusq---date:2025-12-16--for: JHHB-1139桌面端 文件预览统一修改 if($electron.isElectron()){ @@ -377,6 +380,20 @@ max-width: 100%; height: auto; } + /* 修复 Word 复制内容中表格边框丢失和间隔问题 */ + .article-content { + :deep(table) { + border-collapse: collapse !important; + border-spacing: 0 !important; + } + :deep(table td), + :deep(table th) { + border: 1px solid #d0d0d0; + padding: 4px 8px; + min-width: 20px; + word-break: break-word; + } + } .basic-title{ position: relative; display: flex; diff --git a/jeecgboot-vue3/src/views/monitor/mynews/XssWhiteList.ts b/jeecgboot-vue3/src/views/monitor/mynews/XssWhiteList.ts index d8477c313..b4b687a6e 100644 --- a/jeecgboot-vue3/src/views/monitor/mynews/XssWhiteList.ts +++ b/jeecgboot-vue3/src/views/monitor/mynews/XssWhiteList.ts @@ -19,10 +19,10 @@ export const options = { a: ['style', 'target', 'href', 'title', 'rel'], img: ['style', 'src', 'title','width','height'], div: ['style'], - table: ['style', 'width', 'border', 'height'], - tr: ['style'], - td: ['style', 'width', 'colspan'], - th: ['style', 'width', 'colspan'], + table: ['style', 'width', 'border', 'height', 'cellspacing', 'cellpadding'], + tr: ['style', 'valign', 'align'], + td: ['style', 'width', 'colspan', 'rowspan', 'border', 'valign', 'align'], + th: ['style', 'width', 'colspan', 'rowspan', 'border', 'valign', 'align'], tbody: ['style'], ul: ['style'], li: ['style'], diff --git a/jeecgboot-vue3/src/views/monitor/quartz/quartz.data.ts b/jeecgboot-vue3/src/views/monitor/quartz/quartz.data.ts index 10d94f773..b54ae7fc8 100644 --- a/jeecgboot-vue3/src/views/monitor/quartz/quartz.data.ts +++ b/jeecgboot-vue3/src/views/monitor/quartz/quartz.data.ts @@ -1,6 +1,6 @@ import { BasicColumn, FormSchema } from '/@/components/Table'; import { render } from '/@/utils/common/renderUtils'; -import { JCronValidator } from '/@/components/Form'; +import JCronValidator from '/@/components/Form/src/jeecg/components/JEasyCron/validator'; export const columns: BasicColumn[] = [ { diff --git a/jeecgboot-vue3/src/views/monitor/redis/index.vue b/jeecgboot-vue3/src/views/monitor/redis/index.vue index a25e44064..574082687 100644 --- a/jeecgboot-vue3/src/views/monitor/redis/index.vue +++ b/jeecgboot-vue3/src/views/monitor/redis/index.vue @@ -1,130 +1,188 @@ + diff --git a/jeecgboot-vue3/src/views/monitor/redis/redis.data.ts b/jeecgboot-vue3/src/views/monitor/redis/redis.data.ts index d370f9405..6966c4caf 100644 --- a/jeecgboot-vue3/src/views/monitor/redis/redis.data.ts +++ b/jeecgboot-vue3/src/views/monitor/redis/redis.data.ts @@ -2,18 +2,25 @@ import { BasicColumn } from '/@/components/Table'; export const columns: BasicColumn[] = [ { - title: 'Key', + title: '配置项', dataIndex: 'key', - width: 100, + width: 120, + align: 'left', + customRender: ({ text }) => { + return text; + }, }, { - title: 'Description', + title: '说明', dataIndex: 'description', - width: 80, + width: 200, + align: 'left', + ellipsis: true, }, { - title: 'Value', + title: '值', dataIndex: 'value', width: 80, + align: 'right', }, ]; diff --git a/jeecgboot-vue3/src/views/openapi/OpenApi.data.ts b/jeecgboot-vue3/src/views/openapi/OpenApi.data.ts index a94b4da0c..bcb29be7b 100644 --- a/jeecgboot-vue3/src/views/openapi/OpenApi.data.ts +++ b/jeecgboot-vue3/src/views/openapi/OpenApi.data.ts @@ -11,35 +11,34 @@ export const columns: BasicColumn[] = [ align:"center", dataIndex: 'name' }, - { - title: '请求方法', - align:"center", - dataIndex: 'requestMethod' - }, { title: '接口地址', align:"center", - dataIndex: 'requestUrl' + dataIndex: 'requestUrl', + width: 120, }, { - title: 'IP 黑名单', + title: '请求方式', align:"center", - dataIndex: 'blackList' - }, - // { - // title: '状态', - // align:"center", - // dataIndex: 'status' - // }, - { - title: '创建人', - align:"center", - dataIndex: 'createBy' + dataIndex: 'requestMethod', + width: 100, }, { - title: '创建时间', + title: '原始接口', align:"center", - dataIndex: 'createTime' + dataIndex: 'originUrl', + ellipsis: true, + }, + { + title: 'IP 白名单', + align:"center", + dataIndex: 'whiteList', + ellipsis: true, + customRender: ({ text }) => { + if (!text) return '不限制'; + const count = text.split(/[,\n]/).filter(item => item.trim()).length; + return count + ' 条规则'; + } }, ]; //查询数据 @@ -50,8 +49,8 @@ export const searchFormSchema: FormSchema[] = [ component: 'JInput', }, { - label: "创建人", - field: "createBy", + label: "接口地址", + field: "requestUrl", component: 'JInput', }, ]; @@ -68,12 +67,35 @@ export const formSchema: FormSchema[] = [ }, }, { - label: '原始地址', + label: '原始接口', field: 'originUrl', component: 'Input', + componentProps: { + placeholder: '当前系统的原始接口地址,如 /sys/user/list', + }, + helpMessage: '当前系统中被代理的原始接口路径', + dynamicRules: () => { + return [ + { required: true, message: '请输入原始接口路径!' }, + { + validator: (_, value) => { + if (value && !value.startsWith('/')) { + return Promise.reject('原始接口路径必须以 / 开头'); + } + if (value && value.includes('//')) { + return Promise.reject('原始接口路径不能包含 //'); + } + if (value && value.includes('..')) { + return Promise.reject('原始接口路径不能包含 ..'); + } + return Promise.resolve(); + }, + }, + ]; + }, }, { - label: '请求方法', + label: '请求方式', field: 'requestMethod', component: 'JSearchSelect', componentProps:{ @@ -112,7 +134,7 @@ export const formSchema: FormSchema[] = [ }, dynamicRules: ({model,schema}) => { return [ - { required: true, message: '请输入请求方法!'}, + { required: true, message: '请输入请求方式!'}, ]; }, }, @@ -123,14 +145,36 @@ export const formSchema: FormSchema[] = [ dynamicDisabled:true }, { - label: 'IP 黑名单', - field: 'blackList', - component: 'Input', + label: 'IP 白名单', + field: 'whiteList', + helpMessage: '支持精确IP、CIDR网段(如192.168.1.0/24)、通配符(如10.2.3.*),每行一个或逗号分隔,为空则不限制', + component: 'InputTextArea', + slot: 'whiteListSlot', + componentProps: { + rows: 5, + placeholder: '示例:\n192.168.1.100\n10.0.0.0/8\n172.16.*.*', + }, + colProps: { span: 24 }, }, { - label: '请求体内容', - component:"Input", - field: 'body' + label: '备注', + field: 'comment', + component: 'InputTextArea', + componentProps: { + rows: 2, + placeholder: '请输入白名单备注说明', + }, + colProps: { span: 24 }, + }, + { + label: '接口描述', + field: 'description', + component: 'InputTextArea', + componentProps: { + rows: 3, + placeholder: '请输入接口描述', + }, + colProps: { span: 24 }, }, { label: '删除标识', @@ -240,6 +284,21 @@ export const openApiHeaderJVxeColumns: JVxeColumn[] = [ defaultValue:'', customValue: ['1','0'] }, + { + title: '参数类型', + key: 'paramType', + type: JVxeTypes.select, + width: '120px', + options: [ + { title: 'string', value: 'string' }, + { title: 'integer', value: 'integer' }, + { title: 'number', value: 'number' }, + { title: 'boolean', value: 'boolean' }, + { title: 'array', value: 'array' }, + { title: 'object', value: 'object' }, + ], + defaultValue: 'string', + }, { title: '默认值', key: 'defaultValue', @@ -248,6 +307,14 @@ export const openApiHeaderJVxeColumns: JVxeColumn[] = [ placeholder: '请输入${title}', defaultValue:'', }, + { + title: '示例值', + key: 'example', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, { title: '备注', key: 'note', @@ -284,6 +351,21 @@ export const openApiParamJVxeColumns: JVxeColumn[] = [ defaultValue:'', customValue: ['1','0'] }, + { + title: '参数类型', + key: 'paramType', + type: JVxeTypes.select, + width: '120px', + options: [ + { title: 'string', value: 'string' }, + { title: 'integer', value: 'integer' }, + { title: 'number', value: 'number' }, + { title: 'boolean', value: 'boolean' }, + { title: 'array', value: 'array' }, + { title: 'object', value: 'object' }, + ], + defaultValue: 'string', + }, { title: '默认值', key: 'defaultValue', @@ -292,6 +374,14 @@ export const openApiParamJVxeColumns: JVxeColumn[] = [ placeholder: '请输入${title}', defaultValue:'', }, + { + title: '示例值', + key: 'example', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, { title: '备注', key: 'note', @@ -301,12 +391,45 @@ export const openApiParamJVxeColumns: JVxeColumn[] = [ }, ] +export const responseFieldJVxeColumns: JVxeColumn[] = [ + { + title: '字段名', + key: 'fieldName', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '类型', + key: 'fieldType', + type: JVxeTypes.select, + width: '120px', + options: [ + { title: 'string', value: 'string' }, + { title: 'integer', value: 'integer' }, + { title: 'number', value: 'number' }, + { title: 'boolean', value: 'boolean' }, + { title: 'array', value: 'array' }, + { title: 'object', value: 'object' }, + ], + defaultValue: 'string', + }, + { + title: '说明', + key: 'fieldDesc', + type: JVxeTypes.input, + placeholder: '请输入${title}', + defaultValue: '', + }, +]; + // 高级查询数据 export const superQuerySchema = { name: {title: '接口名称',order: 0,view: 'text', type: 'string',}, - requestMethod: {title: '请求方法',order: 1,view: 'list', type: 'string',dictCode: '',}, + requestMethod: {title: '请求方式',order: 1,view: 'list', type: 'string',dictCode: '',}, requestUrl: {title: '接口地址',order: 2,view: 'text', type: 'string',}, - blackList: {title: 'IP 黑名单',order: 3,view: 'text', type: 'string',}, + whiteList: {title: 'IP 白名单',order: 3,view: 'text', type: 'string',}, status: {title: '状态',order: 5,view: 'number', type: 'number',}, createBy: {title: '创建人',order: 6,view: 'text', type: 'string',}, createTime: {title: '创建时间',order: 7,view: 'datetime', type: 'string',}, diff --git a/jeecgboot-vue3/src/views/openapi/OpenApiAuth.data.ts b/jeecgboot-vue3/src/views/openapi/OpenApiAuth.data.ts index 10c4095e9..e11945a81 100644 --- a/jeecgboot-vue3/src/views/openapi/OpenApiAuth.data.ts +++ b/jeecgboot-vue3/src/views/openapi/OpenApiAuth.data.ts @@ -1,48 +1,84 @@ -import {BasicColumn} from '/@/components/Table'; -import {FormSchema} from '/@/components/Table'; -import { rules} from '/@/utils/helper/validator'; -import { render } from '/@/utils/common/renderUtils'; -import { getWeekMonthQuarterYear } from '/@/utils'; +import { BasicColumn } from '/@/components/Table'; +import { FormSchema } from '/@/components/Table'; + //列表数据 export const columns: BasicColumn[] = [ { - title: '授权名称', - align: "center", - dataIndex: 'name' + title: '授权对象', + align: 'center', + dataIndex: 'name', }, { - title: 'AK', - align: "center", - dataIndex: 'ak' - }, - { - title: 'SK', - align: "center", - dataIndex: 'sk' + title: '访问密钥(AK)', + align: 'center', + dataIndex: 'ak', + ellipsis: true, }, { title: '创建人', - align: "center", - dataIndex: 'createBy' + align: 'center', + dataIndex: 'createBy', }, { title: '创建时间', - align: "center", - dataIndex: 'createTime' + align: 'center', + dataIndex: 'createTime', + }, +]; + +//查询数据 +export const searchFormSchema: FormSchema[] = [ + { + label: '授权对象', + field: 'name', + component: 'JInput', + }, + { + label: '访问密钥', + field: 'ak', + component: 'JInput', + }, +]; + +//授权表单数据 +export const authFormSchema: FormSchema[] = [ + { + label: '授权对象', + field: 'name', + component: 'Input', + required: true, + }, + { + label: '', + field: 'ak', + component: 'Input', + show: false, + }, + { + label: '', + field: 'sk', + component: 'Input', + show: false, + }, + { + label: '', + field: 'id', + component: 'Input', + show: false, + }, + { + label: '', + field: 'systemUserId', + component: 'Input', + show: false, }, - // { - // title: '关联系统用户名', - // align: "center", - // dataIndex: 'createBy', - // }, ]; // 高级查询数据 export const superQuerySchema = { - name: {title: '授权名称',order: 0,view: 'text', type: 'string',}, - ak: {title: 'AK',order: 1,view: 'text', type: 'string',}, - sk: {title: 'SK',order: 2,view: 'text', type: 'string',}, - createBy: {title: '关联系统用户名',order: 3,view: 'text', type: 'string',}, - createTime: {title: '创建时间',order: 4,view: 'datetime', type: 'string',}, - // systemUserId: {title: '关联系统用户名',order: 5,view: 'text', type: 'string',}, + name: { title: '授权对象', order: 0, view: 'text', type: 'string' }, + ak: { title: '访问密钥(AK)', order: 1, view: 'text', type: 'string' }, + sk: { title: '签名密钥(SK)', order: 2, view: 'text', type: 'string' }, + createBy: { title: '创建人', order: 3, view: 'text', type: 'string' }, + createTime: { title: '创建时间', order: 4, view: 'datetime', type: 'string' }, }; diff --git a/jeecgboot-vue3/src/views/openapi/OpenApiAuthList.vue b/jeecgboot-vue3/src/views/openapi/OpenApiAuthList.vue index fa9034b6d..85c26c7c7 100644 --- a/jeecgboot-vue3/src/views/openapi/OpenApiAuthList.vue +++ b/jeecgboot-vue3/src/views/openapi/OpenApiAuthList.vue @@ -1,44 +1,12 @@ - +
+ + diff --git a/jeecgboot-vue3/src/views/openapi/components/OpenApiAuthDrawer.vue b/jeecgboot-vue3/src/views/openapi/components/OpenApiAuthDrawer.vue new file mode 100644 index 000000000..8d9958247 --- /dev/null +++ b/jeecgboot-vue3/src/views/openapi/components/OpenApiAuthDrawer.vue @@ -0,0 +1,75 @@ + + + diff --git a/jeecgboot-vue3/src/views/openapi/components/OpenApiAuthForm.vue b/jeecgboot-vue3/src/views/openapi/components/OpenApiAuthForm.vue index e71b2f2a5..0291d431b 100644 --- a/jeecgboot-vue3/src/views/openapi/components/OpenApiAuthForm.vue +++ b/jeecgboot-vue3/src/views/openapi/components/OpenApiAuthForm.vue @@ -5,18 +5,18 @@ - - + + - - + + - - + + @@ -63,7 +63,7 @@ const confirmLoading = ref(false); //表单验证 const validatorRules = reactive({ - name:[{ required: true, message: '请输入授权名称!'},], + name:[{ required: true, message: '请输入授权对象!'},], systemUserId:[{ required: true, message: '请输入关联系统用户名!'},], }); const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false }); diff --git a/jeecgboot-vue3/src/views/openapi/components/OpenApiDrawer.vue b/jeecgboot-vue3/src/views/openapi/components/OpenApiDrawer.vue new file mode 100644 index 000000000..eea463046 --- /dev/null +++ b/jeecgboot-vue3/src/views/openapi/components/OpenApiDrawer.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/jeecgboot-vue3/src/views/openapi/components/OpenApiModal.vue b/jeecgboot-vue3/src/views/openapi/components/OpenApiModal.vue index e59fcab85..0e88e75ed 100644 --- a/jeecgboot-vue3/src/views/openapi/components/OpenApiModal.vue +++ b/jeecgboot-vue3/src/views/openapi/components/OpenApiModal.vue @@ -46,7 +46,6 @@ import { ref, computed, unref, reactive } from 'vue'; import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicForm, useForm } from '/@/components/Form/index'; - import { JVxeTable } from '/@/components/jeecg/JVxeTable'; import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts'; import { formSchema, openApiHeaderJVxeColumns, openApiParamJVxeColumns } from '../OpenApi.data'; import { saveOrUpdate, queryOpenApiHeader, queryOpenApiParam, getGenPath } from '../OpenApi.api'; diff --git a/jeecgboot-vue3/src/views/super/airag/aiapp/AiApp.api.ts b/jeecgboot-vue3/src/views/super/airag/aiapp/AiApp.api.ts index 8348519f1..4cd6b807e 100644 --- a/jeecgboot-vue3/src/views/super/airag/aiapp/AiApp.api.ts +++ b/jeecgboot-vue3/src/views/super/airag/aiapp/AiApp.api.ts @@ -131,7 +131,7 @@ export const generateMemoryByAppId = (params) => { url: Api.generateMemoryByAppId+'?variables='+ params.variables + '&memoryId='+ params.memoryId, adapter: 'fetch', responseType: 'stream', - timeout: 5 * 60 * 1000, + timeout: 60 * 60 * 1000, }, { isTransformResponse: false, diff --git a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/AiChat.vue b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/AiChat.vue index adbfd04a8..c48a8383c 100644 --- a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/AiChat.vue +++ b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/AiChat.vue @@ -333,6 +333,7 @@ initChartData(params.appId); } else { initChartData(); + appData.value.metadata = { izDraw: '1', defaultSelect: '0' } quickCommandData.value = [ { name: '请介绍一下JeecgBoot', descr: "请介绍一下JeecgBoot" }, { name: 'JEECG有哪些优势?', descr: "JEECG有哪些优势?" }, diff --git a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/chat.vue b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/chat.vue index f05d999d2..e3716a03b 100644 --- a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/chat.vue +++ b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/chat.vue @@ -360,8 +360,8 @@ const isThinking = ref(false); //是否开启网络搜索 const enableSearch = ref(false); - //是否显示网络搜索按钮(只有千问模型支持) - const showWebSearch = ref(false); + //是否显示网络搜索按钮(默认显示) + const showWebSearch = ref(true); //模型provider信息 const modelProvider = ref(''); //是否显示深度思考( 只有deepsee-reason支持 ) @@ -565,7 +565,7 @@ // 停止响应 const handleStop = () => { - console.log('ai 聊天:::---停止响应'); + console.log('ai 聊天:::---停止响应, 当前loading:', loading.value, ', 调用栈:', new Error().stack?.split('\n').slice(1,4).join(' <- ')); if (loading.value) { loading.value = false; } @@ -666,7 +666,7 @@ params: param, adapter: 'fetch', responseType: 'stream', - timeout: 5 * 60 * 1000, + timeout: 60 * 60 * 1000, }, { isTransformResponse: false, @@ -1037,59 +1037,34 @@ } //update-begin---author:wangshuai---date:2025-03-12---for:【QQYUN-11555】聊天时要流式显示消息--- let result = decoder.decode(value, { stream: true }); - result = buffer + result; - const lines = result.split('\n\n'); - for (let line of lines) { - if (line.startsWith('data:')) { - let content = line.replace('data:', '').trim(); - if(!content){ - continue; - } - if(!content.endsWith('}')){ - buffer = buffer + content; - continue; - } - buffer = ""; - try { - //update-begin---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态,内容不能加载出来,得刷新才能看到全部回答--- - if(content.indexOf(":::card:::") !== -1){ - content = content.replace(/\s+/g, ''); - } - let parse = JSON.parse(content); - await renderText(parse,conversationId,text,options).then((res)=>{ - text = res.returnText; - conversationId = res.conversationId; - }); - //update-end---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态,内容不能加载出来,得刷新才能看到全部回答--- - } catch (error) { - console.log('Error parsing update:', error); - } - //update-end---author:wangshuai---date:2025-03-12---for:【QQYUN-11555】聊天时要流式显示消息--- - }else{ - if(!line){ - continue; - } - if(!line.endsWith('}')){ - buffer = buffer + line; - continue; - } - buffer = ""; - //update-begin---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态,内容不能加载出来,得刷新才能看到全部回答--- - try { - if(line.indexOf(":::card:::") !== -1){ - line = line.replace(/\s+/g, ''); - } - let parse = JSON.parse(line); - await renderText(parse, conversationId, text, options).then((res) => { - text = res.returnText; - conversationId = res.conversationId; - }); - } catch (error) { - console.log('Error parsing update:', error); - } - //update-end---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态,内容不能加载出来,得刷新才能看到全部回答--- + buffer += result; + // 按SSE协议用 \n\n 分割完整事件,最后一个元素可能不完整需保留在buffer中 + const parts = buffer.split('\n\n'); + buffer = parts.pop() || ''; + for (let part of parts) { + if (!part || !part.trim()) { + continue; } + let content = part.startsWith('data:') ? part.replace('data:', '').trim() : part.trim(); + if (!content) { + continue; + } + //update-begin---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态,内容不能加载出来,得刷新才能看到全部回答--- + try { + if(content.indexOf(":::card:::") !== -1){ + content = content.replace(/\s+/g, ''); + } + let parse = JSON.parse(content); + await renderText(parse, conversationId, text, options).then((res) => { + text = res.returnText; + conversationId = res.conversationId; + }); + } catch (error) { + console.log('JSON解析失败, content长度:', content.length, ', error:', error); + } + //update-end---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态,内容不能加载出来,得刷新才能看到全部回答--- } + //update-end---author:wangshuai---date:2025-03-12---for:【QQYUN-11555】聊天时要流式显示消息--- } //update-begin---author:wangshuai---date:2025-11-05---for: 如果是断线重连并且文本为空,需要移出前面两条会话--- if(!text && isReConnect && chatData.value.length >1){ @@ -1118,7 +1093,7 @@ const result = await defHttp.get({ url: '/airag/chat/receive/' + requestId , adapter: 'fetch', responseType: 'stream', - timeout: 5 * 60 * 1000 + timeout: 60 * 60 * 1000 }, { isTransformResponse: false }).catch(async (err)=>{ loading.value = false; localStorage.removeItem('chat_requestId_' + uuid.value); @@ -1240,29 +1215,28 @@ //是否显示绘图工具 showDraw.value = metadata.izDraw === '1'; - //是否选中生成图片 - enableDraw.value = metadata.izDraw === '1'; + //是否选中生成图片(defaultSelect 为 0 时默认不选中) + const defaultSelect = metadata.defaultSelect || metadata.izDraw; + enableDraw.value = defaultSelect === '1'; + drawModelId.value = metadata.drawModelId; if (metadata && metadata.modelInfo) { modelProvider.value = metadata.modelInfo.provider || ''; modelName.value = metadata.modelInfo.modelName || ''; - // 只有千问模型支持网络搜索 - showWebSearch.value = modelProvider.value === 'QWEN'; showThink.value = modelName.value === 'deepseek-reasoner'; } else { - showWebSearch.value = false; showThink.value = false; } } catch (e) { console.error('解析模型信息失败', e); - showWebSearch.value = false; showThink.value = false; + enableDraw.value = false; } } else { - showWebSearch.value = false; showThink.value = false; showDraw.value = false; + enableDraw.value = false; } } diff --git a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/chatMessage.vue b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/chatMessage.vue index 913e519ee..c423442ae 100644 --- a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/chatMessage.vue +++ b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/chatMessage.vue @@ -1,5 +1,5 @@