v3.7.2 版本代码合并

This commit is contained in:
JEECG
2024-12-09 15:10:46 +08:00
parent 64b29f47e0
commit b0c4194602
118 changed files with 12729 additions and 1596 deletions

View File

@ -19,3 +19,9 @@ VITE_GLOB_API_URL_PREFIX=
#微前端qiankun应用,命名必须以VITE_APP_SUB_开头,jeecg-app-1为子应用的项目名称,也是子应用的路由父路径
VITE_APP_SUB_jeecg-app-1 = '//localhost:8092'
# 填写后将作为乾坤子应用启动主应用注册时AppName需保持一致放开 VITE_GLOB_QIANKUN_MICRO_APP_NAME 参数表示jeecg-vue3将以乾坤子应用模式启动
#VITE_GLOB_QIANKUN_MICRO_APP_NAME=jeecg-vue3
# 作为乾坤子应用启动时必填需与qiankun主应用注册子应用时填写的 entry 保持一致
#VITE_GLOB_QIANKUN_MICRO_APP_ENTRY=//localhost:3001/jeecg-vue3

View File

@ -20,3 +20,9 @@ VITE_GLOB_DOMAIN_URL=http://jeecg-boot-system:8080/jeecg-boot
# 接口父路径前缀
VITE_GLOB_API_URL_PREFIX=
# 填写后将作为乾坤子应用启动主应用注册时AppName需保持一致放开 VITE_GLOB_QIANKUN_MICRO_APP_NAME 参数表示jeecg-vue3将以乾坤子应用模式启动
#VITE_GLOB_QIANKUN_MICRO_APP_NAME=jeecg-vue3
# 作为乾坤子应用启动时必填需与qiankun主应用注册子应用时填写的 entry 保持一致
#VITE_GLOB_QIANKUN_MICRO_APP_ENTRY=//qiankun.boot3.jeecg.com/jeecg-vue3

View File

@ -7,7 +7,7 @@ import { createHtmlPlugin } from 'vite-plugin-html';
import pkg from '../../../package.json';
import { GLOB_CONFIG_FILE_NAME } from '../../constant';
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean, isQiankunMicro: boolean) {
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env;
const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`;
@ -16,12 +16,17 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
};
// 【JEECG作为乾坤子应用】补充静态资源前缀
const {VITE_GLOB_QIANKUN_MICRO_APP_ENTRY} = env;
const basePublicPath = isQiankunMicro ? VITE_GLOB_QIANKUN_MICRO_APP_ENTRY : '';
const htmlPlugin: PluginOption[] = createHtmlPlugin({
minify: isBuild,
inject: {
// 修改模板html的标题
data: {
title: VITE_GLOB_APP_TITLE,
basePublicPath: basePublicPath,
},
// 将app.config.js文件注入到模板html中
tags: isBuild

View File

@ -15,11 +15,18 @@ import { configCompressPlugin } from './compress';
import { configVisualizerConfig } from './visualizer';
import { configThemePlugin } from './theme';
import { configSvgIconsPlugin } from './svgSprite';
import { configQiankunMicroPlugin } from './qiankunMicro';
// //预编译加载插件(不支持vite3作废)
// import OptimizationPersist from 'vite-plugin-optimize-persist';
// import PkgConfig from 'vite-plugin-package-config';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
/**
*
* @param viteEnv
* @param isBuild
* @param isQiankunMicro 是否【JEECG作为乾坤子应用】
*/
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, isQiankunMicro: boolean) {
const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
const vitePlugins: (PluginOption | PluginOption[])[] = [
@ -38,7 +45,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
vitePlugins.push(UnoCSS({ presets: [presetUno(), presetTypography()] }));
// vite-plugin-html
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild, isQiankunMicro));
// vite-plugin-svg-icons
vitePlugins.push(configSvgIconsPlugin(isBuild));
@ -55,6 +62,12 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// vite-plugin-theme
vitePlugins.push(configThemePlugin(isBuild));
// 【JEECG作为乾坤子应用】注册乾坤子应用模式插件
if (isQiankunMicro) {
// vite-plugin-qiankun
vitePlugins.push(...configQiankunMicroPlugin(viteEnv))
}
// The following plugins only work in the production environment
if (isBuild) {

View File

@ -0,0 +1,16 @@
import qiankun from 'vite-plugin-qiankun';
/**
* 【JEECG作为乾坤子应用】Vite适配乾坤以子应用模式运行
* @param env
*/
export function configQiankunMicroPlugin(env: ViteEnv) {
const {VITE_GLOB_QIANKUN_MICRO_APP_NAME} = env
return [
qiankun(VITE_GLOB_QIANKUN_MICRO_APP_NAME!, {
useDevMode: true,
})
]
}

View File

@ -10,7 +10,7 @@
/>
<title><%= title %></title>
<link rel="icon" href="/logo.png" />
<link rel="icon" href="<%= basePublicPath %>/logo.png" />
<!-- 全局配置 -->
<script>
window._CONFIG = {};
@ -157,7 +157,7 @@
</style>
<div class="app-loading">
<div class="app-loading-wrap">
<img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo" />
<img src="<%= basePublicPath %>/resource/img/logo.png" class="app-loading-logo" alt="Logo" />
<div class="app-loading-dots">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div>

View File

@ -153,11 +153,14 @@
"vite-plugin-package-config": "^0.1.1",
"vite-plugin-purge-icons": "^0.10.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-qiankun": "^1.0.15",
"@rys-fe/vite-plugin-theme": "^0.8.6",
"vite-plugin-vue-setup-extend-plus": "^0.1.0",
"unocss": "^0.58.5",
"vue-eslint-parser": "^9.4.2",
"vue-tsc": "^1.8.27"
"vue-tsc": "^1.8.27",
"dingtalk-jsapi": "^3.0.25",
"big.js": "^6.2.2"
},
"repository": {
"type": "git",

File diff suppressed because it is too large Load Diff

View File

@ -84,11 +84,14 @@ export default function ({ advanceState, emit, getProps, getSchema, formModel, d
advanceState.isAdvanced = true;
} else */
// update-end--author:sunjianlei---date:20211108---for: 注释掉该逻辑使小于等于2行时也显示展开收起按钮
if (itemColSum > BASIC_COL_LEN * 2 && itemColSum <= BASIC_COL_LEN * (unref(getProps).autoAdvancedLine || 3)) {
// update-begin--author:liaozhiyang---date:202401009---for【issues/7261】表格上方查询项autoAdvancedLine配置没有效果删除autoAdvancedLine
/*if (itemColSum > BASIC_COL_LEN * 2 && itemColSum <= BASIC_COL_LEN * (unref(getProps).autoAdvancedLine || 3)) {
advanceState.hideAdvanceBtn = false;
// 默认超过 3 行折叠
} else if (!advanceState.isLoad) {
} else*/
// update-end--author:liaozhiyang---date:202401009---for【issues/7261】表格上方查询项autoAdvancedLine配置没有效果删除autoAdvancedLine
if (!advanceState.isLoad) {
advanceState.isLoad = true;
advanceState.isAdvanced = !advanceState.isAdvanced;
// update-begin--author:sunjianlei---date:20211108---for: 如果总列数大于 autoAdvancedCol就默认折叠

View File

@ -79,13 +79,8 @@
function transform(arr) {
let result: any = [];
if (props.saveCode === 'region') {
// 81 香港、82 澳门
const regionCode = arr[0];
if (['82', '81'].includes(regionCode.substring(0, 2))) {
result = [`${regionCode.substring(0, 2)}0000`, regionCode];
} else {
result = [`${regionCode.substring(0, 2)}0000`, `${regionCode.substring(0, 2)}${regionCode.substring(2, 4)}00`, regionCode];
}
result = [`${regionCode.substring(0, 2)}0000`, `${regionCode.substring(0, 2)}${regionCode.substring(2, 4)}00`, regionCode];
} else if (props.saveCode === 'city') {
const cityCode = arr[0];
result = [`${cityCode.substring(0, 2)}0000`, cityCode];

View File

@ -26,72 +26,74 @@
</a-tabs>
<a-divider />
<!-- 执行时间预览 -->
<a-row :gutter="8">
<a-col :span="18" style="margin-top: 22px">
<a-row :gutter="8">
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.second" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'second'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.minute" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'minute'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.hour" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'hour'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.day" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'day'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.month" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'month'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.week" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'week'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8">
<a-input v-model:value="inputValues.year" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'year'"></span>
</template>
</a-input>
</a-col>
<a-col :span="16">
<a-input v-model:value="inputValues.cron" @blur="onInputCronBlur">
<template #addonBefore>
<a-tooltip title="Cron表达式"></a-tooltip>
</template>
</a-input>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<div>近十次执行时间不含年</div>
<a-textarea type="textarea" :value="preTimeList" :rows="5" />
</a-col>
</a-row>
<div style="overflow: hidden">
<a-row :gutter="8">
<a-col :span="18" style="margin-top: 22px">
<a-row :gutter="8">
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.second" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'second'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.minute" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'minute'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.hour" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'hour'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.day" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'day'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.month" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'month'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.week" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'week'"></span>
</template>
</a-input>
</a-col>
<a-col :span="8">
<a-input v-model:value="inputValues.year" @blur="onInputBlur">
<template #addonBefore>
<span class="allow-click" @click="activeKey = 'year'"></span>
</template>
</a-input>
</a-col>
<a-col :span="16">
<a-input v-model:value="inputValues.cron" @blur="onInputCronBlur">
<template #addonBefore>
<a-tooltip title="Cron表达式"></a-tooltip>
</template>
</a-input>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<div>近十次执行时间不含年</div>
<a-textarea type="textarea" :value="preTimeList" :rows="5" />
</a-col>
</a-row>
</div>
</div>
</div>
</template>

View File

@ -1,5 +1,5 @@
<template>
<BasicModal @register="registerModal" title="Cron表达式" width="800px" @ok="onOk">
<BasicModal @register="registerModal" title="Cron表达式" width="1000px" @ok="onOk">
<EasyCron v-bind="attrs" />
</BasicModal>
</template>

View File

@ -395,7 +395,14 @@
// update-end--author:liaozhiyang---date:20240809---for【TV360X-2062】下拉搜索选择第二页数据后第一次点击时(得到焦点)滚动条没复原到初始位置且数据会加载第二页数据(应该只加载第一页数据)
initDictTableData();
}
// update-end--author:liaozhiyang---date:20240709---forissues/6681】异步查询不生效
// update-begin--author:liaozhiyang---date:20240919---forTV360X-2348】得到焦点时options选项显示第一页内容解决新增时显示非第一页内容
if (Array.isArray(selectedAsyncValue.value) && selectedAsyncValue.value.length === 0 && isDictTable.value && props.async) {
if (pageNo > 2) {
options.value = [];
initDictTableData();
}
}
// update-end--author:liaozhiyang---date:20240919---for【TV360X-2348】得到焦点时options选项显示第一页内容解决新增时显示非第一页内容
attrs.onFocus?.();
};
// update-end--author:liaozhiyang---date:20240523---for【TV360X-26】下拉搜索控件选中选项后再次点击下拉应该显示初始的下拉选项而不是只展示选中结果

View File

@ -67,8 +67,10 @@ export const basicProps = {
rulesMessageJoinLabel: propTypes.bool.def(true),
// 【jeecg】超过3列自动折叠
autoAdvancedCol: propTypes.number.def(3),
// update-begin--author:liaozhiyang---date:202401009---for【issues/7261】表格上方查询项autoAdvancedLine配置没有效果删除autoAdvancedLine
// 超过3行自动折叠
autoAdvancedLine: propTypes.number.def(3),
// autoAdvancedLine: propTypes.number.def(3),
// update-end--author:liaozhiyang---date:202401009---for【issues/7261】表格上方查询项autoAdvancedLine配置没有效果删除autoAdvancedLine
// 不受折叠影响的行数
alwaysShowLines: propTypes.number.def(1),

View File

@ -99,7 +99,9 @@ export interface FormProps {
// 【jeecg】如果 showAdvancedButton 为 true超过指定列数默认折叠默认为3
autoAdvancedCol?: number;
// 如果 showAdvancedButton 为 true超过指定行数行默认折叠
autoAdvancedLine?: number;
// update-begin--author:liaozhiyang---date:202401009---for【issues/7261】表格上方查询项autoAdvancedLine配置没有效果删除autoAdvancedLine
// autoAdvancedLine?: number;
// update-end--author:liaozhiyang---date:202401009---for【issues/7261】表格上方查询项autoAdvancedLine配置没有效果删除autoAdvancedLine
// 折叠时始终保持显示的行数
alwaysShowLines?: number;
// Whether to show the operation button

View File

@ -101,11 +101,6 @@ const jeecgAreaData = new Area();
// 根据code找文本
const getAreaTextByCode = function (code) {
let index = 3;
// update-begin--author:liaozhiyang---date:20240617---for【TV360X-1210】online列表香港、澳门没翻译香港、澳门只有两级其它省份是三级
if (code && ['82', '81'].includes(code.substring(0, 2))) {
index = 2;
}
// update-end--author:liaozhiyang---date:20240617---for【TV360X-1210】online列表香港、澳门没翻译香港、澳门只有两级其它省份是三级
//update-begin-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串是的话获取最后一位的code ---
if (code && code.includes(',')) {
index = code.split(",").length;

View File

@ -36,8 +36,10 @@ export function handleRangeTimeValue(props, values) {
timeValue = timeValue.split(',');
}
const [startTime, endTime]: string[] = timeValue;
values[startTimeKey] = dateUtil(startTime).format(format);
values[endTimeKey] = dateUtil(endTime).format(format);
//update-begin---author:wangshuai---date:2024-10-08---for:【issues/7216】当RangePicker组件值允许开始/结束为空时,表单的fieldMapToTime处理异常---
startTime && (values[startTimeKey] = dateUtil(startTime).format(format));
endTime && (values[endTimeKey] = dateUtil(endTime).format(format));
//update-end---author:wangshuai---date:2024-10-08---for:【issues/7216】当RangePicker组件值允许开始/结束为空时,表单的fieldMapToTime处理异常---
Reflect.deleteProperty(values, field);
}
return values;

View File

@ -1,10 +1,23 @@
<template>
<a-input :disabled="disabled" :style="{ width }" readOnly :placeholder="t('component.icon.placeholder')" :class="prefixCls" v-model:value="currentSelect">
<a-input
:class="prefixCls"
:value="currentSelect"
readOnly
:disabled="disabled"
:placeholder="t('component.icon.placeholder')"
:style="{ width }"
@click="currentSelectClick"
>
<template #addonAfter>
<span class="cursor-pointer px-2 py-1 flex items-center" v-if="isSvgMode && currentSelect">
<SvgIcon :name="currentSelect" @click="currentSelectClick"/>
</span>
<Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" v-else @click="currentSelectClick"/>
<Icon v-else :icon="currentSelect || 'ion:apps-outline'" :class="['px-2 py-1', {
'cursor-pointer': !disabled,
'cursor-not-allowed': disabled,
}]"
@click="currentSelectClick"
/>
</template>
</a-input>
<a-modal :bodyStyle="{ padding: '24px'}" v-bind="$attrs" v-model:open="iconOpen" :keyboard="false" :width="800" @ok="handleOk" :ok-text="t('common.okText')" :cancel-text="t('common.cancelText')">
@ -84,7 +97,7 @@
width: propTypes.string.def('100%'),
copy: propTypes.bool.def(false),
mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
disabled: propTypes.bool.def(true),
disabled: propTypes.bool.def(false),
clearSelect: propTypes.bool.def(false),
iconPrefixSave: propTypes.bool.def(true),
});
@ -149,6 +162,9 @@
* 图标点击重置页数
*/
function currentSelectClick() {
if (props.disabled) {
return
}
iconOpen.value = true;
setTimeout(()=>{
iconListRef.value.currentSelectClick();
@ -185,6 +201,12 @@
@prefix-cls: ~'@{namespace}-icon-picker';
.@{prefix-cls} {
// 输入框手势图标
.ant-input:not([disabled]) {
cursor: pointer;
}
.ant-input-group-addon {
padding: 0;
}

View File

@ -0,0 +1,76 @@
<template>
<div>
<keep-alive>
<component
v-if="currentModal"
v-bind="bindParams"
:key="currentModal"
:is="currentModal"
@register="modalRegCache[currentModal].register"
@reply="handReply"
@selected="reloadPage"
/>
</keep-alive>
<!-- 历史弹出框 -->
<HisTaskDealModal ref="taskDealRef" v-bind="bindParams" />
<!-- 系统公告弹窗 -->
<DynamicNotice ref="showDynamNotice" v-bind="bindParams" />
<!-- eoa查看详情 -->
<EoaDetailModal ref="detailRef" />
<!-- 表单设计器弹窗 -->
<DesformDataModal ref="desformRef" v-bind="bindParams" @added="handleDesformDataAdded" />
<!-- 我的计划弹窗 -->
<PlanModal ref="planRef" v-bind="bindParams" @success="reloadPage" />
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted } from 'vue';
import { useDragNotice } from '/@/hooks/web/useDragNotice';
import EoaMailBoxInModal from '/@/views/super/eoa/email/components/EoaMailBoxInModal.vue';
import DynamicNotice from '@/views/monitor/mynews/DynamicNotice.vue';
import EoaDetailModal from '@/views/super/eoa/cmsoa/modules/EoaDetailModal.vue';
import PlanModal from '/@/views/super/eoa/plan/components/PlanModal.vue';
export default defineComponent({
name: 'JDragNotice',
components: {
EoaDetailModal,
DynamicNotice,
EoaMailBoxInModal,
PlanModal,
},
setup() {
const {
initDragWebSocket,
currentModal,
modalParams,
modalRegCache,
bindParams,
taskDealRef,
desformRef,
handleDesformDataAdded,
handReply,
reloadPage,
} = useDragNotice();
onMounted(() => {
initDragWebSocket();
});
return {
currentModal,
modalParams,
modalRegCache,
bindParams,
taskDealRef,
desformRef,
handleDesformDataAdded,
handReply,
reloadPage,
};
},
});
</script>
<style scoped lang="less"></style>

View File

@ -112,7 +112,10 @@
//整合warpClassName
const getWrapClassName = computed(() => {
const clsName = toRef(getMergeProps.value, 'wrapClassName').value || '';
let clsName = toRef(getMergeProps.value, 'wrapClassName').value || '';
// update-begin--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
clsName = `${clsName} jeecg-modal-code-generate`;
// update-end--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
return unref(fullScreenRef) ? `jeecg-full-screen-modal-code-generate ${clsName} ` : unref(clsName);
});
@ -324,4 +327,13 @@
}
}
/*end 全屏弹窗modal样式*/
// update-begin--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
.jeecg-modal-code-generate {
.ant-modal {
.ant-modal-close {
top: 8px;
}
}
}
// update-end--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
</style>

View File

@ -120,7 +120,9 @@
display: flex;
height: 95%;
align-items: center;
// update-begin--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
margin-top: -2px;
// update-end--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
> span {
margin-left: 48px;
font-size: 16px;

View File

@ -76,10 +76,12 @@
}
.ant-modal-close-x {
display: inline-block;
width: 96px;
height: 56px;
line-height: 56px;
// update-begin--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
// display: inline-block;
// width: 96px;
// height: 56px;
// line-height: 56px;
// update-end--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
}
.ant-modal-confirm-body {

View File

@ -48,6 +48,11 @@
const renderValue = String(value);
const wrapEl = unref(wrapRef);
// 无值不生成
if (renderValue == null || renderValue === '') {
return;
}
if (!wrapEl) return;
if (tag === 'canvas') {

View File

@ -161,11 +161,8 @@ export function useColumns(
const viewColumns = sortFixedColumn(unref(getColumnsRef));
const columns = cloneDeep(viewColumns);
const result = columns
.filter((column) => {
return hasPermission(column.auth) && isIfShow(column);
})
.map((column) => {
const formatEditColumn = (columns) => {
return columns.map((column) => {
// update-begin--author:liaozhiyang---date:20230718---for: 【issues-179】antd3 一些警告以及报错(针对表格)
if(column.slots?.customRender) {
// slots的备份兼容老的写法转成新写法避免控制台警告
@ -198,8 +195,17 @@ export function useColumns(
if ((edit || editRow) && !isDefaultAction) {
column.customRender = renderEditCell(column);
}
// update-begin--author:liaozhiyang---date:20241021---for【pull/7333】修复分组表头可编辑表格失效问题
if (column.children?.length) {
formatEditColumn(column.children.filter((item) => hasPermission(column.auth) && isIfShow(column)));
}
// update-end--author:liaozhiyang---date:20241021---for【pull/7333】修复分组表头可编辑表格失效问题
return reactive(column);
});
};
// update-begin--author:liaozhiyang---date:20241021---for【pull/7333】修复分组表头可编辑表格失效问题
const result = formatEditColumn(columns.filter((item) => hasPermission(item.auth) && isIfShow(item)));
// update-end--author:liaozhiyang---date:20241021---for【pull/7333】修复分组表头可编辑表格失效问题
// update-begin--author:liaozhiyang---date:20230919---for【QQYUN-6387】展开写法去掉报错
if (propsRef.value.expandedRowKeys && !propsRef.value.isTreeTable) {
let index = 0;

View File

@ -198,17 +198,9 @@ export function useCustomSelection(
bodyHeight.value = Math.ceil(height);
}
}
updateRowHeight();
});
bodyResizeObserver.observe(bodyEl.value);
const el = bodyEl.value?.querySelector('tbody.ant-table-tbody tr.ant-table-row') as HTMLDivElement;
if (el) {
// update-begin--author:liaozhiyang---date:20241111---for【issues/7442】basicTable从默认切换到宽松紧凑时多选框显示异常
nextTick(() => {
rowHeight.value = el.offsetHeight;
});
// update-end--author:liaozhiyang---date:20241111---for【issues/7442】basicTable从默认切换到宽松紧凑时多选框显示异常
return;
}
}
rowHeight.value = 50;
});
@ -226,6 +218,16 @@ export function useCustomSelection(
}
});
// 更新首行行高
function updateRowHeight() {
const el = bodyEl.value?.querySelector('tbody.ant-table-tbody tr.ant-table-row') as HTMLDivElement;
if (el) {
// update-begin--author:liaozhiyang---date:20241111---for【issues/7442】basicTable从默认切换到宽松紧凑时多选框显示异常
nextTick(() => rowHeight.value = el.offsetHeight);
// update-end--author:liaozhiyang---date:20241111---for【issues/7442】basicTable从默认切换到宽松紧凑时多选框显示异常
}
}
// 选择全部
function onSelectAll(checked: boolean, flag = 'currentPage') {
// update-begin--author:liaozhiyang---date:20231122---for【issues/5577】BasicTable组件全选和取消全选时不触发onSelectAll事件

View File

@ -1,5 +1,5 @@
import type { BasicTableProps, TableRowSelection, BasicColumn } from '../types/table';
import type { Ref, ComputedRef } from 'vue';
import type { Ref, ComputedRef, Slots } from 'vue';
import { computed, unref, ref, nextTick, watch } from 'vue';
import { getViewportOffset } from '/@/utils/domUtils';
import { isBoolean } from '/@/utils/is';
@ -14,7 +14,8 @@ export function useTableScroll(
tableElRef: Ref<ComponentRef>,
columnsRef: ComputedRef<BasicColumn[]>,
rowSelectionRef: ComputedRef<TableRowSelection<any> | null>,
getDataSourceRef: ComputedRef<Recordable[]>
getDataSourceRef: ComputedRef<Recordable[]>,
slots: Slots
) {
const tableHeightRef: Ref<Nullable<number>> = ref(null);
@ -175,7 +176,11 @@ export function useTableScroll(
if (len !== 0) {
width += len * NORMAL_WIDTH;
}
// update-begin--author:liaozhiyang---date:202401009---for【TV360X-116】内嵌风格字段较多时表格错位
if (slots.expandedRowRender) {
width += propsRef.value.expandColumnWidth;
}
// update-end--author:liaozhiyang---date:202401009---for【TV360X-116】内嵌风格字段较多时表格错位
const table = unref(tableElRef);
const tableWidth = table?.$el?.offsetWidth ?? 0;
return tableWidth > width ? '100%' : width;

View File

@ -108,6 +108,9 @@ export const basicProps = {
},
minHeight: propTypes.number,
maxHeight: propTypes.number,
// update-begin--author:liaozhiyang---date:202401009---for【TV360X-116】内嵌风格字段较多时表格错位
expandColumnWidth: propTypes.number.def(48),
// update-end--author:liaozhiyang---date:202401009---for【TV360X-116】内嵌风格字段较多时表格错位
// 统一设置列最大宽度
maxColumnWidth: propTypes.number,
dataSource: {

View File

@ -199,7 +199,7 @@ export interface BasicTableProps<T = any> {
// 是否显示操作列
showActionColumn?: boolean;
// 操作列配置
actionColumn?: BasicColumn;
actionColumn?: Partial<BasicColumn>;
// 文本超过宽度是否显示。。。
ellipsis?: boolean;
// 是否可以自适应高度
@ -220,6 +220,10 @@ export interface BasicTableProps<T = any> {
maxHeight?: number;
// 是否显示边框
bordered?: boolean;
// update-begin--author:liaozhiyang---date:202401009---for【TV360X-116】内嵌风格字段较多时表格错位
// 展开列宽度
expandColumnWidth: number;
// update-end--author:liaozhiyang---date:202401009---for【TV360X-116】内嵌风格字段较多时表格错位
// 分页配置
pagination?: PaginationProps | boolean;
// loading加载

View File

@ -29,5 +29,5 @@ export interface PopConfirm {
icon?: string;
placement?: string;
overlayClassName?: string;
getPopupContainer: Fn;
getPopupContainer?: Fn;
}

View File

@ -5,6 +5,7 @@
<ImgUpload
:fullscreen="fullscreen"
@uploading="handleImageUploading"
@loading="handleLoading"
@done="handleDone"
v-show="editorRef"
:disabled="disabled"
@ -13,6 +14,7 @@
<!-- update-end--author:liaozhiyang---date:20240517---forTV360X-35富文本图片上传遮挡其他按钮 -->
<Editor :id="tinymceId" ref="elRef" :disabled="disabled" :init="initOptions" :style="{ visibility: 'hidden' }" v-if="!initOptions.inline"></Editor>
<slot v-else></slot>
<ProcessMask ref="processMaskRef" :show="showUploadMask"/>
</div>
</template>
@ -33,6 +35,7 @@
import 'tinymce/plugins/image';
import { defineComponent, computed, nextTick, ref, unref, watch, onDeactivated, onBeforeUnmount, onMounted } from 'vue';
import ImgUpload from './ImgUpload.vue';
import ProcessMask from './ProcessMask.vue';
import {simpleToolbar, menubar, simplePlugins} from './tinymce';
import { buildShortUUID } from '/@/utils/uuid';
import { bindHandlers } from './helper';
@ -82,6 +85,10 @@
type: Boolean,
default: true,
},
showUploadMask: {
type: Boolean,
default: false,
},
//是否聚焦
autoFocus:{
type: Boolean,
@ -91,9 +98,9 @@
export default defineComponent({
name: 'Tinymce',
components: { ImgUpload,Editor },
components: { ImgUpload,Editor,ProcessMask },
inheritAttrs: false,
props: tinymceProps,
props: tinymceProps as any,
emits: ['change', 'update:modelValue', 'inited', 'init-error'],
setup(props, { emit, attrs }) {
console.log("---Tinymce---初始化---")
@ -103,6 +110,7 @@
const tinymceId = ref<string>(buildShortUUID('tiny-vue'));
const elRef = ref<Nullable<HTMLElement>>(null);
const editorRootRef = ref<Nullable<HTMLElement>>(null);
const processMaskRef = ref<any>(null);
const imgUploadShow = ref(false);
const targetElem = ref<null | HTMLDivElement>(null);
@ -325,6 +333,20 @@
setValue(editor, val);
}
/**
* 上传进度计算
* @param file
* @param fileList
*/
function handleLoading(fileLength,showMask){
if(fileLength && fileLength > 0){
setTimeout(() => {
props?.showUploadMask && processMaskRef.value.calcProcess(fileLength)
},100)
}else{
props?.showUploadMask && (processMaskRef.value.showMask = showMask);
}
}
function getUploadingImgName(name: string) {
return `[uploading:${name}]`;
}
@ -397,6 +419,9 @@
editorRootRef,
imgUploadShow,
targetElem,
handleLoading,
processMaskRef
};
},
});
@ -428,6 +453,7 @@
}
// update-end--author:liaozhiyang---date:20240527---for【TV360X-329】富文本禁用状态下工具栏划过边框丢失
}
html[data-theme='dark'] {
.@{prefix-cls} {
.tox .tox-edit-area__iframe {background-color: #141414;}

View File

@ -8,6 +8,7 @@
:showUploadList="false"
:data="getBizData()"
:headers="getheader()"
:before-upload="beforeUpload"
accept=".jpg,.jpeg,.gif,.png,.webp"
>
<a-button type="primary" v-bind="{ ...getButtonProps }">
@ -37,10 +38,8 @@
default: false,
},
},
emits: ['uploading', 'done', 'error'],
emits: ['uploading', 'done', 'error', 'loading'],
setup(props, { emit }) {
let uploading = false;
//update-begin-author:taoyan date:2022-5-13 for: 富文本上传图片不支持
function getheader() {
return getHeaders();
@ -67,33 +66,37 @@
};
});
let uploadLength = 0;
function handleChange({ file, fileList }) {
if (file.status === 'error') {
emit('error');
uploading = false;
// 过滤掉已经存在的文件
fileList = fileList.filter((file) => {
const existFile = uploadFileList.value.find(({ uid }) => uid === file.uid);
return existFile ? false : true;
});
uploadLength == 0 && (uploadLength = fileList.length);
if (file.status != 'uploading') {
emit('loading', uploadLength, true);
}
let files = [] as any;
let noUploadingFileCount = 0;
// 处理上传好的文件
if (file.status != 'uploading') {
fileList.forEach((file) => {
if (file.status === 'done' && file.response.success) {
files.push(file);
const name = file?.name;
let realUrl = getFileAccessHttpUrl(file.response.message);
uploadFileList.value.push(file);
emit('done', name, realUrl);
}
if (file.status != 'uploading') {
noUploadingFileCount++;
}
});
}
if (noUploadingFileCount == fileList.length) {
fileList.forEach((file) => {
const name = file?.name;
let realUrl = getFileAccessHttpUrl(file.response.message);
emit('done', name, realUrl);
});
}
}
//上传之前
function beforeUpload() {
uploadLength = 0;
emit('loading', null, true);
setTimeout(() => {
emit('loading', null, false);
}, 10000);
}
return {
prefixCls,
handleChange,
@ -102,7 +105,8 @@
getBizData,
t,
getButtonProps,
uploadFileList
uploadFileList,
beforeUpload,
};
},
});

View File

@ -0,0 +1,110 @@
<template>
<div :class="[prefixCls]" v-if="showMask && show">
<div class="progress-bar-rear">
<div class="progress-bar-front" :style="{ width: progressBarWidth }"></div>
</div>
<div class="value">{{ percentage }}</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import {useDesign} from "@/hooks/web/useDesign";
const props = defineProps({
backColor: {
type: [String],
default: 'white',
},
processColor: {
type: String,
default: '#018FFB',
},
show: {
type: Boolean,
default: false,
},
});
const { prefixCls } = useDesign('tinymce-process-mask');
//显示遮罩
const showMask = ref(false);
//进度值占比
const progressValue = ref<any>(0);
//当前数量
const currentNum = ref(0);
// 计算进度条宽度的计算属性
const progressBarWidth = computed(() => {
return progressValue.value > 0 ? `${progressValue.value}px` : '0px';
});
// 计算进度条百分比
const percentage = computed(() => {
return `${progressValue.value}%`;
});
// 进度色
const frontColor = computed(() => {
return props.processColor;
});
// 后置背景色
const rearColor = computed(() => {
return props.backColor;
});
function calcProcess(totalNum) {
!showMask.value && (showMask.value = true);
currentNum.value += 1;
progressValue.value = ((currentNum.value / totalNum) * 100).toFixed(2);
console.log('currentNum.value', currentNum.value);
console.log('totalNum.value', totalNum);
if (currentNum.value == totalNum) {
showMask.value = false;
currentNum.value = 0;
progressValue.value = 0;
}
}
defineExpose({
calcProcess,
showMask,
});
</script>
<style lang="less">
//noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-tinymce-process-mask';
.@{prefix-cls} {
& {
position: absolute; /* 或者使用固定定位等其他方式 */
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5); /* 半透明遮罩 */
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
z-index: 99;
}
.progress-bar-rear {
width: 100px; /* 进度条宽度 */
height: 10px; /* 进度条高度 */
background-color: v-bind(rearColor); /* 进度条颜色 */
border-radius: 4px;
}
.progress-bar-front {
height: 10px; /* 进度条高度 */
background-color: v-bind(frontColor); /* 进度条颜色 */
border-radius: 4px;
}
.value {
color: #fff;
margin-left: 5px;
font-size: 16px;
font-weight: 600;
}
}
</style>

View File

@ -94,4 +94,15 @@ export function definedComponent() {
// addComponent(JVxeTypes.userSelect, JVxeUserSelectCell)
}
/**
* 清空注册的组件
*/
export function clearComponent() {
componentMap.clear();
// update-begin--author:liaozhiyang---date:20231208---for【issues/860】生成的一对多代码热更新之后点击新增卡死[暂时先解决]
import.meta.env.DEV && (window[JVxeComponents] = componentMap);
// update-end--author:liaozhiyang---date:20231208---for【issues/860】生成的一对多代码热更新之后点击新增卡死[暂时先解决]
}
export { componentMap };

View File

@ -5,15 +5,17 @@
<template #buttons>
<div :class="`${prefixCls}-button div`" :size="btnSize">
<slot v-if="showPrefix" name="toolbarPrefix" :size="btnSize" />
<a-button v-if="showAdd" type="primary" preIcon="ant-design:plus-outlined" :disabled="disabled" :loading="deleting" @click="trigger('add')">
<span>新增</span>
<a-button v-if="addBtnCfg.enabled && showAdd" type="primary" :preIcon="addBtnCfg.buttonIcon" :disabled="disabled" :loading="deleting" @click="trigger('add')">
<span>{{ addBtnCfg.buttonName }}</span>
</a-button>
<a-button v-if="showSave" preIcon="ant-design:save-outlined" :disabled="disabled" @click="trigger('save')">
<span>保存</span>
</a-button>
<template v-if="deleting || selectedRowIds.length > 0">
<Popconfirm v-if="showRemove" :title="`确定要删除这 ${selectedRowIds.length} 项吗?`" :disabled="deleting" @confirm="onRemove">
<a-button preIcon="ant-design:minus-outlined" :disabled="disabled" :loading="deleting">删除</a-button>
<Popconfirm v-if="removeBtnCfg.enabled && showRemove" :title="`确定要删除这 ${selectedRowIds.length} 项吗?`" :disabled="deleting" @confirm="onRemove">
<a-button :preIcon="removeBtnCfg.buttonIcon" :disabled="disabled" :loading="deleting">
<span>{{ removeBtnCfg.buttonName }}</span>
</a-button>
</Popconfirm>
<template v-if="showClearSelection">
<a-button preIcon="ant-design:delete-outlined" @click="trigger('clearSelection')">清空选择</a-button>
@ -46,6 +48,16 @@
disabledRows: propTypes.object,
hasBtnAuth: propTypes.func,
selectedRowIds: propTypes.array.def(() => []),
addBtnCfg: propTypes.object.def(() => ({
enabled: true,
buttonIcon: 'ant-design:plus-outlined',
buttonName: '新增',
})),
removeBtnCfg: propTypes.object.def(() => ({
enabled: true,
buttonIcon: 'ant-design:minus-outlined',
buttonName: '删除',
})),
});
const emit = defineEmits(['save', 'add', 'remove', 'clearSelection', 'register']);
const xToolbarRef = ref({} as VxeToolbarInstance);

View File

@ -10,8 +10,10 @@
<a-menu>
<a-menu-item key="0" :disabled="disabledMoveUp" @click="handleRowMoveUp">向上移</a-menu-item>
<a-menu-item key="1" :disabled="disabledMoveDown" @click="handleRowMoveDown">向下移</a-menu-item>
<a-menu-divider />
<a-menu-item key="3" @click="handleRowInsertDown">插入一行</a-menu-item>
<template v-if="allowInsertRow">
<a-menu-divider />
<a-menu-item key="3" @click="handleRowInsertDown">插入一行</a-menu-item>
</template>
</a-menu>
</template>
</a-dropdown>
@ -48,6 +50,9 @@
const disabledMoveUp = computed(() => rowIndex.value === 0);
const disabledMoveDown = computed(() => rowIndex.value === fullDataLength.value - 1);
// 是否允许插入行
const allowInsertRow = computed(() => originColumn.value.insertRow);
/** 向上移 */
function handleRowMoveUp() {
if (!disabledMoveUp.value) {
@ -79,7 +84,8 @@
handleRowMoveUp,
handleRowMoveDown,
handleRowInsertDown,
isAllowDrag
isAllowDrag,
allowInsertRow,
};
},
// 【组件增强】注释详见JVxeComponent.Enhanced

View File

@ -30,7 +30,8 @@ export function useColumns(props: JVxeTableProps, data: JVxeDataProps, methods:
// handle 方法参数
const args: HandleArgs = { props, slots, data, methods, columns };
let seqColumn, selectionColumn, expandColumn, dragSortColumn;
props.columns.forEach((column: JVxeColumn) => {
const handleColumn = (column: JVxeColumn, container: JVxeColumn[]) => {
// 排除未授权的列 1 = 显示/隐藏; 2 = 禁用
let auth = methods.getColAuth(column.key);
if (auth?.type == '1' && !auth.isAuth) {
@ -47,6 +48,15 @@ export function useColumns(props: JVxeTableProps, data: JVxeDataProps, methods:
if (col.type === JVxeTypes.hidden) {
return handleInnerColumn(args, col, handleHiddenColumn);
}
// 处理子级列
// 判断是否是分组列,如果当前是父级,则无需处理 render
if (Array.isArray(col.children) && col.children.length > 0) {
const children: JVxeColumn[] = [];
col.children.forEach((child: JVxeColumn) => handleColumn(child, children));
col.children = children;
container.push(col);
return;
}
// 组件未注册,自动设置为 normal
if (!isRegistered(col.type)) {
col.type = JVxeTypes.normal;
@ -72,21 +82,25 @@ export function useColumns(props: JVxeTableProps, data: JVxeDataProps, methods:
};
if (col.type === JVxeTypes.rowNumber) {
seqColumn = col;
columns.push(col);
container.push(col);
} else if (col.type === JVxeTypes.rowRadio || col.type === JVxeTypes.rowCheckbox) {
selectionColumn = col;
columns.push(col);
container.push(col);
} else if (col.type === JVxeTypes.rowExpand) {
expandColumn = col;
columns.push(col);
container.push(col);
} else if (col.type === JVxeTypes.rowDragSort) {
dragSortColumn = col;
columns.push(col);
container.push(col);
} else {
col.params = column;
args.columns = container;
handlerCol(args);
}
});
}
props.columns.forEach((column: JVxeColumn) => handleColumn(column, columns));
handleInnerColumn(args, seqColumn, handleSeqColumn);
handleInnerColumn(args, selectionColumn, handleSelectionColumn);
handleInnerColumn(args, expandColumn, handleExpandColumn);
@ -250,6 +264,7 @@ function handleDragSortColumn({ props, data, col, columns, renderOptions }: Hand
align: 'center',
// update-begin--author:liaozhiyang---date:20240417---for:【QQYUN-8785】online表单列位置的id未做限制拖动其他列到id列上面同步数据库时报错
params: {
insertRow: props.insertRow,
notAllowDrag: props.notAllowDrag,
...col?.params,
},

View File

@ -16,6 +16,8 @@ export function useToolbar(props: JVxeTableProps, data: JVxeDataProps, methods:
hasBtnAuth: methods.hasBtnAuth,
selectedRowIds: data.selectedRowIds.value,
custom: props.custom,
addBtnCfg: props.addBtnCfg,
removeBtnCfg: props.removeBtnCfg,
// 新增事件
onAdd: () => {
// update-begin--author:liaozhiyang---date:20240521---for【TV360X-212】online新增字段就出校验提示

View File

@ -1,13 +1,12 @@
/* JVxeTable 行编辑 权限 */
import { usePermissionStoreWithOut } from '/@/store/modules/permission';
const permissionStore = usePermissionStoreWithOut();
/**
* JVxe 专用,获取权限
* @param prefix
*/
export function getJVxeAuths(prefix) {
const permissionStore = usePermissionStoreWithOut();
prefix = getPrefix(prefix);
let { authList, allAuthList } = permissionStore;
let authsMap = new Map<string, typeof allAuthList[0]>();

View File

@ -42,6 +42,8 @@ export const vxeProps = () => ({
rowExpand: propTypes.bool.def(false),
// 展开行配置
expandConfig: propTypes.object.def(() => ({})),
// 是否可插入行
insertRow: propTypes.bool.def(true),
// 页面是否在加载中
loading: propTypes.bool.def(false),
// 表格高度
@ -113,6 +115,11 @@ export const vxeProps = () => ({
// 不允许拖拽的行 [{'key':field,'value':value}]
notAllowDrag: propTypes.array.def(() => []),
// update-end--author:liaozhiyang---date:20240417---for:【QQYUN-8785】online表单列位置的id未做限制拖动其他列到id列上面同步数据库时报错
// 新增按钮配置
addBtnCfg: propTypes.object,
// 删除按钮配置
removeBtnCfg: propTypes.object,
});
export const vxeEmits = ['save', 'added', 'removed', 'inserted', 'dragged', 'selectRowChange', 'pageChange', 'valueChange', 'blur'];

View File

@ -70,9 +70,12 @@
// update-begin--author:liaozhiyang---date:20231218---for【QQYUN-6366】升级到antd4.x
.ant-modal {
.ant-modal-close {
position: absolute;
top: 0;
right: 0;
// update-begin--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
// position: absolute;
// top: 0;
// right: 0;
top: 13px;
// update-end--author:liaozhiyang---date:20241010---for【issues/7260】原生a-modal关闭按钮位置偏移
width: auto;
height: auto;
}

View File

@ -13,6 +13,10 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
VITE_GLOB_APP_OPEN_QIANKUN,
VITE_GLOB_DOMAIN_URL,
VITE_GLOB_ONLINE_VIEW_URL,
// 【JEECG作为乾坤子应用】
VITE_GLOB_QIANKUN_MICRO_APP_NAME,
VITE_GLOB_QIANKUN_MICRO_APP_ENTRY,
} = getAppEnvConfig();
// if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
@ -36,7 +40,19 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
urlPrefix: VITE_GLOB_API_URL_PREFIX,
uploadUrl: VITE_GLOB_DOMAIN_URL,
viewUrl: VITE_GLOB_ONLINE_VIEW_URL,
// 【JEECG作为乾坤子应用】是否以乾坤子应用模式启动
isQiankunMicro: VITE_GLOB_QIANKUN_MICRO_APP_NAME != null && VITE_GLOB_QIANKUN_MICRO_APP_NAME !== '',
// 【JEECG作为乾坤子应用】乾坤子应用入口
qiankunMicroAppEntry: VITE_GLOB_QIANKUN_MICRO_APP_ENTRY,
};
// 【JEECG作为乾坤子应用】乾坤子应用下需要定义一下
if (!window['_CONFIG']) {
window['_CONFIG'] = {}
}
// @ts-ignore
window._CONFIG['domianURL'] = VITE_GLOB_DOMAIN_URL;
return glob as Readonly<GlobConfig>;
};

View File

@ -0,0 +1,349 @@
import { ref, nextTick, getCurrentInstance, watch } from 'vue';
import { getToken } from '/@/utils/auth';
import md5 from 'crypto-js/md5';
import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
import { useGlobSetting } from '/@/hooks/setting';
import { useModal } from '/@/components/Modal';
import { useUserStore } from '/@/store/modules/user';
//弹窗业务需求引用的
import { useMyRunningTaskList } from '@/hooks/jeecg/process/useMyRunningTaskList';
import { hisProcessNodeInfo } from '@/hooks/jeecg/process/useBpmNodeInfo';
import { queryByCode, save } from '@/views/super/eoa/cmsbpm/cmsbpm.api';
import { isUrl } from '@/utils/is';
import { getQueryVariable, getUrlParams } from '@/utils';
import { backProcess, invalidProcess } from '@/views/super/eoa/cmsbpm/cmsbpm.api';
import { editReadStatus } from '@/views/super/eoa/cmsoa/cmsoa.api';
import { useRouter } from 'vue-router';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage } = useMessage();
export function useDragNotice() {
//*********************************websocket配置begin******************************************
const glob = useGlobSetting();
const { push, currentRoute } = useRouter();
const userStore = useUserStore();
const instance: any = getCurrentInstance();
const taskDealRef = ref<any>(null);
const desformRef = ref<any>(null);
// 初始化 WebSocket
function initWebSocket() {
const token = getToken();
//将登录token生成一个短的标识
const wsClientId = md5(token);
// WebSocket与普通的请求所用协议有所不同ws等同于httpwss等同于https
const url = glob.domainUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/dragChannelSocket/' + wsClientId;
connectWebSocket(url);
onWebSocket(onWebSocketMessage);
}
async function onWebSocketMessage(data) {
console.log('仪表盘监听按钮点击事件websocket', data);
if (data?.CMD === 'drag') {
//触发动作: url路径 modal弹窗
const action = data.result.action;
//弹窗类型: 点击按钮打开什么弹窗根据type打开不同的弹窗
const type = data.result.type;
//url地址可以是路由也可以是外部链接
let url = data.result.url;
//弹窗参数或者url参数
const record = data.result.records || {};
console.log('仪表盘监听点击事件类型type', type);
console.log('仪表盘监听点击事件动作action', action);
console.log('仪表盘监听点击事件路径url', url);
console.log('仪表盘监听点击事件参数', record);
//1.路径的话,判断外部链接还是内部路由跳转
if (action == 'url') {
//常用下載特殊处理
if (url == 'fileUrl') {
url = record[url];
}
const urlParamsObj = getUrlParams(url);
if (url.startsWith('http')) {
window.open(url, '_blank');
} else {
push({ path: urlParamsObj.url, query: { ...urlParamsObj.params, ...record } });
}
} else {
//2.弹窗方式打开项目组件
switch (type) {
case 'task':
//流程办理弹窗
const { formData, formUrl } = await getTaskNodeInfo(record);
formData['PROCESS_TAB_TYPE'] = 'run';
handleOpenType('task', { formData, formUrl, title: '流程办理' });
break;
case 'history':
//历史流程弹窗
hisProcessNodeInfo({ procInstId: record.processInstanceId }).then((res) => {
console.log('获取流程节点信息', res);
if (res.success) {
const formData = {
dataId: res.result.dataId,
taskId: record.id,
taskDefKey: record.taskId,
procInsId: record.processInstanceId,
tableName: res.result.tableName,
vars: res.result.records,
};
let tempFormUrl = res.result.formUrl;
//节点配置表单URLVUE组件类型对应的拓展参数
if (tempFormUrl && tempFormUrl.indexOf('?') != -1 && !isUrl(tempFormUrl) && tempFormUrl.indexOf('{{DOMAIN_URL}}') == -1) {
tempFormUrl = res.result.formUrl.split('?')[0];
formData['extendUrlParams'] = getQueryVariable(res.result.formUrl);
}
currentModal.value = null;
modalParams.value = { path: tempFormUrl, formData };
taskDealRef.value.deal(record);
taskDealRef.value.data.title = '流程历史';
console.log('taskDealRef', taskDealRef.value);
}
});
break;
case 'entrust':
//委托办理
handleOpenType('entrust', { taskId: record.id });
break;
case 'taskNotify':
//催办任务
handleOpenType('taskNotify', {
title: '催办提醒',
procInstId: record.processInstanceId,
});
break;
case 'invalid':
//作废流程
await invalidProcess({
processInstanceId: record.processInstanceId,
});
reloadPage();
break;
case 'backTask':
//取回流程
await backProcess({
processInstanceId: record.processInstanceId,
});
reloadPage();
break;
case 'sysNotice':
//通知公告弹窗
await editReadStatus({ anntId: record.anntId });
currentModal.value = null;
if (record.openType === 'component') {
modalParams.value = { path: record.openPage, formData: { id: record.busId } };
instance.refs.dynamicNoticeRef?.detail(record.openPage);
} else {
instance.refs.detailRef?.show({
record,
isUpdate: true,
});
}
break;
case 'email':
//邮箱查看弹窗
handleOpenType('email', { record });
break;
case 'plan':
//我的计划弹窗
let data = {};
if (!!record?.id) {
data = { id: record?.id };
instance.refs.planRef?.show({
isUpdate: !!record?.id,
record: data,
});
} else {
data = { joinPerson: userStore.getUserInfo.username, ...record };
instance.refs.planRef?.show({
isUpdate: !!record?.id,
...data,
});
}
break;
case 'desform':
const title = '表单【' + record.desformName + '】发起申请';
bindParams.value = { dialogOptions: { top: 60, width: 1000, padding: { top: 25, right: 25, bottom: 30, left: 25 } } };
openDesformModal('add', record, title);
break;
default:
break;
}
}
}
}
//*********************************websocket配置end******************************************
//*********************************打开弹窗修改动态设置弹窗begin*******************************
//当前表单弹窗
const currentModal = ref<string | null>(null);
//当前表单参数
const modalParams = ref<Recordable>({});
//表单注册缓存
const modalRegCache = ref<Recordable>({});
//组件绑定参数
const bindParams = ref<Recordable>({});
/**
* 根据类型打开不同弹窗
* @param type
* @param params
*/
async function handleOpenType(type, params) {
currentModal.value = null;
modalParams.value = { ...params };
switch (type) {
case 'task':
//流程办理
currentModal.value = 'TaskHandleModal';
break;
case 'history':
currentModal.value = 'HisTaskDealModal';
case 'entrust':
//委托
currentModal.value = 'SelectEntrusterModal';
break;
case 'taskNotify':
//催办
currentModal.value = 'TaskNotifyModal';
break;
case 'email':
//邮件查看
currentModal.value = 'EoaMailBoxInModal';
break;
default:
currentModal.value = null;
break;
}
//注册表单弹窗
initModalRegister();
await nextTick(() => {
if (modalRegCache.value[currentModal.value!]?.isRegister) {
console.log('已注冊,走缓存');
modalRegCache.value[currentModal.value!].modalMethods.openModal(true, modalParams.value);
}
});
}
/**
* 初始化弹窗注册
*/
function initModalRegister() {
//如果当前选择表单为null就不处理
if (!currentModal.value) {
return;
}
//判断缓存中是否存在,不存在就走缓存逻辑
if (!modalRegCache.value[currentModal.value]) {
const [registerModal, modalMethods] = useModal();
modalRegCache.value[currentModal.value] = {
isRegister: false,
register: bindRegisterModal(registerModal, modalMethods),
modalMethods,
};
}
}
/**
* 绑定注册弹窗
* @param regFn
* @param modalMethod
*/
function bindRegisterModal(regFn, modalMethod) {
return async (...args) => {
console.log('开始注册:', currentModal.value);
await regFn(...args);
console.log('注册完成:', currentModal.value);
//打开弹窗
modalMethod.openModal(true, modalParams.value);
//设置缓存标识
modalRegCache.value[currentModal.value!].isRegister = true;
};
}
//*********************************打开弹窗修改动态设置弹窗end******************************************
//***********************打开弹窗的业务逻辑处理*********************************************************
const { getTaskNodeInfo } = useMyRunningTaskList('run');
/**
* 打开表单
*/
function openDesformModal(mode, record, title) {
let desform = record,
dataId = null;
if (mode === 'edit' || mode === 'detail') {
let { desformId: id, desformCode, desformDataId } = record;
dataId = desformDataId;
desform = { id, desformCode };
}
queryByCode({ desformCode: desform.desformCode }).then((res) => {
if (res.success) {
const designJson = res.result.desformDesignJson;
const json = JSON.parse(designJson);
if (json.config.dialogOptions) {
bindParams.value = { dialogOptions: json.config.dialogOptions };
}
desformRef.value.open(mode, desform, dataId, title);
}
});
}
/**
* 添加我的申请表单流程
*/
function handleDesformDataAdded(event) {
const flowCodePre = 'desform_';
// 将流程保存至后台
let { desform, dataId } = event;
save({
desformId: desform.id,
desformCode: desform.desformCode,
desformDataId: dataId,
desformName: desform.desformName,
processName: desform.procName,
flowCode: `${flowCodePre}${desform.desformCode}`,
titleExp: desform.titleExp,
}).then((res) => {
if (!res.success) {
createMessage.error(res.message);
return;
}
push({ path: '/oaOffice/myOrder' });
});
}
/**
* 邮箱回复
* @param record
*/
function handReply(record) {
//跳转到邮箱
push({ name: 'eoa-email', params: { type: 'process', rec: JSON.stringify(record.value) } });
}
watch(
() => currentRoute.value,
(value) => {
if (value?.meta?.frameSrc && value?.meta?.frameSrc.indexOf('/drag/view?pageId=') >= 0) {
reloadPage();
}
}
);
//刷新页面
function reloadPage() {
const iframes: any = document.getElementsByClassName('jeecg-iframe-page__main');
// 将 HTMLCollection 转换为数组
const iframeArray = Array.from(iframes);
if (currentRoute.value?.meta?.frameSrc && currentRoute.value?.meta?.frameSrc.indexOf('/drag/view?pageId=') >= 0) {
const targetIframe: any = iframeArray.find((iframe: any) => iframe.src == currentRoute.value?.meta?.frameSrc);
console.log('targetIframe', targetIframe);
if (targetIframe) {
targetIframe.contentWindow.postMessage({ reload: true }, '*');
}
}
}
return {
initDragWebSocket: initWebSocket,
handleOpenType,
currentModal,
modalParams,
modalRegCache,
bindParams,
taskDealRef,
desformRef,
handleDesformDataAdded,
handReply,
reloadPage,
};
}

View File

@ -1,7 +1,7 @@
<template>
<div :style="getPlaceholderDomStyle" v-if="getIsShowPlaceholderDom"></div>
<div :style="getWrapStyle" :class="getClass">
<LayoutHeader v-if="getShowInsetHeaderRef" />
<LayoutHeader v-if="getShowHeader" />
<MultipleTabs v-if="getShowTabs" />
</div>
</template>
@ -11,6 +11,8 @@
import LayoutHeader from './index.vue';
import MultipleTabs from '../tabs/index.vue';
import { useAppStore } from "@/store/modules/app";
import { useGlobSetting } from "/@/hooks/setting";
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useFullContent } from '/@/hooks/web/useFullContent';
@ -19,6 +21,7 @@
import { useDesign } from '/@/hooks/web/useDesign';
import { useLayoutHeight } from '../content/useContentViewHeight';
import { TabsThemeEnum } from '/@/enums/appEnum';
// update-begin--author:liaozhiyang---date:20240407---for【QQYUN-8774】网站header区域加高
const HEADER_HEIGHT = 60;
// update-begin--author:liaozhiyang---date:20240407---for【【QQYUN-8774】网站header区域加高
@ -35,15 +38,30 @@
const { setHeaderHeight } = useLayoutHeight();
const { prefixCls } = useDesign('layout-multiple-header');
const appStore = useAppStore()
const glob = useGlobSetting()
const { getCalcContentWidth, getSplit } = useMenuSetting();
const { getIsMobile } = useAppInject();
const { getFixed, getShowInsetHeaderRef, getShowFullHeaderRef, getHeaderTheme, getShowHeader } = useHeaderSetting();
const { getFixed, getShowInsetHeaderRef, getShowFullHeaderRef, getHeaderTheme } = useHeaderSetting();
const { getFullContent } = useFullContent();
const { getShowMultipleTab, getTabsTheme } = useMultipleTabSetting();
const getShowHeader = computed(() => {
// 控制是否显示顶部
if (appStore.mainAppProps.hideHeader) {
return false;
}
return unref(getShowInsetHeaderRef);
})
const getShowTabs = computed(() => {
// 控制是否显示多Tabs切换
if (appStore.mainAppProps.hideMultiTabs) {
return false;
}
return unref(getShowMultipleTab) && !unref(getFullContent);
});
@ -53,7 +71,7 @@
const getWrapStyle = computed((): CSSProperties => {
const style: CSSProperties = {};
if (unref(getFixed)) {
if (unref(getFixed) && !glob.isQiankunMicro) {
style.width = unref(getIsMobile) ? '100%' : unref(getCalcContentWidth);
}
if (unref(getShowFullHeaderRef)) {
@ -83,7 +101,7 @@
if ((unref(getShowFullHeaderRef) || !unref(getSplit)) && unref(getShowHeader) && !unref(getFullContent)) {
height += HEADER_HEIGHT;
}
if (unref(getShowMultipleTab) && !unref(getFullContent)) {
if (unref(getShowTabs) && !unref(getFullContent)) {
height += unref(getTabsThemeHeight);
}
setHeaderHeight(height);
@ -93,10 +111,15 @@
});
const getClass = computed(() => {
return [prefixCls, `${prefixCls}--${unref(getHeaderTheme)}`, { [`${prefixCls}--fixed`]: unref(getIsFixed) }];
return [prefixCls, `${prefixCls}--${unref(getHeaderTheme)}`, {
[`${prefixCls}--fixed`]: unref(getIsFixed),
// 【JEECG作为乾坤子应用】
[`${prefixCls}--qiankun-micro`]: glob.isQiankunMicro,
}];
});
return {
glob,
getClass,
prefixCls,
getPlaceholderDomStyle,
@ -104,7 +127,7 @@
getWrapStyle,
getIsShowPlaceholderDom,
getShowTabs,
getShowInsetHeaderRef,
getShowHeader,
};
},
});
@ -126,5 +149,11 @@
z-index: @multiple-tab-fixed-z-index;
width: 100%;
}
// 【JEECG作为乾坤子应用】
&--qiankun-micro {
position: absolute;
}
}
</style>

View File

@ -12,6 +12,7 @@
import { ScrollContainer } from '/@/components/Container';
import { useGo } from '/@/hooks/web/usePage';
import { useGlobSetting } from "/@/hooks/setting";
import { useSplitMenu } from './useLayoutMenu';
import { openWindow } from '/@/utils';
import { propTypes } from '/@/utils/propTypes';
@ -56,6 +57,8 @@
const { prefixCls } = useDesign('layout-menu');
const glob = useGlobSetting()
const { menusRef } = useSplitMenu(toRef(props, 'splitType'));
const { getIsMobile } = useAppInject();
@ -67,6 +70,11 @@
const getIsShowLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
const getUseScroll = computed(() => {
// 【JEECG作为乾坤子应用】在乾坤子应用下菜单不固定
if (glob.isQiankunMicro) {
return false;
}
return (
!unref(getIsHorizontal) &&
(unref(getIsSidebarType) || props.splitType === MenuSplitTyeEnum.LEFT || props.splitType === MenuSplitTyeEnum.NONE)

View File

@ -30,12 +30,15 @@
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
import { useAppStore } from "@/store/modules/app";
import { useGlobSetting } from "/@/hooks/setting";
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { useDesign } from '/@/hooks/web/useDesign';
import DragBar from './DragBar.vue';
export default defineComponent({
name: 'LayoutSideBar',
components: { Sider: Layout.Sider, LayoutMenu, DragBar, LayoutTrigger },
@ -48,6 +51,9 @@
const { prefixCls } = useDesign('layout-sideBar');
const glob = useGlobSetting()
const appStore = useAppStore()
const { getIsMobile } = useAppInject();
const { getTriggerAttr, getShowTrigger } = useTrigger(getIsMobile);
@ -65,6 +71,10 @@
});
const showClassSideBarRef = computed(() => {
// 控制是否显示侧边栏
if (appStore.mainAppProps.hideSider) {
return false;
}
return unref(getSplit) ? !unref(getMenuHidden) : true;
});
@ -74,6 +84,8 @@
{
[`${prefixCls}--fixed`]: unref(getMenuFixed),
[`${prefixCls}--mix`]: unref(getIsMixMode) && !unref(getIsMobile),
// 【JEECG作为乾坤子应用】
[`${prefixCls}--qiankun-micro`]: glob.isQiankunMicro,
},
];
});
@ -131,6 +143,11 @@
height: 100%;
}
// 【JEECG作为乾坤子应用】
&--qiankun-micro {
position: absolute !important;
}
&--mix {
top: @header-height;
height: calc(100% - @header-height);

View File

@ -145,7 +145,9 @@ html[data-theme='light'] {
}
.ant-tabs-extra-content {
margin-top: 2px;
// update-begin--author:liaozhiyang---date:20241016---for【issues/7345】标签样式切换到极简模式样式错乱
// margin-top: 2px;
// update-end--author:liaozhiyang---date:20241016---for【issues/7345】标签样式切换到极简模式样式错乱
line-height: @multiple-height !important;
}

View File

@ -19,18 +19,20 @@
</template>
<template #rightExtra v-if="getShowRedo || getShowQuick">
<TabRedo v-if="getShowRedo" />
<!-- <TabContent isExtra :tabItem="$route" v-if="getShowQuick" /> -->
<!-- 列表页全屏
<FoldButton v-if="getShowFold" />-->
<!-- <FullscreenOutlined /> -->
<router-link to="/ai" class="ai-icon">
<a-tooltip title="AI助手" placement="left">
<svg t="1706259688149" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2056" width="17" height="17">
<path d="M826.368 325.632c0-7.168 2.048-10.24 10.24-10.24h123.904c7.168 0 10.24 2.048 10.24 10.24v621.568c0 7.168-2.048 10.24-10.24 10.24h-122.88c-8.192 0-10.24-4.096-10.24-10.24l-1.024-621.568z m-8.192-178.176c0-50.176 35.84-79.872 79.872-79.872 48.128 0 79.872 32.768 79.872 79.872 0 52.224-33.792 79.872-81.92 79.872-46.08 1.024-77.824-27.648-77.824-79.872zM462.848 584.704C441.344 497.664 389.12 307.2 368.64 215.04h-2.048c-16.384 92.16-58.368 247.808-92.16 369.664h188.416zM243.712 712.704l-62.464 236.544c-2.048 7.168-4.096 8.192-12.288 8.192H54.272c-8.192 0-10.24-2.048-8.192-12.288l224.256-783.36c4.096-13.312 7.168-26.624 8.192-65.536 0-6.144 2.048-8.192 7.168-8.192H450.56c6.144 0 8.192 2.048 10.24 8.192l250.88 849.92c2.048 7.168 0 10.24-7.168 10.24H573.44c-7.168 0-10.24-2.048-12.288-7.168l-65.536-236.544c1.024 1.024-251.904 0-251.904 0z" fill="#333333" p-id="19816"></path>
</svg>
</a-tooltip>
</router-link>
<div class="rightExtra">
<TabRedo v-if="getShowRedo" />
<!-- <TabContent isExtra :tabItem="$route" v-if="getShowQuick" /> -->
<!-- 列表页全屏
<FoldButton v-if="getShowFold" />-->
<!-- <FullscreenOutlined /> -->
<router-link to="/ai" class="ai-icon">
<a-tooltip title="AI助手" placement="left">
<svg t="1706259688149" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2056" width="17" height="17">
<path d="M826.368 325.632c0-7.168 2.048-10.24 10.24-10.24h123.904c7.168 0 10.24 2.048 10.24 10.24v621.568c0 7.168-2.048 10.24-10.24 10.24h-122.88c-8.192 0-10.24-4.096-10.24-10.24l-1.024-621.568z m-8.192-178.176c0-50.176 35.84-79.872 79.872-79.872 48.128 0 79.872 32.768 79.872 79.872 0 52.224-33.792 79.872-81.92 79.872-46.08 1.024-77.824-27.648-77.824-79.872zM462.848 584.704C441.344 497.664 389.12 307.2 368.64 215.04h-2.048c-16.384 92.16-58.368 247.808-92.16 369.664h188.416zM243.712 712.704l-62.464 236.544c-2.048 7.168-4.096 8.192-12.288 8.192H54.272c-8.192 0-10.24-2.048-8.192-12.288l224.256-783.36c4.096-13.312 7.168-26.624 8.192-65.536 0-6.144 2.048-8.192 7.168-8.192H450.56c6.144 0 8.192 2.048 10.24 8.192l250.88 849.92c2.048 7.168 0 10.24-7.168 10.24H573.44c-7.168 0-10.24-2.048-12.288-7.168l-65.536-236.544c1.024 1.024-251.904 0-251.904 0z" fill="#333333" p-id="19816"></path>
</svg>
</a-tooltip>
</router-link>
</div>
</template>
</Tabs>
</div>
@ -162,16 +164,25 @@
:deep(.anticon) {
display: inline-block;
}
.ai-icon{
float: right;
margin-top: 3px;
cursor: pointer;
width: 36px;
height: 50px;
line-height: 50px;
color: @text-color;
text-align: center;
border-left: 1px solid @border-color-base;
// update-begin--author:liaozhiyang---date:20241016---for【issues/7345】标签样式切换到极简模式样式错乱
.rightExtra {
display: flex;
:deep(svg) {
&:not(.icon) {
vertical-align: -0.3em;
}
}
.ai-icon {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
width: 36px;
color: @text-color;
text-align: center;
border-left: 1px solid @border-color-base;
}
}
// update-end--author:liaozhiyang---date:20241016---for【issues/7345】标签样式切换到极简模式样式错乱
}
</style>

View File

@ -1,3 +1,4 @@
import type { MainAppProps } from "#/main";
import 'uno.css';
import '/@/design/index.less';
import 'ant-design-vue/dist/reset.css';
@ -8,7 +9,7 @@ import App from './App.vue';
import { createApp } from 'vue';
import { initAppConfigStore } from '/@/logics/initAppConfig';
import { setupErrorHandle } from '/@/logics/error-handle';
import { router, setupRouter } from '/@/router';
import { router, createRouter, setupRouter } from '/@/router';
import { setupRouterGuard } from '/@/router/guard';
import { setupStore } from '/@/store';
import { setupGlobDirectives } from '/@/directives';
@ -16,20 +17,47 @@ import { setupI18n } from '/@/locales/setupI18n';
import { registerGlobComp } from '/@/components/registerGlobComp';
import { registerThirdComp } from '/@/settings/registerThirdComp';
import { useSso } from '/@/hooks/web/useSso';
import { checkIsQiankunMicro } from "/@/qiankun/micro";
import { autoUseQiankunMicro } from "/@/qiankun/micro/qiankunMicro";
import { useAppStoreWithOut } from "@/store/modules/app";
// 注册online模块lib
import { registerPackages } from '/@/utils/monorepo/registerPackages';
async function bootstrap() {
// 程序入口
async function main() {
if (checkIsQiankunMicro()) {
// 【JEECG作为乾坤子应用】以乾坤子应用模式启动
// await autoUseQiankunMicro(bootstrap)
await autoUseQiankunMicro(bootstrap)
} else {
// 获取参数
const props = getMainAppProps();
// 普通启动
await bootstrap(props)
}
}
main();
async function bootstrap(props?: MainAppProps) {
// 创建应用实例
const app = createApp(App);
// 【QQYUN-6329】
window.appRootInstance = app;
// 多语言配置,异步情况:语言文件可以从服务器端获得
await setupI18n(app);
window['JAppRootInstance'] = app;
// 创建路由
createRouter();
// 配置存储
setupStore(app);
// 配置参数
setupProps(props);
// 多语言配置,异步情况:语言文件可以从服务器端获得
await setupI18n(app);
// 初始化内部系统配置
initAppConfigStore();
@ -61,9 +89,45 @@ async function bootstrap() {
await router.isReady();
// 挂载应用
app.mount('#app', true);
app.mount(getMountContainer(props), true);
console.log(" vue3 app 加载完成!")
return app
}
bootstrap();
// 获取应用挂载容器
function getMountContainer(props?: MainAppProps) {
const id = '#app';
if (!props?.container?.querySelector) {
return id;
}
return props.container.querySelector(id) ?? id;
}
// 获取主应用参数
function getMainAppProps(): MainAppProps {
// 从 queryString 中获取
const searchParams = new URLSearchParams(window.location.search);
// 隐藏侧边栏(菜单)
let hideSider = searchParams.get('hideSider') === 'true';
// 隐藏顶部
let hideHeader = searchParams.get('hideHeader') === 'true';
// 隐藏 多Tab 切换
let hideMultiTabs = searchParams.get('hideMultiTabs') === 'true';
return {
hideSider,
hideHeader,
hideMultiTabs
}
}
// 配置主应用参数
function setupProps(props?: MainAppProps) {
if (!props) {
return
}
const appStore = useAppStoreWithOut();
appStore.setMainAppProps(props);
}

View File

@ -0,0 +1,12 @@
import {qiankunWindow} from 'vite-plugin-qiankun/dist/helper'
/**
* 【JEECG作为乾坤子应用】【判断当前是否是以乾坤子应用的模式运行】
*/
export function checkIsQiankunMicro(): boolean {
return !!qiankunWindow.__POWERED_BY_QIANKUN__;
}
export function getGlobal() {
return (checkIsQiankunMicro() ? qiankunWindow : window) as Window
}

View File

@ -0,0 +1,57 @@
/*
* 【JEECG作为乾坤子应用】
*/
import type {App} from 'vue';
import type {MainAppProps} from "#/main";
import {destroyStore} from "@/store";
import {destroyRouter} from "@/router";
import {clearComponent} from "@/components/jeecg/JVxeTable/src/componentMap";
import {renderWithQiankun} from 'vite-plugin-qiankun/dist/helper';
/**
* 以乾坤子应用模式运行
* @param render
*/
export async function useQiankunMicroApp(render: (props?: MainAppProps) => Promise<App>) {
let instance: Nullable<App> = null;
// 注册乾坤子应用生命周期函数
renderWithQiankun({
async mount(props) {
console.debug('[qiankun-micro] mount - props :', props)
instance = await render({
container: props.container!,
hideSider: props.hideSider,
hideHeader: props.hideHeader,
hideMultiTabs: props.hideMultiTabs,
});
},
bootstrap() {
console.debug('[qiankun-micro] bootstrap');
},
update(props) {
console.debug('[qiankun-micro] update: ', props);
},
unmount(props) {
console.debug('[qiankun-micro] unmount: ', props);
destroyStore();
destroyRouter();
if (instance) {
clearComponent();
instance.unmount();
instance._container.innerHTML = '';
instance = null;
}
},
});
return instance!
}
export async function autoUseQiankunMicro(fn: Fn) {
return useQiankunMicroApp(fn)
}

View File

@ -9,7 +9,7 @@ import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
import { RootRoute } from '/@/router/routes';
import { isOAuth2AppEnv } from '/@/views/sys/login/useLogin';
import {isOAuth2AppEnv, isOAuth2DingAppEnv} from '/@/views/sys/login/useLogin';
import { OAUTH2_THIRD_LOGIN_TENANT_ID } from "/@/enums/cacheEnum";
import { setAuthCache } from "/@/utils/auth";
import { PAGE_NOT_FOUND_NAME_404 } from '/@/router/constant';
@ -159,7 +159,13 @@ export function createPermissionGuard(router: Router) {
//==============================【首次登录并且是企业微信或者钉钉的情况下才会调用】==================
//判断是免登录页面,如果页面包含/tenantId/,那么就直接前往主页
if(isOAuth2AppEnv() && to.path.indexOf("/tenantId/") != -1){
next(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
//update-begin---author:wangshuai---date:2024-11-08---for:【TV360X-2958】钉钉登录后打开了敲敲云换其他账号登录后再打开敲敲云显示的是原来账号的应用---
if (isOAuth2DingAppEnv()) {
next(OAUTH2_LOGIN_PAGE_PATH);
} else {
next(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
}
//update-end---author:wangshuai---date:2024-11-08---for:【TV360X-2958】钉钉登录后打开了敲敲云换其他账号登录后再打开敲敲云显示的是原来账号的应用---
return;
}
//==============================【首次登录并且是企业微信或者钉钉的情况下才会调用】==================

View File

@ -1,8 +1,8 @@
import type { RouteRecordRaw } from 'vue-router';
import type { App } from 'vue';
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
import { basicRoutes } from './routes';
import {createRouter as createVueRouter, destroyRouter, router} from './router'
// 白名单应该包含基本静态路由
const WHITE_NAME_LIST: string[] = [];
@ -13,22 +13,26 @@ const getRouteNames = (array: any[]) =>
});
getRouteNames(basicRoutes);
// app router
export const router = createRouter({
history: createWebHistory(import.meta.env.VITE_PUBLIC_PATH),
routes: basicRoutes as unknown as RouteRecordRaw[],
strict: true,
scrollBehavior: () => ({ left: 0, top: 0 }),
});
/**
* 创建路由实例
*/
export function createRouter() {
let router = createVueRouter({
routes: basicRoutes as unknown as RouteRecordRaw[],
strict: true,
scrollBehavior: () => ({left: 0, top: 0}),
})
// TODO 【QQYUN-4517】【表单设计器】记录分享路由守卫测试
router.beforeEach(async (to, from, next) => {
//console.group('【QQYUN-4517】beforeEach');
//console.warn('from', from);
//console.warn('to', to);
//console.groupEnd();
next();
});
// TODO 【QQYUN-4517】【表单设计器】记录分享路由守卫测试
// @ts-ignore
router.beforeEach(async (to, from, next) => {
//console.group('【QQYUN-4517】beforeEach');
//console.warn('from', from);
//console.warn('to', to);
//console.groupEnd();
next();
});
}
// reset router
export function resetRouter() {
@ -44,3 +48,8 @@ export function resetRouter() {
export function setupRouter(app: App<Element>) {
app.use(router);
}
export {
router,
destroyRouter,
}

View File

@ -0,0 +1,40 @@
/*
* 路由实例存储文件,请勿轻易添加其他代码,防止出现 HMR 或其他问题
*/
import type {Router, RouterHistory} from 'vue-router';
import {createRouter as createVueRouter, createWebHistory, RouterOptions} from 'vue-router';
export let router: Router = null as unknown as Router;
export function setRouter(r: Router) {
router = r
}
let webHistory: Nullable<RouterHistory> = null;
/**
* 创建路由
* @param options 参数
*/
export function createRouter(options: Partial<RouterOptions>) {
webHistory = createWebHistory(import.meta.env.VITE_PUBLIC_PATH);
// app router
let router = createVueRouter({
history: webHistory,
routes: [],
...options,
});
setRouter(router)
return router
}
// 销毁路由
export function destroyRouter() {
setRouter(null as unknown as Router);
if (webHistory) {
webHistory.destroy();
}
webHistory = null
}

View File

@ -109,6 +109,10 @@ const setting: ProjectConfig = {
type: MenuTypeEnum.SIDEBAR,
// 菜单主题
theme: ThemeEnum.DARK,
// update-begin--author:liaozhiyang---date:20241203---for【issues/7522】解决menuSetting ts警告
// 左侧导航栏文字颜色调整区分彩色和暗黑 (不对应配置)
isThemeBright: false,
// update-end--author:liaozhiyang---date:20241203---for【issues/7522】解决menuSetting ts警告
// 分割菜单
split: false,
// 顶部菜单布局
@ -146,7 +150,7 @@ const setting: ProjectConfig = {
// 动画配置
transitionSetting: {
// 是否开启切换动画
// The disabled state will also disable pageLoadinng
// The disabled state will also disable pageLoading
enable: true,
// 动画名 Route basic switching animation

View File

@ -1,9 +1,19 @@
import type { App } from 'vue';
import type { Pinia } from 'pinia';
import { createPinia } from 'pinia';
const store = createPinia();
let store: Nullable<Pinia> = null;
export function setupStore(app: App<Element>) {
if (store == null) {
store = createPinia();
}
app.use(store);
}
// 销毁store
export function destroyStore() {
store = null;
}
export { store };

View File

@ -1,3 +1,4 @@
import type { MainAppProps } from "#/main";
import type { ProjectConfig, HeaderSetting, MenuSetting, TransitionSetting, MultiTabsSetting } from '/#/config';
import type { BeforeMiniState } from '/#/store';
@ -20,7 +21,9 @@ interface AppState {
// When the window shrinks, remember some states, and restore these states when the window is restored
beforeMiniInfo: BeforeMiniState;
// 页面跳转临时参数存储
messageHrefParams: any
messageHrefParams: any,
// 应用参数
mainAppProps: MainAppProps,
}
let timeId: TimeoutHandle;
export const useAppStore = defineStore({
@ -30,7 +33,8 @@ export const useAppStore = defineStore({
pageLoading: false,
projectConfig: Persistent.getLocal(PROJ_CFG_KEY),
beforeMiniInfo: {},
messageHrefParams: {}
messageHrefParams: {},
mainAppProps: {},
}),
getters: {
getPageLoading(): boolean {
@ -62,7 +66,10 @@ export const useAppStore = defineStore({
},
getMessageHrefParams():any{
return this.messageHrefParams;
}
},
getMainAppProps(): MainAppProps {
return this.mainAppProps;
},
},
actions: {
setPageLoading(loading: boolean): void {
@ -104,7 +111,14 @@ export const useAppStore = defineStore({
setMessageHrefParams(params: any): void {
this.messageHrefParams = params;
},
// 设置主应用参数
setMainAppProps(args: MainAppProps) {
this.mainAppProps.hideHeader = args.hideHeader ?? false;
this.mainAppProps.hideSider = args.hideSider ?? false;
this.mainAppProps.hideMultiTabs = args.hideMultiTabs ?? false;
},
},
});

View File

@ -3,6 +3,7 @@ import type { GlobEnvConfig } from '/#/config';
import { warn } from '/@/utils/log';
import pkg from '../../package.json';
import { getConfigFileName } from '../../build/getConfigFileName';
import { getGlobal } from "@/qiankun/micro";
export function getCommonStoragePrefix() {
const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
@ -17,10 +18,12 @@ export function getStorageShortName() {
export function getAppEnvConfig() {
const ENV_NAME = getConfigFileName(import.meta.env);
const global = getGlobal();
const ENV = (import.meta.env.DEV
? // Get the global configuration (the configuration will be extracted independently when packaging)
(import.meta.env as unknown as GlobEnvConfig)
: window[ENV_NAME as any]) as unknown as GlobEnvConfig;
: global[ENV_NAME as any]) as unknown as GlobEnvConfig;
const {
VITE_GLOB_APP_TITLE,
@ -33,6 +36,10 @@ export function getAppEnvConfig() {
VITE_GLOB_APP_CAS_BASE_URL,
VITE_GLOB_DOMAIN_URL,
VITE_GLOB_ONLINE_VIEW_URL,
// 【JEECG作为乾坤子应用】
VITE_GLOB_QIANKUN_MICRO_APP_NAME,
VITE_GLOB_QIANKUN_MICRO_APP_ENTRY,
} = ENV;
// if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
@ -52,6 +59,10 @@ export function getAppEnvConfig() {
VITE_GLOB_APP_CAS_BASE_URL,
VITE_GLOB_DOMAIN_URL,
VITE_GLOB_ONLINE_VIEW_URL,
// 【JEECG作为乾坤子应用】
VITE_GLOB_QIANKUN_MICRO_APP_NAME,
VITE_GLOB_QIANKUN_MICRO_APP_ENTRY,
};
}

View File

@ -140,6 +140,15 @@ const transform: AxiosTransform = {
config.params = undefined;
}
}
// update-begin--author:sunjianlei---date:220241019---for【JEECG作为乾坤子应用】作为乾坤子应用启动时拼接请求路径
if (globSetting.isQiankunMicro) {
if (config.url && config.url.startsWith('/')) {
config.url = globSetting.qiankunMicroAppEntry + config.url
}
}
// update-end--author:sunjianlei---date:220241019---for【JEECG作为乾坤子应用】作为乾坤子应用启动时拼接请求路径
return config;
},

View File

@ -4,7 +4,7 @@ import type { FormSchema } from "@/components/Form";
import { unref } from 'vue';
import { isObject } from '/@/utils/is';
import Big from 'big.js';
// update-begin--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开原因是带了#号,需要替换一下
export const URL_HASH_TAB = `__AGWE4H__HASH__TAG__PWHRG__`;
// update-end--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开原因是带了#号,需要替换一下
@ -123,7 +123,7 @@ export function cloneObject(obj) {
export const withInstall = <T>(component: T, alias?: string) => {
//console.log("---初始化---", component)
const comp = component as any;
comp.install = (app: App) => {
// @ts-ignore
@ -254,7 +254,9 @@ export function numToUpper(value) {
}
};
let lth = value.toString().length;
value *= 100;
// update-begin--author:liaozhiyang---date:20241202---for【issues/7493】numToUpper方法返回解决错误
value = new Big(value).times(100);
// update-end--author:liaozhiyang---date:20241202---for【issues/7493】numToUpper方法返回解决错误
value += '';
let length = value.length;
if (lth <= 8) {
@ -520,6 +522,10 @@ export function useConditionFilter() {
data.view = 'number';
}
switch (data.view) {
case 'file':
case 'image':
case 'password':
return commonConditionOptions;
case 'text':
case 'textarea':
case 'umeditor':
@ -550,3 +556,22 @@ export function useConditionFilter() {
};
return { filterCondition };
}
// 获取url中的参数
export const getUrlParams = (url) => {
const result = {
url: '',
params: {},
};
const list = url.split('?');
result.url = list[0];
const params = list[1];
if (params) {
const list = params.split('&');
list.forEach((ele) => {
const dic = ele.split('=');
const label = dic[0];
result.params[label] = dic[1];
});
}
return result;
};

View File

@ -4,7 +4,7 @@
<BasicForm @register="register" />
</CollapseContainer>
<CollapseContainer title="超过3行自动收起折叠时保留2行" class="mt-4">
<CollapseContainer title="超过5列自动收起折叠时保留2行" class="mt-4">
<BasicForm @register="register1" />
</CollapseContainer>
</PageWrapper>
@ -175,11 +175,12 @@
labelWidth: 120,
schemas: [...getSchamas(), ...getAppendSchemas(), { field: '', component: 'Divider', label: '更多字段' }, ...extraSchemas],
actionColOptions: {
span: 24,
span: 8,
},
compact: true,
showAdvancedButton: true,
alwaysShowLines: 2,
autoAdvancedCol: 2,
alwaysShowLines: 1,
});
return {
register,

View File

@ -1,7 +1,7 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
actuatorList = '/actuator/httptrace-new/',
actuatorList = '/actuator/jeecghttptrace/',
}
/**

View File

@ -3,11 +3,8 @@
<template #headerContent>
<div class="flex justify-between items-center">
<span class="flex-1">
<a :href="GITHUB_URL" target="_blank">
<!-- {{ name }}-->
JeecgBoot
</a>
是一款基于BPM的低代码开发平台前后端分离架构 SpringBoot 2.xSpringCloudAnt Design&VueMybatis-plusShiroJWT支持微服务强大的代码生成器让前后端代码一键生成实现低代码开发 JeecgBoot引领新低代码开发模式 OnlineCoding-> 代码生成器-> 手工MERGE 帮助Java项目解决70%的重复工作让开发更多关注业务既能快速提高效率节省研发成本同时又不失灵活性一系列低代码能力Online表单Online报表Online图表表单设计流程设计报表设计大屏设计 等等...
<a :href="GITHUB_URL" target="_blank">{{ name }}</a>
是一款基于BPM的低代码平台前后端分离架构 SpringBoot 2.xSpringCloudAnt Design&VueMybatis-plusShiroJWT支持微服务强大的代码生成器让前后端代码一键生成实现低代码开发 JeecgBoot引领新低代码开发模式 OnlineCoding-> 代码生成器-> 手工MERGE 帮助Java项目解决70%的重复工作让开发更多关注业务既能快速提高效率节省研发成本同时又不失灵活性一系列低代码能力Online表单Online报表Online图表表单设计流程设计报表设计大屏设计 等等...
</span>
</div>
</template>

View File

@ -4,14 +4,17 @@
<script setup lang="ts">
import { ref } from 'vue';
import { isOAuth2AppEnv, sysOAuth2Login } from '/@/views/sys/login/useLogin';
import {isOAuth2AppEnv, sysOAuth2Callback, sysOAuth2Login} from '/@/views/sys/login/useLogin';
import { useRouter } from 'vue-router';
import { PageEnum } from '/@/enums/pageEnum';
import { router } from '/@/router';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from '/@/hooks/web/useI18n';
import {getTenantId} from "/@/utils/auth";
import { getAuthCache, getTenantId, getToken } from "/@/utils/auth";
import { OAUTH2_THIRD_LOGIN_TENANT_ID } from "@/enums/cacheEnum";
import { defHttp } from "@/utils/http/axios";
import { requestAuthCode } from "dingtalk-jsapi";
const isOAuth = ref<boolean>(isOAuth2AppEnv());
const env = ref<any>({ thirdApp: false, wxWork: false, dingtalk: false });
@ -55,7 +58,8 @@
} else if (env.value.wxWork) {
sysOAuth2Login('wechat_enterprise');
} else if (env.value.dingtalk) {
sysOAuth2Login('dingtalk');
//新版钉钉登录
dingdingLogin();
}
}
}
@ -84,4 +88,43 @@
}
});
}
/**
* 钉钉登录
*/
function dingdingLogin() {
//先获取钉钉的企业id如果没有配置 还是走原来的逻辑,走原来的逻辑 需要判断存不存在token存在token直接去首页
let tenantId = getAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID) || 0;
let url = `/sys/thirdLogin/get/corpId/clientId?tenantId=${tenantId}`;
//update-begin---author:wangshuai---date:2024-12-09---for:不要使用getAction online里面的要用defHttp---
defHttp.get({ url:url },{ isTransformResponse: false }).then((res) => {
//update-end---author:wangshuai---date:2024-12-09---for:不要使用getAction online里面的要用defHttp---
if (res.success) {
if(res.result && res.result.corpId && res.result.clientId){
requestAuthCode({ corpId: res.result.corpId, clientId: res.result.clientId }).then((res) => {
let { code } = res;
sysOAuth2Callback(code);
});
}else{
toOldAuthLogin();
}
} else {
toOldAuthLogin();
}
}).catch((err) => {
toOldAuthLogin();
});
}
/**
* 旧版钉钉登录
*/
function toOldAuthLogin() {
let token = getToken();
if (token) {
router.replace({ path: PageEnum.BASE_HOME });
} else {
sysOAuth2Login('dingtalk');
}
}
</script>

View File

@ -174,6 +174,13 @@ export function isOAuth2AppEnv() {
return /wxwork|dingtalk/i.test(navigator.userAgent);
}
/**
* 判断是否是钉钉环境
*/
export function isOAuth2DingAppEnv() {
return /dingtalk/i.test(navigator.userAgent);
}
/**
* 后台构造oauth2登录地址
* @param source
@ -191,3 +198,19 @@ export function sysOAuth2Login(source) {
window.location.href = url;
}
//update-end---author:wangshuai ---date:20220629 for[issues/I5BG1I]vue3不支持auth2登录------------
//update-begin---author:wangshuai ---date:20241108 for[QQYUN-9421]vue3新版auth登录用户不用点击登录------------
/**
* 后台callBack
* @param code
*/
export function sysOAuth2Callback(code:string) {
let url = `${window._CONFIG['domianURL']}/sys/thirdLogin/oauth2/dingding/login`;
url += `?state=${encodeURIComponent(window.location.origin)}&authCode=${code}`;
let tenantId = getAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID);
if(tenantId){
url += `&tenantId=${tenantId}`;
}
window.location.href = url;
}
//update-end---author:wangshuai ---date:20241108 for[QQYUN-9421]vue3新版auth登录用户不用点击登录------------

View File

@ -15,6 +15,15 @@ export const thirdAppFormSchema: FormSchema[] = [
component: 'Input',
show: false,
},
{
label: 'CorpId',
field: 'corpId',
component: 'Input',
ifShow: ({ values }) => {
return values.thirdType === 'dingtalk';
},
required: true,
},
{
label: 'Agentld',
field: 'agentId',
@ -32,12 +41,6 @@ export const thirdAppFormSchema: FormSchema[] = [
field: 'clientSecret',
component: 'Input',
required: true,
},
{
label: 'agentAppSecret',
field: 'agentAppSecret',
component: 'Input',
ifShow: false,
},{
label: '启用',
field: 'status',

View File

@ -21,6 +21,12 @@
</div>
</template>
<div class="base-desc">完成步骤1后填入Agentld AppKeyAppSecret后 可对接应用与同步通讯录</div>
<div class="flex-flow">
<div class="base-title">CorpId</div>
<div class="base-message">
<a-input-password v-model:value="appConfigData.corpId" readonly />
</div>
</div>
<div class="flex-flow">
<div class="base-title">Agentld</div>
<div class="base-message">

View File

@ -82,7 +82,6 @@
agentId: '',
clientId: '',
clientSecret: '',
agentAppSecret: '',
});
//企业微信钉钉配置modal
const [registerAppConfigModal, { openModal }] = useModal();

View File

@ -10,6 +10,9 @@
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from './fill.rule.data';
import { saveFillRule, updateFillRule } from './fill.rule.api';
import {useMessage} from "@/hooks/web/useMessage";
const { createMessage: $message } = useMessage();
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
@ -43,6 +46,20 @@
async function handleSubmit() {
try {
let formValue = await validate();
// 检查参数是否合法
let ruleParams = formValue.ruleParams;
if (!!ruleParams) {
ruleParams = JSON.parse(ruleParams);
for (const key of Object.keys(ruleParams)) {
// online 保留字检查
if (key === 'onl_watch') {
$message.error('参数名称不能是onl_watch');
return
}
}
}
setModalProps({ confirmLoading: true });
if (isUpdate.value) {
let allFieldsValue = getFieldsValue();

View File

@ -4,14 +4,17 @@
<script setup lang="ts">
import { ref } from 'vue';
import { isOAuth2AppEnv, sysOAuth2Login } from '/@/views/sys/login/useLogin';
import { isOAuth2AppEnv, sysOAuth2Callback, sysOAuth2Login } from '/@/views/sys/login/useLogin';
import { useRouter } from 'vue-router';
import { PageEnum } from '/@/enums/pageEnum';
import { router } from '/@/router';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from '/@/hooks/web/useI18n';
import {getTenantId} from "/@/utils/auth";
import { getAuthCache, getTenantId, getToken } from "/@/utils/auth";
import { requestAuthCode } from 'dingtalk-jsapi';
import { defHttp } from '/@/utils/http/axios';
import { OAUTH2_THIRD_LOGIN_TENANT_ID } from "/@/enums/cacheEnum";
const isOAuth = ref<boolean>(isOAuth2AppEnv());
const env = ref<any>({ thirdApp: false, wxWork: false, dingtalk: false });
@ -55,7 +58,8 @@
} else if (env.value.wxWork) {
sysOAuth2Login('wechat_enterprise');
} else if (env.value.dingtalk) {
sysOAuth2Login('dingtalk');
//新版钉钉登录
dingdingLogin();
}
}
}
@ -84,4 +88,43 @@
}
});
}
/**
* 钉钉登录
*/
function dingdingLogin() {
//先获取钉钉的企业id如果没有配置 还是走原来的逻辑,走原来的逻辑 需要判断存不存在token存在token直接去首页
let tenantId = getAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID) || 0;
let url = `/sys/thirdLogin/get/corpId/clientId?tenantId=${tenantId}`;
//update-begin---author:wangshuai---date:2024-12-09---for:不要使用getAction online里面的要用defHttp---
defHttp.get({ url:url },{ isTransformResponse: false }).then((res) => {
//update-end---author:wangshuai---date:2024-12-09---for:不要使用getAction online里面的要用defHttp---
if (res.success) {
if(res.result && res.result.corpId && res.result.clientId){
requestAuthCode({ corpId: res.result.corpId, clientId: res.result.clientId }).then((res) => {
let { code } = res;
sysOAuth2Callback(code);
});
}else{
toOldAuthLogin();
}
} else {
toOldAuthLogin();
}
}).catch((err) => {
toOldAuthLogin();
});
}
/**
* 旧版钉钉登录
*/
function toOldAuthLogin() {
let token = getToken();
if (token) {
router.replace({ path: PageEnum.BASE_HOME });
} else {
sysOAuth2Login('dingtalk');
}
}
</script>

View File

@ -160,6 +160,11 @@ export interface GlobConfig {
shortName: string;
// 短标题
shortTitle: string;
// 【JEECG作为乾坤子应用】是否以乾坤子应用模式启动
isQiankunMicro: boolean;
// 【JEECG作为乾坤子应用】乾坤子应用入口
qiankunMicroAppEntry?: string;
}
export interface GlobEnvConfig {
// Site title
@ -182,4 +187,9 @@ export interface GlobEnvConfig {
VITE_GLOB_UPLOAD_URL?: string;
// view url
VITE_GLOB_ONLINE_VIEW_URL?: string;
// 【JEECG作为乾坤子应用】填写后将作为乾坤子应用启动主应用注册时AppName需保持一致
VITE_GLOB_QIANKUN_MICRO_APP_NAME?: string;
// 【JEECG作为乾坤子应用】作为乾坤子应用启动时必填需与qiankun主应用注册子应用时填写的 entry 保持一致
VITE_GLOB_QIANKUN_MICRO_APP_ENTRY?: string;
}

View File

@ -59,6 +59,10 @@ declare global {
VITE_USE_CDN: boolean;
VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none';
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean;
// 【JEECG作为乾坤子应用】乾坤子应用名主应用注册时AppName需保持一致
VITE_GLOB_QIANKUN_MICRO_APP_NAME?: string;
// 【JEECG作为乾坤子应用】非必填需与qiankun主应用注册子应用时填写的 entry 保持一致
VITE_GLOB_QIANKUN_MICRO_APP_ENTRY?: string;
}
declare function parseInt(s: string | number, radix?: number): number;

10
jeecgboot-vue3/types/main.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
// 应用参数
export type MainAppProps = {
container?: HTMLElement;
// 隐藏侧边栏(菜单)
hideSider?: boolean;
// 隐藏顶部
hideHeader?: boolean;
// 隐藏 多Tab 切换
hideMultiTabs?: boolean;
}

View File

@ -31,8 +31,19 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
const isBuild = command === 'build';
const serverOptions: Recordable = {}
// ----- [begin] 【JEECG作为乾坤子应用】 -----
const {VITE_GLOB_QIANKUN_MICRO_APP_NAME, VITE_GLOB_QIANKUN_MICRO_APP_ENTRY} = viteEnv;
const isQiankunMicro = VITE_GLOB_QIANKUN_MICRO_APP_NAME != null && VITE_GLOB_QIANKUN_MICRO_APP_NAME !== '';
if (isQiankunMicro && !isBuild) {
serverOptions.cors = true;
serverOptions.origin = VITE_GLOB_QIANKUN_MICRO_APP_ENTRY!.split('/').slice(0, 3).join('/');
}
// ----- [end] 【JEECG作为乾坤子应用】 -----
return {
base: VITE_PUBLIC_PATH,
base: isQiankunMicro ? VITE_GLOB_QIANKUN_MICRO_APP_ENTRY : VITE_PUBLIC_PATH,
root,
resolve: {
alias: [
@ -64,10 +75,13 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
server: {
// Listening on all local IPs
host: true,
// @ts-ignore
https: false,
port: VITE_PORT,
// Load proxy configuration from .env
proxy: createProxy(VITE_PROXY),
// 合并 server 配置
...serverOptions,
},
build: {
minify: 'esbuild',
@ -116,8 +130,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
},
// The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
plugins: createVitePlugins(viteEnv, isBuild),
// 预加载构建配置(首屏性能)
plugins: createVitePlugins(viteEnv, isBuild, isQiankunMicro),
optimizeDeps: {
esbuildOptions: {
target: 'es2020',