From 0148f459794bc2d9595e1abe330c01a7b604f9d9 Mon Sep 17 00:00:00 2001 From: JEECG <445654970@qq.com> Date: Wed, 25 Jun 2025 16:04:02 +0800 Subject: [PATCH] =?UTF-8?q?v3.8.1=E5=8F=91=E5=B8=83=EF=BC=8C=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=89=8D=E7=AB=AF=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeecgboot-vue3/.env.development | 3 + jeecgboot-vue3/.env.prod_electron | 38 ++ jeecgboot-vue3/.env.production | 3 + jeecgboot-vue3/.npmrc | 3 + jeecgboot-vue3/LICENSE | 8 +- jeecgboot-vue3/build/script/buildConf.ts | 10 + jeecgboot-vue3/build/utils.ts | 8 + jeecgboot-vue3/build/vite/plugin/electron.ts | 34 ++ jeecgboot-vue3/build/vite/plugin/index.ts | 8 + jeecgboot-vue3/electron-builder.yaml | 24 ++ jeecgboot-vue3/electron.md | 35 ++ jeecgboot-vue3/electron/env.ts | 18 + jeecgboot-vue3/electron/icons/app.ico | Bin 0 -> 99795 bytes jeecgboot-vue3/electron/icons/installer.ico | Bin 0 -> 119221 bytes jeecgboot-vue3/electron/ipc/index.ts | 4 + jeecgboot-vue3/electron/main.ts | 56 +++ jeecgboot-vue3/electron/paths.ts | 18 + jeecgboot-vue3/electron/preload/index.ts | 5 + jeecgboot-vue3/electron/script/buildAfter.ts | 1 + jeecgboot-vue3/electron/script/buildBefore.ts | 27 ++ jeecgboot-vue3/electron/utils/index.ts | 31 ++ jeecgboot-vue3/electron/utils/tray.ts | 181 +++++++++ jeecgboot-vue3/electron/utils/window.ts | 85 ++++ jeecgboot-vue3/src/api/common/api.ts | 6 + jeecgboot-vue3/src/assets/icons/robot.svg | 1 + .../src/components/Form/src/BasicForm.vue | 13 + .../src/components/Form/src/componentMap.ts | 2 + .../Form/src/components/FormItem.vue | 13 +- .../Form/src/components/Middleware.vue | 2 +- .../src/jeecg/components/JDictSelectTag.vue | 6 + .../src/jeecg/components/JImageUpload.vue | 14 +- .../JLinkTableCard/JLinkTableCard.vue | 379 ++++++++++++++++++ .../components/LinkTableListModal.vue | 320 +++++++++++++++ .../JLinkTableCard/hooks/useLinkTable.ts | 358 +++++++++++++++++ .../src/jeecg/components/JSearchSelect.vue | 175 ++++++-- .../jeecg/components/JSelectUserByDept.vue | 5 + .../src/jeecg/components/base/JSelectBiz.vue | 9 +- .../src/components/Form/src/types/form.ts | 6 +- .../src/components/Form/src/types/index.ts | 3 +- .../src/components/Icon/src/IconPicker.vue | 23 +- .../components/JDragNotice/JDragNotice.vue | 54 +++ .../src/components/Markdown/src/Markdown.vue | 12 +- .../src/components/settings/ColumnSetting.vue | 6 +- .../Table/src/hooks/useCustomSelection.tsx | 31 +- .../Table/src/hooks/useTableScroll.ts | 34 +- .../src/components/Table/src/types/table.ts | 2 + jeecgboot-vue3/src/design/index.less | 24 ++ jeecgboot-vue3/src/electron/index.ts | 65 +++ jeecgboot-vue3/src/hooks/setting/index.ts | 3 + .../src/hooks/setting/useRootSetting.ts | 5 +- jeecgboot-vue3/src/hooks/web/useDragNotice.ts | 165 ++++++++ jeecgboot-vue3/src/hooks/web/usePage.ts | 7 +- jeecgboot-vue3/src/hooks/web/useTabs.ts | 6 + .../header/components/user-dropdown/index.vue | 3 + .../src/layouts/default/header/index.vue | 9 +- .../layouts/default/setting/SettingDrawer.tsx | 3 + .../src/layouts/default/setting/enum.ts | 1 + .../src/layouts/default/setting/handler.ts | 5 +- .../src/layouts/default/tabs/index.vue | 12 +- .../src/layouts/default/tabs/types.ts | 1 + .../layouts/default/tabs/useTabDropdown.ts | 14 +- jeecgboot-vue3/src/locales/lang/en/layout.ts | 3 +- .../src/locales/lang/zh-CN/layout.ts | 2 + jeecgboot-vue3/src/main.ts | 4 + jeecgboot-vue3/src/router/index.ts | 12 +- jeecgboot-vue3/src/router/router.ts | 8 +- jeecgboot-vue3/src/settings/projectSetting.ts | 3 + .../src/store/modules/multipleTab.ts | 56 ++- .../src/utils/dict/JDictSelectUtil.js | 3 +- jeecgboot-vue3/src/utils/dict/index.ts | 2 +- jeecgboot-vue3/src/utils/env.ts | 20 + jeecgboot-vue3/src/utils/http/axios/index.ts | 6 + .../views/demo/jeecg/jeecgComponents.data.ts | 6 + .../src/views/super/airag/aiapp/AiApp.api.ts | 24 +- .../src/views/super/airag/aiapp/AiApp.data.ts | 2 +- .../src/views/super/airag/aiapp/AiAppList.vue | 175 ++++++-- .../views/super/airag/aiapp/chat/AiChat.vue | 45 ++- .../super/airag/aiapp/chat/AiChatIcon.vue | 12 +- .../src/views/super/airag/aiapp/chat/chat.vue | 331 +++++++++++---- .../super/airag/aiapp/chat/chatMessage.vue | 153 ++++++- .../views/super/airag/aiapp/chat/chatText.vue | 157 +++++++- .../aiapp/chat/components/ImageViewer.vue | 71 ++++ .../views/super/airag/aiapp/chat/js/chat.js | 97 +++-- .../super/airag/aiapp/chat/presetQuestion.vue | 20 +- .../views/super/airag/aiapp/chat/slide.vue | 25 +- .../aiapp/chat/style/github-markdown.less | 6 +- .../aiapp/components/AiAppAddFlowModal.vue | 10 +- .../components/AiAppAddKnowledgeModal.vue | 34 +- .../components/AiAppGeneratedPromptModal.vue | 76 +++- .../airag/aiapp/components/AiAppModal.vue | 39 +- .../aiapp/components/AiAppSettingModal.vue | 211 +++++++--- .../airag/aiknowledge/AiKnowledgeBase.api.ts | 13 + .../aiknowledge/AiKnowledgeBase.api.util.tsx | 24 ++ .../airag/aiknowledge/AiKnowledgeBaseList.vue | 33 +- .../components/AiKnowledgeBaseModal.vue | 10 + .../components/AiTextDescModal.vue | 25 +- .../components/AiragKnowledgeDocListModal.vue | 260 +++++++++--- .../views/super/airag/aimodel/AiModelList.vue | 17 +- .../airag/aimodel/components/AiModelModal.vue | 75 +++- .../super/airag/aimodel/components/model.json | 2 +- .../views/super/airag/aimodel/model.api.ts | 12 +- .../src/views/super/airag/ocr/AiOcr.api.ts | 46 +++ .../src/views/super/airag/ocr/AiOcr.data.ts | 81 ++++ .../src/views/super/airag/ocr/AiOcrList.vue | 104 +++++ .../ocr/components/AiOcrAnalysisModal.vue | 116 ++++++ .../super/airag/ocr/components/AiOcrModal.vue | 62 +++ .../address/components/DepartLeftTree.vue | 18 +- .../src/views/system/depart/depart.data.ts | 6 + .../departUser/components/DepartTree.vue | 130 +++++- .../src/views/system/menu/menu.data.ts | 3 + .../tenant/pack/TenantDefaultPackList.vue | 9 +- .../tenant/pack/TenantPackMenuModal.vue | 2 +- .../src/views/system/tenant/tenant.data.ts | 44 +- .../src/views/system/user/UserDrawer.vue | 4 +- .../src/views/system/user/user.data.ts | 7 +- .../system/usersetting/AccountSetting.vue | 20 +- .../system/usersetting/UserSetting.api.ts | 10 + .../commponents/UserPasswordNotBindPhone.vue | 115 ++++++ jeecgboot-vue3/types/config.d.ts | 7 + jeecgboot-vue3/types/global.d.ts | 4 +- 120 files changed, 4783 insertions(+), 486 deletions(-) create mode 100644 jeecgboot-vue3/.env.prod_electron create mode 100644 jeecgboot-vue3/build/vite/plugin/electron.ts create mode 100644 jeecgboot-vue3/electron-builder.yaml create mode 100644 jeecgboot-vue3/electron.md create mode 100644 jeecgboot-vue3/electron/env.ts create mode 100644 jeecgboot-vue3/electron/icons/app.ico create mode 100644 jeecgboot-vue3/electron/icons/installer.ico create mode 100644 jeecgboot-vue3/electron/ipc/index.ts create mode 100644 jeecgboot-vue3/electron/main.ts create mode 100644 jeecgboot-vue3/electron/paths.ts create mode 100644 jeecgboot-vue3/electron/preload/index.ts create mode 100644 jeecgboot-vue3/electron/script/buildAfter.ts create mode 100644 jeecgboot-vue3/electron/script/buildBefore.ts create mode 100644 jeecgboot-vue3/electron/utils/index.ts create mode 100644 jeecgboot-vue3/electron/utils/tray.ts create mode 100644 jeecgboot-vue3/electron/utils/window.ts create mode 100644 jeecgboot-vue3/src/assets/icons/robot.svg create mode 100644 jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/JLinkTableCard.vue create mode 100644 jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/components/LinkTableListModal.vue create mode 100644 jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/hooks/useLinkTable.ts create mode 100644 jeecgboot-vue3/src/components/JDragNotice/JDragNotice.vue create mode 100644 jeecgboot-vue3/src/electron/index.ts create mode 100644 jeecgboot-vue3/src/hooks/web/useDragNotice.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/aiapp/chat/components/ImageViewer.vue create mode 100644 jeecgboot-vue3/src/views/super/airag/aiknowledge/AiKnowledgeBase.api.util.tsx create mode 100644 jeecgboot-vue3/src/views/super/airag/ocr/AiOcr.api.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/ocr/AiOcr.data.ts create mode 100644 jeecgboot-vue3/src/views/super/airag/ocr/AiOcrList.vue create mode 100644 jeecgboot-vue3/src/views/super/airag/ocr/components/AiOcrAnalysisModal.vue create mode 100644 jeecgboot-vue3/src/views/super/airag/ocr/components/AiOcrModal.vue create mode 100644 jeecgboot-vue3/src/views/system/usersetting/commponents/UserPasswordNotBindPhone.vue diff --git a/jeecgboot-vue3/.env.development b/jeecgboot-vue3/.env.development index 3a6342764..fb3498c90 100644 --- a/jeecgboot-vue3/.env.development +++ b/jeecgboot-vue3/.env.development @@ -28,3 +28,6 @@ VITE_APP_SUB_jeecg-app-1 = '//localhost:8092' # 全局隐藏哪些布局。可选属性:sider,header,multi-tabs;多个用逗号隔开 #VITE_GLOB_HIDE_LAYOUT_TYPES=sider,header,multi-tabs + +# 在线文档编辑版本。可选属性:wps, offlineWps(离线版) ,onlyoffice +VITE_GLOB_ONLINE_DOCUMENT_VERSION=wps diff --git a/jeecgboot-vue3/.env.prod_electron b/jeecgboot-vue3/.env.prod_electron new file mode 100644 index 000000000..ca0133578 --- /dev/null +++ b/jeecgboot-vue3/.env.prod_electron @@ -0,0 +1,38 @@ +# 是否启用mock +VITE_USE_MOCK = false + +# 后台接口父地址(必填) +# 【Electron下需要与 VITE_GLOB_DOMAIN_URL 配置保持一致】 +VITE_GLOB_API_URL=https://api3.boot.jeecg.com + +# 后台接口全路径地址(必填) +VITE_GLOB_DOMAIN_URL=https://api3.boot.jeecg.com + +# 接口父路径前缀 +VITE_GLOB_API_URL_PREFIX= + +# 在线文档编辑版本。可选属性:wps, offlineWps(离线版), onlyoffice +VITE_GLOB_ONLINE_DOCUMENT_VERSION=wps + +# 全局隐藏哪些布局。可选属性:sider,header,multi-tabs;多个用逗号隔开 +#VITE_GLOB_HIDE_LAYOUT_TYPES=sider,header,multi-tabs + +# ----------------------------------------- +# ------------ 以下参数不建议修改 ------------ +# ----------------------------------------- + +# 发布路径 +# 【election下只能是 . 开头的相对路径,建议为 ./ 】 +VITE_PUBLIC_PATH = ./ + +# 是否启用gzip或brotli压缩 +# 选项值: gzip | brotli | none +# 如果需要多个可以使用“,”分隔 +# 【electron下由于是本地html文件访问,所以不需要压缩】 +VITE_BUILD_COMPRESS = 'none' + +# 使用压缩时是否删除原始文件,默认为false +VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false + +# ※ 请勿修改此项 ※ +VITE_GLOB_RUN_PLATFORM=electron diff --git a/jeecgboot-vue3/.env.production b/jeecgboot-vue3/.env.production index aef68837f..74f6ee81d 100644 --- a/jeecgboot-vue3/.env.production +++ b/jeecgboot-vue3/.env.production @@ -29,3 +29,6 @@ VITE_GLOB_API_URL_PREFIX= # 全局隐藏哪些布局。可选属性:sider,header,multi-tabs;多个用逗号隔开 #VITE_GLOB_HIDE_LAYOUT_TYPES=sider,header,multi-tabs + +# 在线文档编辑版本。可选属性:wps, offlineWps(离线版), onlyoffice +VITE_GLOB_ONLINE_DOCUMENT_VERSION=wps diff --git a/jeecgboot-vue3/.npmrc b/jeecgboot-vue3/.npmrc index cf0404245..24e860038 100644 --- a/jeecgboot-vue3/.npmrc +++ b/jeecgboot-vue3/.npmrc @@ -1,2 +1,5 @@ shamefully-hoist=true strict-peer-dependencies=false + +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ diff --git a/jeecgboot-vue3/LICENSE b/jeecgboot-vue3/LICENSE index 97c181fa5..de1700ef5 100644 --- a/jeecgboot-vue3/LICENSE +++ b/jeecgboot-vue3/LICENSE @@ -19,15 +19,15 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software. -JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京,地址:中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱:jeecgos@163.com + JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京,地址:中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱:jeecgos@163.com 本软件受适用的国家软件著作权法(包括国际条约)和开源协议 双重保护许可。 - + 开源协议中文释意如下: 1.JeecgBoot开源版本无任何限制,在遵循本开源协议条款下,允许商用使用,不会造成侵权行为。 2.允许基于本平台软件开展业务系统开发。 3.在任何情况下,您不得使用本软件开发可能被认为与本软件竞争的软件。 - + 最终解释权归:http://www.jeecg.com diff --git a/jeecgboot-vue3/build/script/buildConf.ts b/jeecgboot-vue3/build/script/buildConf.ts index 1892af3c1..4a1d824f0 100644 --- a/jeecgboot-vue3/build/script/buildConf.ts +++ b/jeecgboot-vue3/build/script/buildConf.ts @@ -35,6 +35,16 @@ function createConfig(params: CreateConfigParams) { console.log(colors.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`); console.log(colors.gray(OUTPUT_DIR + '/' + colors.green(configFileName)) + '\n'); + + // update-begin--author:sunjianlei---date:20250423---for:【QQYUN-9685】构建 electron 桌面应用 + // 如果是 Electron 环境,还需要将配置文件写入到 JSON 文件中 + if (config.VITE_GLOB_RUN_PLATFORM === 'electron') { + writeFileSync(getRootPath(`${OUTPUT_DIR}/electron/env.json`), JSON.stringify(config)); + console.log(colors.cyan(`✨ [${pkg.name}]`) + ` - electron env file is build successfully:`); + console.log(colors.gray(OUTPUT_DIR + '/' + colors.green('electron/env.json')) + '\n'); + } + // update-end----author:sunjianlei---date:20250423---for:【QQYUN-9685】构建 electron 桌面应用 + } catch (error) { console.log(colors.red('configuration file configuration file failed to package:\n' + error)); } diff --git a/jeecgboot-vue3/build/utils.ts b/jeecgboot-vue3/build/utils.ts index 902d4bb87..9490116ff 100644 --- a/jeecgboot-vue3/build/utils.ts +++ b/jeecgboot-vue3/build/utils.ts @@ -49,6 +49,14 @@ export function wrapperEnv(envConf: Recordable): ViteEnv { * 获取当前环境下生效的配置文件名 */ function getConfFiles() { + + // update-begin--author:sunjianlei---date:20250411---for:【QQYUN-9685】构建 electron 桌面应用 + const {VITE_GLOB_RUN_PLATFORM} = process.env + if (VITE_GLOB_RUN_PLATFORM === 'electron') { + return ['.env', '.env.prod_electron']; + } + // update-end----author:sunjianlei---date:20250411---for:【QQYUN-9685】构建 electron 桌面应用 + const script = process.env.npm_lifecycle_script; // update-begin--author:liaozhiyang---date:20240326---for:【QQYUN-8690】修正获取当前环境下的文件名 const reg = new RegExp('NODE_ENV=([a-z_\\d]+)'); diff --git a/jeecgboot-vue3/build/vite/plugin/electron.ts b/jeecgboot-vue3/build/vite/plugin/electron.ts new file mode 100644 index 000000000..5b597e92b --- /dev/null +++ b/jeecgboot-vue3/build/vite/plugin/electron.ts @@ -0,0 +1,34 @@ +// import electron from 'vite-plugin-electron/simple' +// +// export function configElectronPlugin(_viteEnv: ViteEnv, isBuild: boolean) { +// return electron({ +// main: { +// // 主进程入口 +// entry: 'electron/main.ts', +// vite: { +// build: { +// sourcemap: !isBuild, +// outDir: 'dist/electron', +// }, +// }, +// onstart: ({startup}) => { +// // 开发热重载 +// startup() +// }, +// }, +// preload: { +// input: 'electron/preload/index.ts', +// vite: { +// build: { +// sourcemap: !isBuild, +// outDir: 'dist/electron/preload', +// }, +// }, +// onstart: ({startup}) => { +// // 开发热重载 +// startup() +// }, +// } +// }) +// +// } diff --git a/jeecgboot-vue3/build/vite/plugin/index.ts b/jeecgboot-vue3/build/vite/plugin/index.ts index 7d068ef68..bce656193 100644 --- a/jeecgboot-vue3/build/vite/plugin/index.ts +++ b/jeecgboot-vue3/build/vite/plugin/index.ts @@ -16,6 +16,8 @@ import { configVisualizerConfig } from './visualizer'; import { configThemePlugin } from './theme'; import { configSvgIconsPlugin } from './svgSprite'; import { configQiankunMicroPlugin } from './qiankunMicro'; +// // electron plugin +// import { configElectronPlugin } from "./electron"; // //预编译加载插件(不支持vite3作废) // import OptimizationPersist from 'vite-plugin-optimize-persist'; // import PkgConfig from 'vite-plugin-package-config'; @@ -68,6 +70,12 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, isQiankunM vitePlugins.push(...configQiankunMicroPlugin(viteEnv)) } + // // electron plugin + // const isElectron = viteEnv.VITE_GLOB_RUN_PLATFORM === 'electron'; + // if (isElectron) { + // vitePlugins.push(configElectronPlugin(viteEnv, isBuild)) + // } + // The following plugins only work in the production environment if (isBuild) { diff --git a/jeecgboot-vue3/electron-builder.yaml b/jeecgboot-vue3/electron-builder.yaml new file mode 100644 index 000000000..05a01e0a1 --- /dev/null +++ b/jeecgboot-vue3/electron-builder.yaml @@ -0,0 +1,24 @@ +appId: 'com.jeecg.boot3' +# 产品名称 +productName: 'jeecgboot' +files: + # 仅包含 dist 目录下所有文件 + - 'dist/**/*' + # 特别排除 node_modules 目录 + - '!node_modules' +directories: + # 输出目录 + output: 'dist-electron' +win: + # win exe 程序图标 + icon: 'electron/icons/app.ico' + artifactName: 'jeecgboot-setup-${version}.exe' +# 安装包配置 +nsis: + oneClick: false + # 是否允许用户选择安装目录 + allowToChangeInstallationDirectory: true + # 是否创建桌面快捷方式 + createDesktopShortcut: true + # 安装程序的图标 + installerIcon: 'electron/icons/installer.ico' diff --git a/jeecgboot-vue3/electron.md b/jeecgboot-vue3/electron.md new file mode 100644 index 000000000..c10e4c29a --- /dev/null +++ b/jeecgboot-vue3/electron.md @@ -0,0 +1,35 @@ +# Electron桌面应用打包 + +- 1.安装依赖很慢,得10分钟左右 +- 2.electron桌面应用打包文档 + https://help.jeecg.com/ui/setup/electron-build +- 3.临时注释掉electron功能 + 注释代码:build/vite/plugin/electron.ts + 修改build/vite/plugin/index.ts,搜索`electron plugin`注释相关逻辑代码 + 修改package.json删除相关依赖 + +```yaml +{ + "main": "dist/electron/main.js", + "scripts": { + "electron:dev": "cross-env VITE_GLOB_RUN_PLATFORM=electron npm run dev", + "electron:build-all": "npm run electron:build-web && npm run electron:build-app", + "electron:build-web": "cross-env VITE_GLOB_RUN_PLATFORM=electron NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 vite build --mode prod_electron && cross-env VITE_GLOB_RUN_PLATFORM=electron esno ./build/script/postBuild.ts && esno ./build/script/copyChat.ts", + "electron:build-app": "esno ./electron/script/buildBefore.ts && electron-builder && esno ./electron/script/buildAfter.ts", + }, + "devDependencies": { + "electron": "35.1.4", + "electron-builder": "^26.0.12", + "vite-plugin-electron": "^0.29.0", + }, +} + +``` + + +# Electron桌面通知示例和代码位置 + +1. 代码位置:electron/utils/tray.ts +2. 发送系统通知调用:sendDesktopNotice +3. 开始托盘图标闪动调用:startBlink +4. 停止托盘图标闪动调用:stopBlink diff --git a/jeecgboot-vue3/electron/env.ts b/jeecgboot-vue3/electron/env.ts new file mode 100644 index 000000000..e6add3251 --- /dev/null +++ b/jeecgboot-vue3/electron/env.ts @@ -0,0 +1,18 @@ +// 不能直接使用 process.env,会报错 +export const $ps = process; + +export const isDev = !!$ps.env.VITE_DEV_SERVER_URL; + +export const $env = getEnv(); + +function getEnv() { + if (isDev) { + return $ps.env; + } + // 非开发环境,从 JSON 文件中获取环境变量 + const env = require('./env.json'); + return { + ...$ps.env, + ...env, + }; +} diff --git a/jeecgboot-vue3/electron/icons/app.ico b/jeecgboot-vue3/electron/icons/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0fbd91b499d333b477808ef77c64bae74d7009c GIT binary patch literal 99795 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 literal 0 HcmV?d00001 diff --git a/jeecgboot-vue3/electron/icons/installer.ico b/jeecgboot-vue3/electron/icons/installer.ico new file mode 100644 index 0000000000000000000000000000000000000000..001572edf4d7f643b12b0ba796ed9930258a87be GIT binary patch literal 119221 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 IM6N<$f<9hPs{jB1 literal 0 HcmV?d00001 diff --git a/jeecgboot-vue3/electron/ipc/index.ts b/jeecgboot-vue3/electron/ipc/index.ts new file mode 100644 index 000000000..c8427aef0 --- /dev/null +++ b/jeecgboot-vue3/electron/ipc/index.ts @@ -0,0 +1,4 @@ +import {ipcMain} from 'electron' +import {openInBrowser} from "../utils"; + +ipcMain.on('open-in-browser', (event, url) => openInBrowser(url)); diff --git a/jeecgboot-vue3/electron/main.ts b/jeecgboot-vue3/electron/main.ts new file mode 100644 index 000000000..d4dc81d08 --- /dev/null +++ b/jeecgboot-vue3/electron/main.ts @@ -0,0 +1,56 @@ +import './ipc'; + +import { app, BrowserWindow, Menu } from 'electron'; +import { isDev } from './env'; +import { createMainWindow, createIndexWindow } from './utils/window'; +import { getAppInfo} from "./utils"; + +// 隐藏所有菜单 +Menu.setApplicationMenu(null); + +let mainWindow: BrowserWindow | null = null; + +function main() { + mainWindow = createMainWindow(); + return mainWindow; +} + +// 非开发环境,只允许一个实例运行 +if (!isDev) { + // 是否取得了单一实例锁 + const gotTheLock = app.requestSingleInstanceLock(); + + if (gotTheLock) { + app.on('second-instance', () => { + // 开启一个新的窗口 + createIndexWindow(); + }); + } else { + // 没有取得单一实例锁,则退出应用 + app.exit(0); + } +} + +// 生命周期管理 +app.whenReady().then(() => { + // 获取应用信息 + const $appInfo = getAppInfo(); + if ($appInfo?.productName && $appInfo?.appId) { + app.setName($appInfo.productName); + app.setAppUserModelId($appInfo.appId); + } + + main(); + + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + main(); + } + }); +}); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); diff --git a/jeecgboot-vue3/electron/paths.ts b/jeecgboot-vue3/electron/paths.ts new file mode 100644 index 000000000..ff3c991f0 --- /dev/null +++ b/jeecgboot-vue3/electron/paths.ts @@ -0,0 +1,18 @@ +import path from 'path' +import {isDev} from "./env"; + +export const _PATHS = getPaths() + +function getPaths() { + const _root = __dirname; + const publicRoot = path.join(_root, isDev ? '../../public' : '..'); + const preloadRoot = path.join(_root, 'preload') + + return { + electronRoot: _root, + publicRoot, + preloadRoot, + + appIcon: path.join(_root, `icons/app.ico`).replace(/[\\/]dist[\\/]/, '/'), + } +} \ No newline at end of file diff --git a/jeecgboot-vue3/electron/preload/index.ts b/jeecgboot-vue3/electron/preload/index.ts new file mode 100644 index 000000000..ce7c02ff4 --- /dev/null +++ b/jeecgboot-vue3/electron/preload/index.ts @@ -0,0 +1,5 @@ +import {contextBridge, ipcRenderer} from 'electron' + +contextBridge.exposeInMainWorld('_ELECTRON_PRELOAD_UTILS_', { + openInBrowser: (url: string) => ipcRenderer.send('open-in-browser', url), +}); diff --git a/jeecgboot-vue3/electron/script/buildAfter.ts b/jeecgboot-vue3/electron/script/buildAfter.ts new file mode 100644 index 000000000..50024eac0 --- /dev/null +++ b/jeecgboot-vue3/electron/script/buildAfter.ts @@ -0,0 +1 @@ +console.log('build elctron is done.'); \ No newline at end of file diff --git a/jeecgboot-vue3/electron/script/buildBefore.ts b/jeecgboot-vue3/electron/script/buildBefore.ts new file mode 100644 index 000000000..d1eac5dc6 --- /dev/null +++ b/jeecgboot-vue3/electron/script/buildBefore.ts @@ -0,0 +1,27 @@ +import path from 'path'; +import fs from 'fs'; + +const root = path.join(__dirname, '../../'); +const electronDistRoot = path.join(root, 'dist/electron'); + +let yamlName = 'electron-builder.yaml'; +const sourcePath = fs.readFileSync(path.join(root, yamlName), 'utf-8'); + +try { + // 通过正则表达式匹配 appId 和 productName + const appIdMatch = sourcePath.match(/appId:\s*['"]([^'"]+)['"]/); + const productNameMatch = sourcePath.match(/productName:\s*['"]([^'"]+)['"]/); + if (appIdMatch && productNameMatch) { + const fileContent = `${appIdMatch[0]}\n${productNameMatch[0]}`; + yamlName = 'env.yaml'; + const targetPath = path.join(electronDistRoot, yamlName); + fs.writeFileSync(targetPath, fileContent, 'utf-8'); + console.log(`✨ write dist ${yamlName} successfully.`); + } else { + throw new Error('appId or productName not found'); + } +} catch (e) { + console.error(e); + console.error(`请检查 ${yamlName} 是否存在,或者内容是否正确`); + process.exit(1); +} diff --git a/jeecgboot-vue3/electron/utils/index.ts b/jeecgboot-vue3/electron/utils/index.ts new file mode 100644 index 000000000..6bc206de6 --- /dev/null +++ b/jeecgboot-vue3/electron/utils/index.ts @@ -0,0 +1,31 @@ +import fs from 'fs'; +import path from 'path' +import {shell, dialog} from 'electron' +import {_PATHS} from "../paths"; +import {isDev} from "../env"; + +// 通过浏览器打开链接 +export function openInBrowser(url: string) { + return shell.openExternal(url); +} + + +export function getAppInfo(): any { + try { + const yamlPath = isDev ? path.join(_PATHS.publicRoot, '../electron-builder.yaml') : path.join(_PATHS.electronRoot, 'env.yaml'); + const yamlContent = fs.readFileSync(yamlPath, 'utf-8'); + // 通过正则表达式匹配 appId 和 productName + const appIdMatch = yamlContent.match(/appId:\s*['"]([^'"]+)['"]/); + const productNameMatch = yamlContent.match(/productName:\s*['"]([^'"]+)['"]/); + const appId = appIdMatch ? appIdMatch[1] : ''; + const productName = productNameMatch ? productNameMatch[1] : ''; + return {appId, productName} + } catch (e) { + dialog.showMessageBoxSync(null, { + type: 'error', + title: '错误', + message: '应用启动失败,请从官网下载最新版本安装包后重新安装!', + }); + process.exit(-1); + } +} diff --git a/jeecgboot-vue3/electron/utils/tray.ts b/jeecgboot-vue3/electron/utils/tray.ts new file mode 100644 index 000000000..2d9139152 --- /dev/null +++ b/jeecgboot-vue3/electron/utils/tray.ts @@ -0,0 +1,181 @@ +// tray = 系统托盘 +import path from 'path'; +import {Tray, Menu, app, dialog, nativeImage, BrowserWindow, Notification} from 'electron'; +import {_PATHS} from '../paths'; +import {$env, isDev} from '../env'; + +const TrayIcons = { + normal: nativeImage.createFromPath(path.join(_PATHS.publicRoot, 'logo.png')), + empty: nativeImage.createEmpty(), +}; + +// 创建托盘图标 +export function createTray(win: BrowserWindow) { + const tray = new Tray(TrayIcons.normal); + + const TrayUtils = useTray(tray, win); + + tray.setToolTip($env.VITE_GLOB_APP_TITLE! + (isDev ? ' (开发环境)' : '')); + + // 左键托盘图标显示主窗口 + tray.on('click', () => TrayUtils.showMainWindow()); + // 右键托盘图标显示托盘菜单 + tray.on('right-click', () => showTrayContextMenu()); + + function showTrayContextMenu() { + const trayContextMenu = getTrayMenus(win, TrayUtils); + // 弹出托盘菜单,不使用 setContextMenu 方法是因为要实时更新菜单内容 + tray.popUpContextMenu(trayContextMenu); + } +} + +export function useTray(tray: Tray, win: BrowserWindow) { + let isBlinking = false; + let blinkTimer: NodeJS.Timeout | null = null; + + function showMainWindow() { + win.show(); + } + + // 开始闪动 + function startBlink() { + isBlinking = true; + tray.setImage(TrayIcons.empty); + blinkTimer = setTimeout(() => { + tray.setImage(TrayIcons.normal); + setTimeout(() => { + if (isBlinking) { + startBlink(); + } + }, 500); + }, 500); + } + + // 结束闪动 + function stopBlink() { + isBlinking = false; + if (blinkTimer) { + clearTimeout(blinkTimer); + blinkTimer = null; + } + tray.setImage(TrayIcons.normal); + } + + // 发送桌面通知 + function sendDesktopNotice() { + // 判断是否支持桌面通知 + if (!Notification.isSupported()) { + // todo 实际开发中不需要提示,直接返回或者换一种提示方式 + dialog.showMessageBoxSync(win, { + type: 'error', + title: '错误', + message: '当前系统不支持桌面通知', + }); + return; + } + const ins = new Notification({ + title: '通知标题', + subtitle: '通知副标题', + body: '通知内容第一行\n通知内容第二行', + icon: TrayIcons.normal.resize({width: 32, height: 32}), + }); + + ins.on('click', () => { + dialog.showMessageBoxSync(win, { + type: 'info', + title: '提示', + message: '通知被点击', + }); + }); + + ins.show(); + } + + return { + showMainWindow, + + startBlink, + stopBlink, + isBlinking: () => isBlinking, + + sendDesktopNotice, + }; +} + +const MenuIcon = { + exit: nativeImage + .createFromDataURL( + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACJ0lEQVR4nH1TzWvUQBRP7fpxsWqVXsSLiAevRWhhN28msRJo981kay4WRBCF/QdEFJpbaUHw4kFBQTwUKX4gKh48KPiBBcGLJ1F0uzPZ7ibWXf0DIjObielS+mDIm/fxm9/85sWyBixN06E0CIaV3wB2XhC8puOWNZSG4Y7B+k2mi7Kl9l2n9rHnzvbWJoLRYn7r5jTViQjwzM8ynlC+AFyVgN2NU8G+Rnn6QETx3FfP223A/jeHfWqCsAUJ7Hlryh9Te0nYqiDsz9rE6VHVIABvNwEf/ADYk4OsZPeVFbwiCHtcZBVR9k4CJhJmDuUxwEVJ8H4fINOkC9Vjbeq/UTR1IgPturX3f93Z35+B7ddxgJL6dih/skF9zE9KCJ//5bDLpii1+npIuzolKTubC5gBxzarJo6vWWjrUP+etFlF+ds9lRFOXalN+NPEmxvRDS3KH34v8+PFIgNmTh0EahH+InGCwzoQEbYcuTMnlR8aYbaxGHFvRNiznssP6sA65UsxrdU1+hYnFhlpAGAkdvzlPLFu88mY8pcrVjCsxcqGapC2eYW249/tUH4xS4QaVQLeigi/YWJqPl4DlNRSrAwzSaoXIspeWUYrI9qXINglgT1qAt5JPG+kkNN5BSAJuyoJfhAVdmST4PlPBFASNs6rIgnspqC8HlF+SQAuRQTfKpYiEy6fwuIdP42P71T+t0l/TBKcE8AXm4DXBfB6w50+apgUhf4HZ5j+Z5+zNTAAAAAASUVORK5CYII=' + ) + .resize({ + width: 16, + height: 16, + }), +}; + +// 设置托盘菜单 +function getTrayMenus(win: BrowserWindow, TrayUtils: ReturnType) { + const {startBlink, stopBlink, sendDesktopNotice} = TrayUtils; + const isBlinking = TrayUtils.isBlinking(); + + return Menu.buildFromTemplate([ + ...(isDev + ? [ + { + label: '开发工具', + submenu: [ + { + label: '以下菜单仅显示在开发环境', + sublabel: '当前为开发环境', + enabled: false, + }, + {type: 'separator'}, + { + label: '切换 DevTools', + click: () => win.webContents.toggleDevTools(), + }, + { + label: `托盘图标${isBlinking ? '停止' : '开始'}闪烁`, + sublabel: '模拟新消息提醒', + click: () => (isBlinking ? stopBlink() : startBlink()), + }, + { + label: '发送桌面通知示例', + click: () => sendDesktopNotice(), + }, + ], + }, + {type: 'separator'}, + ] + : ([] as any)), + { + label: '显示主窗口', + // 文件图标 + icon: TrayIcons.normal.resize({width: 16, height: 16}), + click: () => win.show(), + }, + {type: 'separator'}, + { + label: '退出', + // base64图标 + icon: MenuIcon.exit, + click: () => { + // 弹出是否确认退出提示框 + const choice = dialog.showMessageBoxSync(win, { + type: 'question', + title: '提示', + message: '确定要退出应用吗?', + buttons: ['退出', '取消'], + defaultId: 1, + cancelId: 1, + noLink: true, + }); + // 用户选择了退出,直接 exit + if (choice === 0) { + // global.isQuitting = true; + app.exit(0); + } + }, + }, + ]); +} diff --git a/jeecgboot-vue3/electron/utils/window.ts b/jeecgboot-vue3/electron/utils/window.ts new file mode 100644 index 000000000..368a14404 --- /dev/null +++ b/jeecgboot-vue3/electron/utils/window.ts @@ -0,0 +1,85 @@ +import type {BrowserWindowConstructorOptions} from 'electron'; +import {BrowserWindow, dialog} from 'electron'; +import path from 'path'; +import {_PATHS} from '../paths'; +import {$env, isDev} from '../env'; +import {createTray} from './tray'; + +// 创建窗口 +export function createBrowserWindow(options?: BrowserWindowConstructorOptions) { + const win = new BrowserWindow({ + width: 1200, + height: 800, + webPreferences: { + preload: path.join(_PATHS.preloadRoot, 'index.js'), + nodeIntegration: false, + contextIsolation: true, + }, + // 应用图标 + icon: isDev ? _PATHS.appIcon : void 0, + ...options, + }); + + // 设置窗口打开处理器 + win.webContents.setWindowOpenHandler(({url}) => { + const win = createBrowserWindow(); + win.loadURL(url); + // 阻止创建新窗口,因为已经被接管 + return {action: 'deny'}; + }); + + // 当 beforeunload 阻止窗口关闭时触发 + win.webContents.on('will-prevent-unload', () => { + const choice = dialog.showMessageBoxSync(win, { + type: 'question', + title: '确认关闭吗?', + message: '系统可能不会保存您所做的更改。', + buttons: ['关闭', '取消'], + defaultId: 1, + cancelId: 1, + noLink: true, + }); + // 用户选择了关闭,直接销毁窗口 + if (choice === 0) { + win.destroy(); + } + }); + + return win; +} + +// 创建主窗口、系统托盘 +export function createMainWindow() { + const win = createIndexWindow() + + // 设置系统托盘图标 + createTray(win); + + // 主窗口尝试关闭时,默认不直接退出应用,而是隐藏到托盘 + win.on('close', (event) => { + event.preventDefault(); + win.hide(); + }); + + return win; +} + +// 创建索引窗口 +export function createIndexWindow() { + const win = createBrowserWindow({ + width: 1600, + height: 1000, + title: $env.VITE_GLOB_APP_TITLE!, + }); + + // 开发环境加载Vite服务,生产加载打包文件 + if (isDev) { + win.loadURL($env.VITE_DEV_SERVER_URL!) + // 开发环境下,自动打开调试工具 + // win.webContents.openDevTools() + } else { + win.loadFile(path.join(_PATHS.publicRoot, 'index.html')); + } + + return win; +} diff --git a/jeecgboot-vue3/src/api/common/api.ts b/jeecgboot-vue3/src/api/common/api.ts index 47d5cfbbf..19365ddce 100644 --- a/jeecgboot-vue3/src/api/common/api.ts +++ b/jeecgboot-vue3/src/api/common/api.ts @@ -14,6 +14,7 @@ enum Api { getDictItems = '/sys/dict/getDictItems/', getTableList = '/sys/user/queryUserComponentData', getCategoryData = '/sys/category/loadAllData', + refreshDragCache = '/drag/page/refreshCache', } /** @@ -148,3 +149,8 @@ export const getFileblob = (url, parameter) => { export const uploadMyFile = (url, data) => { return defHttp.uploadMyFile(url, data); }; +/** + * 刷新仪表盘缓存 + * @param params + */ +export const refreshDragCache = () => defHttp.get({ url: Api.refreshDragCache }, { isTransformResponse: false }); diff --git a/jeecgboot-vue3/src/assets/icons/robot.svg b/jeecgboot-vue3/src/assets/icons/robot.svg new file mode 100644 index 000000000..a1f035acb --- /dev/null +++ b/jeecgboot-vue3/src/assets/icons/robot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jeecgboot-vue3/src/components/Form/src/BasicForm.vue b/jeecgboot-vue3/src/components/Form/src/BasicForm.vue index ae4f5bfd2..6a70a56d5 100644 --- a/jeecgboot-vue3/src/components/Form/src/BasicForm.vue +++ b/jeecgboot-vue3/src/components/Form/src/BasicForm.vue @@ -367,6 +367,13 @@ margin-bottom: 24px; } // update-end--author:liaozhiyang---date:20240620---for:【TV360X-1420】校验时闪动 + + // 表单组件中间件样式 + .j-form-item-middleware { + flex: 1; + width: 100% + } + &.suffix-item { .ant-form-item-children { display: flex; @@ -376,6 +383,12 @@ margin-top: 4px; } + // 【QQYUN-12876】当紧凑型 suffix 时,表单组件中间件的宽度不占满 + &.suffix-compact .j-form-item-middleware { + flex: unset; + width: auto; + } + .suffix { display: inline-flex; padding-left: 6px; diff --git a/jeecgboot-vue3/src/components/Form/src/componentMap.ts b/jeecgboot-vue3/src/components/Form/src/componentMap.ts index dc098c48a..8f71df05d 100644 --- a/jeecgboot-vue3/src/components/Form/src/componentMap.ts +++ b/jeecgboot-vue3/src/components/Form/src/componentMap.ts @@ -65,6 +65,7 @@ import JTreeSelect from './jeecg/components/JTreeSelect.vue'; import JEllipsis from './jeecg/components/JEllipsis.vue'; import JSelectUserByDept from './jeecg/components/JSelectUserByDept.vue'; import JSelectUserByDepartment from './jeecg/components/JSelectUserByDepartment.vue'; +import JLinkTableCard from './jeecg/components/JLinkTableCard/JLinkTableCard.vue'; import JUpload from './jeecg/components/JUpload/JUpload.vue'; import JSearchSelect from './jeecg/components/JSearchSelect.vue'; import JAddInput from './jeecg/components/JAddInput.vue'; @@ -128,6 +129,7 @@ componentMap.set('JImageUpload', JImageUpload); componentMap.set('JDictSelectTag', JDictSelectTag); componentMap.set('JSelectDept', JSelectDept); componentMap.set('JAreaSelect', JAreaSelect); +componentMap.set('JLinkTableCard', JLinkTableCard); // componentMap.set( // 'JEditor', // createAsyncComponent(() => import('./jeecg/components/JEditor.vue')) diff --git a/jeecgboot-vue3/src/components/Form/src/components/FormItem.vue b/jeecgboot-vue3/src/components/Form/src/components/FormItem.vue index 08d5450d8..68ee3dc88 100644 --- a/jeecgboot-vue3/src/components/Form/src/components/FormItem.vue +++ b/jeecgboot-vue3/src/components/Form/src/components/FormItem.vue @@ -464,10 +464,17 @@ } function renderItem() { - const { itemProps, slot, render, field, suffix, component } = props.schema; + const { itemProps, slot, render, field, suffix, suffixCompact, component } = props.schema; const { labelCol, wrapperCol } = unref(itemLabelWidthProp); const { colon } = props.formProps; + // update-begin--author:sunjianlei---date:20250613---for:itemProps 属性支持函数形式 + let getItemProps = itemProps; + if (typeof getItemProps === 'function') { + getItemProps = getItemProps(unref(getValues)); + } + // update-end--author:sunjianlei---date:20250613---for:itemProps 属性支持函数形式 + if (component === 'Divider') { return ( @@ -486,8 +493,8 @@ -
+
diff --git a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JDictSelectTag.vue b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JDictSelectTag.vue index 8ca4988f4..c81062274 100644 --- a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JDictSelectTag.vue +++ b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JDictSelectTag.vue @@ -89,6 +89,8 @@ required: false, }, style: propTypes.any, + // 搜索时是否只搜索label + onlySearchByLabel: propTypes.bool.def(false), }, emits: ['options-change', 'change','update:value'], setup(props, { emit, refs }) { @@ -202,6 +204,10 @@ } } // update-end--author:liaozhiyang---date:20230914---for:【QQYUN-6514】 配置的时候,Y轴不能输入多个字段了,控制台报错 + if (props.onlySearchByLabel) { + // 如果开启了只在 label 中搜索,就不继续往下搜索value了 + return false; + } // 在 value 中搜索 return (option.value || '').toString().toLowerCase().indexOf(input.toLowerCase()) >= 0; } diff --git a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JImageUpload.vue b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JImageUpload.vue index e742b2f7c..a3c37df84 100644 --- a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JImageUpload.vue +++ b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JImageUpload.vue @@ -25,7 +25,7 @@
- + example @@ -38,7 +38,7 @@ import { useAttrs } from '/@/hooks/core/useAttrs'; import { useMessage } from '/@/hooks/web/useMessage'; import { getFileAccessHttpUrl, getHeaders, getRandom } from '/@/utils/common/compUtils'; - import { uploadUrl } from '/@/api/common/api'; + import { uploadUrl as systemUploadUrl } from '/@/api/common/api'; import { getToken } from '/@/utils/auth'; const { createMessage, createErrorModal } = useMessage(); @@ -79,6 +79,15 @@ required: false, default: 1, }, + uploadUrl: { + type: String, + default: systemUploadUrl, + }, + previewWidth: { + type: Number, + required: false, + default: 520, + }, }, emits: ['options-change', 'change', 'update:value'], setup(props, { emit, refs }) { @@ -256,7 +265,6 @@ multiple, headers, loading, - uploadUrl, beforeUpload, uploadVisible, handlePreview, diff --git a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/JLinkTableCard.vue b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/JLinkTableCard.vue new file mode 100644 index 000000000..15663d3d8 --- /dev/null +++ b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/JLinkTableCard.vue @@ -0,0 +1,379 @@ + + + + + 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 new file mode 100644 index 000000000..1ac73241e --- /dev/null +++ b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/components/LinkTableListModal.vue @@ -0,0 +1,320 @@ + + + + + diff --git a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/hooks/useLinkTable.ts b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/hooks/useLinkTable.ts new file mode 100644 index 000000000..91b3244fb --- /dev/null +++ b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JLinkTableCard/hooks/useLinkTable.ts @@ -0,0 +1,358 @@ +import { defHttp } from '/@/utils/http/axios'; +import { ref, watchEffect, computed, reactive } from 'vue'; +import { pick } from 'lodash-es'; +import { filterMultiDictText } from '/@/utils/dict/JDictSelectUtil'; +import { getFileAccessHttpUrl } from '/@/utils/common/compUtils'; + +function queryTableData(tableName, params) { + const url = '/online/cgform/api/getData/' + tableName; + return defHttp.get({ url, params }); +} + +function queryTableColumns(tableName, params) { + const url = '/online/cgform/api/getColumns/' + tableName; + return defHttp.get({ url, params }); +} + +export function useLinkTable(props) { + //TODO 目前只支持查询第一页的数据,可以输入关键字搜索 + const pageNo = ref('1'); + // 查询列 + const baseParam = ref({}); + // 搜素条件 + const searchParam = ref({}); + // 第一个文本列 + const mainContentField = ref(''); + //权限数据 + const auths = reactive({ + add: true, + update: true, + }); + + //显示列 + const textFieldArray = computed(() => { + if (props.textField) { + return props.textField.split(','); + } + return []; + }); + const otherColumns = ref([]); + // 展示的列 配置的很多列,但是只展示三行 + const realShowColumns = computed(() => { + const columns = otherColumns.value; + if (props.multi == true) { + return columns.slice(0, 3); + } else { + return columns.slice(0, 6); + } + }); + + watchEffect(async () => { + const table = props.tableName; + if (table) { + const valueField = props.valueField || ''; + const textField = props.textField || ''; + const arr: any[] = []; + if (valueField) { + arr.push(valueField); + } + if (textField) { + const temp = textField.split(','); + mainContentField.value = temp[0]; + for (const field of temp) { + arr.push(field); + } + } + const imageField = props.imageField || ''; + if (imageField) { + arr.push(imageField); + } + baseParam.value = { + linkTableSelectFields: arr.join(','), + }; + await resetTableColumns(); + await reloadTableLinkOptions(); + } + }); + + const otherFields = computed(() => { + const textField = props.textField || ''; + const others: any[] = []; + let labelField = ''; + if (textField) { + const temp = textField.split(','); + labelField = temp[0]; + for (let i = 0; i < temp.length; i++) { + if (i > 0) { + others.push(temp[i]); + } + } + } + return { + others, + labelField, + }; + }); + + // 选项 + const selectOptions = ref([]); + const tableColumns = ref([]); + const dictOptions = ref({}); + + async function resetTableColumns() { + const params = baseParam.value; + const data = await queryTableColumns(props.tableName, params); + tableColumns.value = data.columns; + if (data.columns) { + const imageField = props.imageField; + const arr = data.columns.filter((c) => c.dataIndex != mainContentField.value && c.dataIndex != imageField); + otherColumns.value = arr; + } + dictOptions.value = data.dictOptions; + // 权限数据 + console.log('隐藏的按钮', data.hideColumns); + if (data.hideColumns) { + const hideCols = data.hideColumns; + if (hideCols.indexOf('add') >= 0) { + auths.add = false; + } else { + auths.add = true; + } + if (hideCols.indexOf('update') >= 0) { + auths.update = false; + } else { + auths.update = true; + } + } + } + + async function reloadTableLinkOptions() { + const params = getLoadDataParams(); + const data = await queryTableData(props.tableName, params); + const records = data.records; + //tableTitle.value = data.head.tableTxt; + const dataList: any[] = []; + const { others, labelField } = otherFields.value; + const imageField = props.imageField; + if (records && records.length > 0) { + for (const rd of records) { + const temp = { ...rd }; + transData(temp); + const result = Object.assign({}, pick(temp, others), { id: temp.id, label: temp[labelField], value: temp[props.valueField] }); + if (imageField) { + result[imageField] = temp[imageField]; + } + dataList.push(result); + } + } + //添加一个空对象 为add操作占位 + // update-begin--author:liaozhiyang---date:20240607---for:【TV360X-1095】高级查询关联记录去掉编辑按钮及去掉记录按钮 + props.editBtnShow && dataList.push({}); + // update-end--author:liaozhiyang---date:20240607---for:【TV360X-1095】高级查询关联记录去掉编辑按钮及去掉记录按钮 + selectOptions.value = dataList; + } + + /** + * 数据简单翻译-字典 + * @param data + */ + function transData(data) { + const columns = tableColumns.value; + const dictInfo = dictOptions.value; + for (const c of columns) { + const { dataIndex, customRender } = c; + if (data[dataIndex] || data[dataIndex] === 0) { + if (customRender && customRender == dataIndex) { + //这样的就是 字典数据了 可以直接翻译 + if (dictInfo[customRender]) { + data[dataIndex] = filterMultiDictText(dictInfo[customRender], data[dataIndex]); + continue; + } + } + } + // 兼容后台翻译字段 + const dictText = data[dataIndex + '_dictText']; + if (dictText) { + data[dataIndex] = dictText; + } + } + } + + //获取加载数据的查询条件 + function getLoadDataParams() { + const params = Object.assign({ pageSize: 100, pageNo: pageNo.value }, baseParam.value, searchParam.value); + return params; + } + + //设置查询条件 + function addQueryParams(text) { + if (!text) { + searchParam.value = {}; + } else { + const arr = textFieldArray.value; + const params: any[] = []; + const fields: any[] = []; + for (let i = 0; i < arr.length; i++) { + if (i <= 1) { + fields.push(arr[i]); + params.push({ field: arr[i], rule: 'like', val: text }); + } + } + // params[arr[i]] = `*${text}*` + // params['selectConditionFields'] = fields.join(',') + // searchParam.value = params; + params['superQueryMatchType'] = 'or'; + params['superQueryParams'] = encodeURI(JSON.stringify(params)); + searchParam.value = params; + } + } + + async function loadOne(value) { + if (!value) { + return []; + } + let valueFieldName = props.valueField; + let params = { + ...baseParam.value, + pageSize: 100, + pageNo: pageNo.value, + }; + params['superQueryMatchType'] = 'and'; + let valueCondition = [{ field: valueFieldName, rule: 'in', val: value }]; + params['superQueryParams'] = encodeURI(JSON.stringify(valueCondition)); + const data = await queryTableData(props.tableName, params); + let records = data.records; + //tableTitle.value = data.head.tableTxt; + let dataList: any[] = []; + if (records && records.length > 0) { + for (let item of records) { + let temp = { ...item }; + transData(temp); + dataList.push(temp); + } + } + return dataList; + } + + /** + * true:数据一致;false:数据不一致 + * @param arr + * @param value + */ + function compareData(arr, value) { + if (!arr || arr.length == 0) { + return false; + } + const valueArray = value.split(','); + if (valueArray.length != arr.length) { + return false; + } + let flag = true; + for (const item of arr) { + const temp = item[props.valueField]; + if (valueArray.indexOf(temp) < 0) { + flag = false; + } + } + return flag; + } + + function formatData(formData) { + Object.keys(formData).map((k) => { + if (formData[k] instanceof Array) { + formData[k] = formData[k].join(','); + } + }); + } + + function initFormData(formData, linkFieldArray, record) { + if (!record) { + record = {}; + } + if (linkFieldArray && linkFieldArray.length > 0) { + for (const str of linkFieldArray) { + const arr = str.split(','); + //["表单字段,表字典字段"] + const field = arr[0]; + const dictField = arr[1]; + if (!formData[field]) { + const value = record[dictField] || ''; + formData[field] = [value]; + } else { + formData[field].push(record[dictField]); + } + } + } + } + + // 获取图片地址 + function getImageSrc(item) { + if (props.imageField) { + let url = item[props.imageField]; + // update-begin--author:liaozhiyang---date:20250517---for:【TV360X-38】关联记录空间,被关联数据优多个图片时,封面图片不展示 + if (typeof url === 'string') { + // 有多张图时默认取第一张 + url = url.split(',')[0]; + } + // update-end--author:liaozhiyang---date:20250517---for:【TV360X-38】关联记录空间,被关联数据优多个图片时,封面图片不展示 + return getFileAccessHttpUrl(url); + } + return ''; + } + const showImage = computed(() => { + if (props.imageField) { + return true; + } else { + return false; + } + }); + + return { + pageNo, + otherColumns, + realShowColumns, + selectOptions, + reloadTableLinkOptions, + textFieldArray, + addQueryParams, + tableColumns, + transData, + mainContentField, + loadOne, + compareData, + formatData, + initFormData, + getImageSrc, + showImage, + auths, + }; +} + +/** + * 使用固定高度的modal + */ +export function useFixedHeightModal() { + const minWidth = 800; + const popModalFixedWidth = ref(800); + let tempWidth = window.innerWidth - 300; + if (tempWidth < minWidth) { + tempWidth = minWidth; + } + popModalFixedWidth.value = tempWidth; + + // 弹窗高度控制 + const popBodyStyle = ref({}); + function resetBodyStyle() { + const height = window.innerHeight - 210; + popBodyStyle.value = { + height: height + 'px', + overflowY: 'auto', + }; + } + + return { + popModalFixedWidth, + popBodyStyle, + resetBodyStyle, + }; +} diff --git a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JSearchSelect.vue b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JSearchSelect.vue index 4b3f502de..6c0776d43 100644 --- a/jeecgboot-vue3/src/components/Form/src/jeecg/components/JSearchSelect.vue +++ b/jeecgboot-vue3/src/components/Form/src/jeecg/components/JSearchSelect.vue @@ -15,6 +15,9 @@ @search="loadData" @change="handleAsyncChange" @popupScroll="handlePopupScroll" + :mode="multiple?'multiple':''" + @select="handleSelect" + @deselect="handleDeSelect" >