JeecgBoot 3.6.0大版本发布

pull/824/head
zhangdaiscott 2023-10-18 14:55:25 +08:00
parent abd4b7d102
commit 8ff3f4db25
192 changed files with 8983 additions and 5898 deletions

1
.gitignore vendored
View File

@ -29,6 +29,7 @@ pnpm-debug.log*
*.sln
*.sw?
/os_del.cmd
os_del.cmd
/.vscode/
/.history/
/svn clear.bat

View File

@ -1,11 +1,11 @@
JEECG BOOT 低代码开发平台Vue3前端
===============
当前最新版本: 3.5.5发布时间2023-09-22
当前最新版本: 3.6.0发布时间2023-10-23
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://jeecg.com/aboutusIndex)
[![](https://img.shields.io/badge/Blog-官方博客-blue.svg)](https://jeecg.blog.csdn.net)
[![](https://img.shields.io/badge/version-3.5.5-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-3.6.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)

View File

@ -20,7 +20,7 @@ import { configSvgIconsPlugin } from './svgSprite';
// import PkgConfig from 'vite-plugin-package-config';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
const {VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
const vitePlugins: (PluginOption | PluginOption[])[] = [
// have to

View File

@ -1,6 +1,6 @@
{
"name": "jeecgboot-vue3",
"version": "3.5.5",
"version": "3.6.0",
"author": {
"name": "jeecg",
"email": "jeecgos@163.com",
@ -28,8 +28,8 @@
"@ant-design/icons-vue": "^6.1.0",
"@logicflow/core": "^1.2.12",
"@logicflow/extension": "^1.2.13",
"@vue/shared": "^3.3.4",
"@vue/runtime-core": "^3.3.4",
"@vue/shared": "^3.3.4",
"@vueuse/shared": "^10.4.1",
"@vueuse/core": "^10.4.1",
"@zxcvbn-ts/core": "^3.0.3",

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -85,7 +85,17 @@ export function getUserInfo() {
const userStore = useUserStoreWithOut();
userStore.setToken('');
setAuthCache(TOKEN_KEY, null);
router.push(PageEnum.BASE_LOGIN);
// update-begin-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
router.push({
path: PageEnum.BASE_LOGIN,
query: {
// 传入当前的路由,登录成功后跳转到当前路由
redirect: router.currentRoute.value.fullPath,
}
});
// update-end-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
}
// update-end--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
});
@ -146,9 +156,15 @@ export const passwordChange = (params) => defHttp.get({ url: Api.passwordChange,
* @description:
*/
export function thirdLogin(params, mode: ErrorMessageMode = 'modal') {
//==========begin 第三方登录/auth2登录需要传递租户id===========
let tenantId = "0";
if(!params.tenantId){
tenantId = params.tenantId;
}
//==========end 第三方登录/auth2登录需要传递租户id===========
return defHttp.get<LoginResultModel>(
{
url: `${Api.thirdLogin}/${params.token}/${params.thirdType}`,
url: `${Api.thirdLogin}/${params.token}/${params.thirdType}/${tenantId}`,
},
{
errorMessageMode: mode,

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -62,10 +62,13 @@
padding-left: 7px;
cursor: pointer;
transition: all 0.2s ease;
&.light {
border-bottom: 1px solid @border-color-base;
//
&.jeecg-layout-mix-sider-logo,&.jeecg-layout-menu-logo{
background:@sider-logo-bg-color;
}
// &.light {
// border-bottom: 1px solid @border-color-base;
// }
&.collapsed-show-title {
padding-left: 20px;

View File

@ -12,7 +12,7 @@
import { defineComponent, PropType } from 'vue';
import CountButton from './CountButton.vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { useRuleFormItem } from '/@/hooks/component/useFormItemSingle';
const props = {
value: { type: String },

View File

@ -10,22 +10,26 @@
v-bind="getAttr(item.event)"
@click="handleClickMenu(item)"
:disabled="item.disabled"
:class="[{ 'is-pop-confirm': item.popConfirm }, (item.class ?? [])]"
:class="[{ 'is-pop-confirm': item.popConfirm }, item.class ?? []]"
>
<a-popconfirm v-if="popconfirm && item.popConfirm" v-bind="getPopConfirmAttrs(item.popConfirm)">
<template #icon v-if="item.popConfirm.icon">
<Icon v-if="item.iconColor" :icon="item.popConfirm.icon" :color="item.iconColor"/>
<Icon v-else :icon="item.popConfirm.icon"/>
<Icon v-if="item.iconColor" :icon="item.popConfirm.icon" :color="item.iconColor" />
<Icon v-else :icon="item.popConfirm.icon" />
</template>
<div class="dropdown-event-area">
<Icon :icon="item.icon" v-if="item.icon && item.iconColor" :color="item.iconColor"/>
<Icon :icon="item.icon" v-else-if="item.icon"/>
<Icon :icon="item.icon" v-if="item.icon && item.iconColor" :color="item.iconColor" />
<Icon :icon="item.icon" v-else-if="item.icon" />
<span class="ml-1">{{ item.text }}</span>
</div>
</a-popconfirm>
<!-- 设置动态插槽 -->
<template v-else-if="item.slot">
<slot :name="item.slot" :label="item.text"></slot>
</template>
<template v-else>
<Icon :icon="item.icon" v-if="item.icon && item.iconColor" :color="item.iconColor"/>
<Icon :icon="item.icon" v-else-if="item.icon"/>
<Icon :icon="item.icon" v-if="item.icon && item.iconColor" :color="item.iconColor" />
<Icon :icon="item.icon" v-else-if="item.icon" />
<span class="ml-1">{{ item.text }}</span>
</template>
</a-menu-item>

View File

@ -61,6 +61,7 @@ import JRangeNumber from './jeecg/components/JRangeNumber.vue';
import UserSelect from './jeecg/components/userSelect/index.vue';
import JRangeDate from './jeecg/components/JRangeDate.vue'
import JRangeTime from './jeecg/components/JRangeTime.vue'
import RoleSelectInput from './jeecg/components/roleSelect/RoleSelectInput.vue';
const componentMap = new Map<ComponentType, Component>();
@ -131,6 +132,9 @@ componentMap.set('JRangeNumber', JRangeNumber);
componentMap.set('UserSelect', UserSelect);
componentMap.set('RangeDate', JRangeDate);
componentMap.set('RangeTime', JRangeTime);
componentMap.set('RoleSelect', RoleSelectInput);
export function add(compName: ComponentType, component: Component) {
componentMap.set(compName, component);

View File

@ -206,10 +206,15 @@
}
function renderComponent() {
const { renderComponentContent, component, field, changeEvent = 'change', valueField } = props.schema;
const { renderComponentContent, component, field, changeEvent = 'change', valueField, componentProps } = props.schema;
const isCheck = component && ['Switch', 'Checkbox'].includes(component);
// update-begin--author:liaozhiyang---date:20231013---forQQYUN-6679input
let isTrim = false;
if (component === 'Input' && componentProps && componentProps.trim) {
isTrim = true;
}
// update-end--author:liaozhiyang---date:20231013---forQQYUN-6679input
const eventKey = `on${upperFirst(changeEvent)}`;
// update-begin--author:liaozhiyang---date:20230922---forissues/752dynamicRules 使 trigger: 'blur'
const on = {
@ -219,7 +224,18 @@
propsData[eventKey](...args);
}
const target = e ? e.target : null;
const value = target ? (isCheck ? target.checked : target.value) : e;
// update-begin--author:liaozhiyang---date:20231013---forQQYUN-6679input
let value;
if (target) {
if (isCheck) {
value = target.checked;
} else {
value = isTrim ? target.value.trim() : target.value;
}
} else {
value = e;
}
// update-end--author:liaozhiyang---date:20231013---forQQYUN-6679input
props.setFormModel(field, value);
//props.validateFields([field], { triggerName: 'change' }).catch((_) => {});
},

View File

@ -15,7 +15,7 @@
},
inheritAttrs: false,
props: {
value: propTypes.oneOfType([propTypes.object, propTypes.array]),
value: propTypes.oneOfType([propTypes.object, propTypes.array, propTypes.string]),
//
showArea: propTypes.bool.def(true),
//

View File

@ -152,11 +152,14 @@
<style lang="less" scoped>
.area-select {
width: 100%;
display: flex;
/* update-begin-author:taoyan date:2023-2-18 for: QQYUN-4292【online表单】高级查询 2.省市县样式问题 */
/* display: flex;*/
.ant-select {
width: 33.3%;
width: calc(33.3% - 7px)
}
/* update-end-author:taoyan date:2023-2-18 for: QQYUN-4292【online表单】高级查询 2.省市县样式问题 */
.ant-select:not(:first-child) {
margin-left: 10px;

View File

@ -1,5 +1,5 @@
<template>
<div v-bind="boxBindProps">
<div ref="containerRef" v-bind="boxBindProps">
<!-- 全屏按钮 -->
<a-icon v-if="fullScreen" class="full-screen-icon" :type="fullScreenIcon" @click="onToggleFullScreen" />
<textarea ref="textarea" v-bind="getBindValue"></textarea>
@ -45,6 +45,8 @@
import { useAttrs } from '/@/hooks/core/useAttrs';
import { useDesign } from '/@/hooks/web/useDesign';
import { isJsonObjectString } from '/@/utils/is.ts';
//
import { useCodeHinting } from '../hooks/useCodeHinting';
export default defineComponent({
name: 'JCodeEditor',
@ -61,9 +63,12 @@
zIndex: propTypes.any.def(999),
theme: propTypes.string.def('idea'),
language: propTypes.string.def(''),
//
keywords: propTypes.array.def([]),
},
emits: ['change', 'update:value'],
setup(props, { emit }) {
const containerRef = ref(null);
const { prefixCls } = useDesign('code-editer');
const CodeMirror = window.CodeMirror || _CodeMirror;
const emitData = ref<object>();
@ -121,6 +126,10 @@
}
return _props;
});
// update-begin--author:liaozhiyang---date:20230904---forQQYUN-5955online js
const { codeHintingMount, codeHintingRegistry } = useCodeHinting(CodeMirror, props.keywords, props.language);
codeHintingRegistry();
// update-end--author:liaozhiyang---date:20230904---forQQYUN-5955online js
/**
* 监听组件值
*/
@ -171,6 +180,9 @@
coder.on('change', onChange);
//
setValue(innerValue, false);
// update-begin--author:liaozhiyang---date:20230904---forQQYUN-5955online js
codeHintingMount(coder);
// update-end--author:liaozhiyang---date:20230904---forQQYUN-5955online js
}
//
@ -206,6 +218,7 @@
}
//update-end-author:taoyan date:2022-10-18 for: VUEN-2480bugonline vue3 8online js
return {
state,
textarea,
@ -215,7 +228,8 @@
isFullScreen,
fullScreenIcon,
onToggleFullScreen,
refresh
refresh,
containerRef,
};
},
});
@ -295,4 +309,9 @@
border: 1px solid #ddd;
}
}
.CodeMirror-hints.idea {
z-index: 1001;
max-width: 600px;
max-height: 300px;
}
</style>

View File

@ -11,7 +11,7 @@
import { propTypes } from '/@/utils/propTypes';
const props = defineProps({
value: propTypes.oneOfType([propTypes.string, propTypes.array]),
value: propTypes.oneOfType([propTypes.string, propTypes.number, propTypes.array]),
length: propTypes.number.def(25),
});
//

View File

@ -37,7 +37,7 @@
import { propTypes } from '/@/utils/propTypes';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { useMessage } from '/@/hooks/web/useMessage';
import { getFileAccessHttpUrl, getRandom } from '/@/utils/common/compUtils';
import { getFileAccessHttpUrl, getHeaders, getRandom } from '/@/utils/common/compUtils';
import { uploadUrl } from '/@/api/common/api';
import { getToken } from '/@/utils/auth';
@ -94,9 +94,7 @@
return path.substring(path.lastIndexOf('/') + 1);
};
//token
const headers = ref<object>({
'X-Access-Token': getToken(),
});
const headers = getHeaders();
//
const loading = ref<boolean>(false);
//

View File

@ -125,7 +125,9 @@
//
props.setFieldsValue && props.setFieldsValue(values);
// update-begin--author:liaozhiyang---date:20230831---forissues/5288popup
// update-begin--author:liaozhiyang---date:20230811---forissues/5213JPopupchange
emit('popUpChange', values);
// update-end--author:liaozhiyang---date:20230811---forissues/5213JPopupchange
// update-begin--author:liaozhiyang---date:20230831---forissues/5288popup
}

View File

@ -116,7 +116,7 @@
watch(
() => props.dictOptions,
(val) => {
if (val && val.length > 0) {
if (val && val.length >= 0) {
options.value = [...val];
}
},

View File

@ -2,7 +2,7 @@
<template>
<div>
<JSelectBiz @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs" @change="handleChange"/>
<DeptSelectModal @register="regModal" @getSelectResult="setValue" v-bind="getBindValue" />
<DeptSelectModal @register="regModal" @getSelectResult="setValue" v-bind="getBindValue" :multiple="multiple" />
</div>
</template>
<script lang="ts">

View File

@ -30,7 +30,7 @@
},
rowKey: {
type: String,
default: 'code',
default: 'id',
},
params: {
type: Object,

View File

@ -2,7 +2,7 @@
<template>
<div>
<JSelectBiz @change="handleChange" @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs"></JSelectBiz>
<UserSelectModal :rowKey="rowKey" @register="regModal" @getSelectResult="setValue" v-bind="getBindValue"></UserSelectModal>
<UserSelectModal :rowKey="rowKey" @register="regModal" @getSelectResult="setValue" v-bind="getBindValue" :excludeUserIdList="excludeUserIdList"></UserSelectModal>
</div>
</template>
<script lang="ts">
@ -37,6 +37,13 @@
type: Object,
default: () => {},
},
//update-begin---author:wangshuai ---date:20230703 forQQYUN-56855------------
//id
excludeUserIdList:{
type: Array,
default: () => [],
}
//update-end---author:wangshuai ---date:20230703 forQQYUN-56855------------
},
emits: ['options-change', 'change', 'update:value'],
setup(props, { emit }) {
@ -84,6 +91,17 @@
}
});
//update-begin---author:wangshuai ---date:20230703 forQQYUN-56855------------
const excludeUserIdList = ref<any>([]);
/**
* 需要监听一下excludeUserIdList否则modal获取不到
*/
watch(()=>props.excludeUserIdList,(data)=>{
excludeUserIdList.value = data;
},{ immediate: true })
//update-end---author:wangshuai ---date:20230703 forQQYUN-56855------------
/**
* 打卡弹出框
*/
@ -141,6 +159,7 @@
regModal,
setValue,
handleOpen,
excludeUserIdList,
handleChange,
};
},

View File

@ -17,7 +17,7 @@
<div class="ant-upload-text">{{ text }}</div>
</div>
</template>
<a-button v-else-if="buttonVisible" :disabled="isMaxCount || disabled">
<a-button v-else-if="buttonVisible" :disabled="buttonDisabled">
<Icon icon="ant-design:upload-outlined" />
<span>{{ text }}</span>
</a-button>
@ -36,7 +36,7 @@
import { useAttrs } from '/@/hooks/core/useAttrs';
import { useDesign } from '/@/hooks/web/useDesign';
import { UploadTypeEnum } from './upload.data';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { getFileAccessHttpUrl, getHeaders } from '/@/utils/common/compUtils';
import UploadItemActions from './components/UploadItemActions.vue';
const { createMessage, createConfirm } = useMessage();
@ -67,11 +67,11 @@
removeConfirm: propTypes.bool.def(false),
beforeUpload: propTypes.func,
disabled: propTypes.bool.def(false),
//
replaceLastOne: propTypes.bool.def(false),
});
const headers = reactive({
'X-Access-Token': getToken(),
});
const headers = getHeaders();
const fileList = ref<any[]>([]);
const uploadGoOn = ref<boolean>(true);
// refs
@ -80,6 +80,20 @@
const isMaxCount = computed(() => props.maxCount > 0 && fileList.value.length >= props.maxCount);
//
const isImageMode = computed(() => props.fileType === UploadTypeEnum.image);
//
const buttonDisabled = computed(()=>{
if(props.disabled === true){
return true;
}
if(isMaxCount.value === true){
if(props.replaceLastOne === true){
return false
}else{
return true;
}
}
return false
});
// props attrs
const bindProps = computed(() => {
//update-begin-author:liusq date:20220411 for: [issue/455]accept
@ -311,7 +325,9 @@
return;
}
}
emitValue(newFileList);
//update-begin---author:liusq ---date:20230914 for[issues/5327]UploadreturnUrlfalse'[object Object]' ------------
emitValue(JSON.stringify(newFileList));
//update-end---author:liusq ---date:20230914 for[issues/5327]UploadreturnUrlfalse'[object Object]' ------------
}
}
}

View File

@ -11,6 +11,7 @@
@check="onCheck"
:fieldNames="fieldNames"
:checkedKeys="checkedKeys"
:multiple="multiple"
:checkStrictly="getCheckStrictly"
/>
<!--树操作部分-->
@ -39,6 +40,7 @@
import { BasicTree, TreeActionType } from '/@/components/Tree';
import { useTreeBiz } from '/@/components/Form/src/jeecg/hooks/useTreeBiz';
import {propTypes} from "/@/utils/propTypes";
import { omit } from 'lodash-es';
export default defineComponent({
name: 'DeptSelectModal',
@ -65,13 +67,14 @@
//update-begin-author:taoyan date:2022-10-28 for:
let propValue = props.value === ''?[]:props.value;
//update-begin-author:liusq date:2023-05-26 for: [issues/538]JSelectDept dynamicDisabled
const getBindValue = Object.assign({}, unref(props), unref(attrs), {value: propValue},{disabled: false});
let temp = Object.assign({}, unref(props), unref(attrs), {value: propValue},{disabled: false});
const getBindValue = omit(temp, 'multiple');
//update-end-author:liusq date:2023-05-26 for: [issues/538]JSelectDept dynamicDisabled
//update-end-author:taoyan date:2022-10-28 for:
const queryUrl = getQueryUrl();
const [{ visibleChange, checkedKeys, getCheckStrictly, getSelectTreeData, onCheck, onLoadData, treeData, checkALL, expandAll, onSelect }] =
useTreeBiz(treeRef, queryUrl, getBindValue);
useTreeBiz(treeRef, queryUrl, getBindValue, props);
const searchInfo = ref(props.params);
const tree = ref([]);
//treeNodekeytreeData

View File

@ -78,7 +78,8 @@
canResize: false,
bordered: true,
size: 'small',
rowKey: 'code',
//rowKey,
rowKey: props.rowKey,
};
const getBindValue = Object.assign({}, unref(props), unref(attrs), config);
const [{ rowSelection, visibleChange, indexColumnProps, getSelectResult, handleDeleteSelected, selectRows }] = useSelectBiz(
@ -176,6 +177,7 @@
selectedTable,
selectRows,
handleDeleteSelected,
searchInfo,
};
},
});

View File

@ -5,7 +5,7 @@
v-bind="$attrs"
@register="register"
:title="modalTitle"
width="900px"
:width="showSelected ? '1200px' : '900px'"
wrapClassName="j-user-select-modal"
@ok="handleOk"
destroyOnClose
@ -24,6 +24,7 @@
:searchInfo="searchInfo"
:rowSelection="rowSelection"
:indexColumnProps="indexColumnProps"
:afterFetch="afterFetch"
>
<!-- update-begin-author:taoyan date:2022-5-25 for: VUEN-1112一对多 用户选择 未显示选择条数及清空 -->
<template #tableTitle></template>
@ -48,7 +49,7 @@
</div>
</template>
<script lang="ts">
import { defineComponent, unref, ref } from 'vue';
import { defineComponent, unref, ref, watch } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { getUserList } from '/@/api/common/api';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
@ -72,6 +73,13 @@
type: String,
default: '选择用户',
},
//update-begin---author:wangshuai ---date:20230703 forQQYUN-56855------------
//id
excludeUserIdList: {
type: Array,
default: [],
},
//update-end---author:wangshuai ---date:20230703 forQQYUN-56855------------
},
emits: ['register', 'getSelectResult'],
setup(props, { emit, refs }) {
@ -107,6 +115,15 @@
getBindValue
);
const searchInfo = ref(props.params);
// update-begin--author:liaozhiyang---date:20230811---forissues/657
watch(rowSelection.selectedRowKeys, (newVal) => {
//update-begin---author:wangshuai ---date: 20230829 fornull------------
if(tableRef.value){
tableRef.value.setSelectedRowKeys(newVal);
}
//update-end---author:wangshuai ---date: 20230829 fornull------------
});
// update-end--author:liaozhiyang---date:20230811---forissues/657
//form
const formConfig = {
baseColProps: {
@ -210,6 +227,29 @@
});
}
//update-begin---author:wangshuai ---date:20230703 forQQYUN-56855------------
/**
* 用户返回结果逻辑查询
*/
function afterFetch(record) {
let excludeList = props.excludeUserIdList;
if(!excludeList){
return record;
}
let arr:any[] = [];
//id
if(excludeList.length>0 && record && record.length>0){
for(let item of record){
if(excludeList.indexOf(item.id)<0){
arr.push({...item})
}
}
return arr;
}
return record;
}
//update-end---author:wangshuai ---date:20230703 forQQYUN-56855------------
return {
//config,
handleOk,
@ -227,6 +267,7 @@
handleDeleteSelected,
tableScroll,
tableRef,
afterFetch,
};
},
});

View File

@ -0,0 +1,232 @@
<template>
<div>
<div @click="showModal" :class="disabled ? 'select-input disabled-select' : 'select-input'">
<template v-if="selectedList.length > 0">
<template v-for="(item, index) in selectedList">
<SelectedUserItem v-if="index < maxCount" :info="item" @unSelect="unSelect" query />
</template>
</template>
<span v-else style="height: 30px; line-height: 30px; display: inline-block; margin-left: 7px; color: #bfbfbf">请选择</span>
<div v-if="ellipsisInfo.status" class="user-selected-item">
<div class="user-select-ellipsis">
<span style="color: red">+{{ ellipsisInfo.count }}...</span>
</div>
</div>
</div>
<RoleSelectModal :appId="currentAppId" :multi="multi" :getContainer="getContainer" title="选择组织角色" @register="registerRoleModal" @selected="onSelected" />
</div>
</template>
<script lang="ts">
import { useModal } from '/@/components/Modal';
import { defHttp } from '/@/utils/http/axios';
import { computed, ref, watch, watchEffect, defineComponent } from 'vue';
import RoleSelectModal from './RoleSelectModal.vue';
import SelectedUserItem from '../userSelect/SelectedUserItem.vue';
import { Form } from 'ant-design-vue';
import { useUserStore } from '/@/store/modules/user';
const maxCount = 2;
export default defineComponent({
name: 'RoleSelectInput',
components: {
RoleSelectModal,
SelectedUserItem,
},
props: {
disabled: {
type: Boolean,
default: false,
},
store: {
type: String,
default: 'id',
},
value: {
type: String,
default: '',
},
multi: {
type: Boolean,
default: false,
},
getContainer: {
type: Function,
default: null,
},
appId: {
type: String,
default: '',
},
},
emits: ['update:value', 'change'],
setup(props, { emit }) {
const formItemContext = Form.useInjectFormItemContext();
const selectedList = ref<any[]>([]);
const loading = ref(true);
const [registerRoleModal, { openModal: openRoleModal, closeModal: closeRoleModal }] = useModal();
function showModal(e) {
e.preventDefault();
e.stopPropagation();
let list = selectedList.value.map((item) => item.id);
openRoleModal(true, {
list,
});
}
const ellipsisInfo = computed(() => {
let max = maxCount;
let len = selectedList.value.length;
if (len > max) {
return { status: true, count: len - max };
} else {
return { status: false };
}
});
function unSelect(id) {
console.log('unSelectUser', id);
loading.value = false;
let arr = selectedList.value;
let index = -1;
for (let i = 0; i < arr.length; i++) {
if (arr[i].id == id) {
index = i;
break;
}
}
if (index >= 0) {
arr.splice(index, 1);
selectedList.value = arr;
onSelectedChange();
}
}
function onSelectedChange() {
let temp: any[] = [];
let arr = selectedList.value;
if (arr && arr.length > 0) {
temp = arr.map((k) => {
return k[props.store];
});
}
let str = temp.join(',');
emit('update:value', str);
emit('change', str);
formItemContext.onFieldChange();
console.log('选中数据', str);
}
function onSelected(_v, values) {
console.log('角色选择完毕:', values);
loading.value = false;
if (values && values.length > 0) {
selectedList.value = values;
} else {
selectedList.value = [];
}
onSelectedChange();
closeRoleModal();
}
//
const currentAppId = ref('');
const userStore = useUserStore();
watchEffect(() => {
let tenantId = userStore.getTenant;
let appId = props.appId;
if (appId) {
currentAppId.value = appId;
} else {
currentAppId.value = new Date().getTime() + '-' + tenantId;
}
});
watch(
() => props.value,
async (val) => {
if (val) {
if (loading.value === true) {
await getRoleList(val);
}
} else {
selectedList.value = [];
}
loading.value = true;
},
{ immediate: true }
);
/**
* 获取角色列表
* @param ids
*/
async function getRoleList(ids) {
const url = '/sys/role/listByTenant';
let params = {
[props.store]: ids,
pageSize: 200
};
// roleCode
if (props.store === 'code') {
params.roleCode = ids;
}
selectedList.value = [];
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
console.log('getRoleList>>', data);
if (data.success) {
const { records } = data.result;
let arr: any[] = [];
if (records && records.length > 0) {
for (let item of records) {
arr.push({
id: item.id,
name: item.name || item.roleName,
code: item.roleCode,
checked: true,
selectType: 'sys_role',
});
}
}
selectedList.value = arr;
} else {
console.error(data.message);
}
}
return {
selectedList,
ellipsisInfo,
maxCount,
registerRoleModal,
closeRoleModal,
showModal,
onSelected,
unSelect,
currentAppId,
};
},
});
</script>
<style scoped lang="less">
.select-input {
padding: 0 5px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 3px;
box-sizing: border-box;
display: flex;
color: #9e9e9e;
font-size: 14px;
flex-wrap: nowrap;
min-height: 32px;
overflow-x: hidden;
&.disabled-select {
cursor: not-allowed;
background-color: #f5f5f5 !important;
}
}
</style>

View File

@ -99,19 +99,25 @@
//
const [register] = useModalInner(() => {
const [register] = useModalInner((data) => {
let list = dataList.value;
if(!list || list.length ==0 ){
}
for(let item of list){
item.checked = false
}else{
let selectedIdList = data.list || [];
for(let item of list){
if(selectedIdList.indexOf(item.id)>=0){
item.checked = true;
}else{
item.checked = false;
}
}
}
});
//
function handleOk() {
let arr = toRaw(selectedIdList.value);
emit('selected', arr);
emit('selected', arr, toRaw(selectedList.value));
}
const dataList = ref<any[]>([]);
@ -161,6 +167,7 @@
arr.push({
id: item.id,
name: item.name || item.roleName,
code: item.roleCode,
selectType: props.type,
checked: false
})
@ -177,6 +184,13 @@
function onSelect(e, item) {
prevent(e);
console.log('onselect');
// false
if(props.multi === false){
let list = dataList.value;
for(let item of list){
item.checked = false;
}
}
item.checked = !item.checked;
}

View File

@ -14,6 +14,12 @@
<span style="width: 24px; height: 24px; line-height: 20px; margin-right: 3px; display: inline-block">
<a-avatar v-if="info.avatar" :src="getFileAccessHttpUrl(info.avatar)" :size="24"></a-avatar>
<a-avatar v-else-if="info.avatarIcon" class="ant-btn-primary" :size="24" >
<template #icon>
<Icon :icon=" 'ant-design:'+info.avatarIcon " style="font-size: 16px;margin-top: 4px"/>
</template>
</a-avatar>
<a-avatar v-else-if="info.selectType == 'sys_role'" :size="24" style="background-color: rgb(255, 173, 0);">
<template #icon>
<team-outlined style="font-size: 16px"/>

View File

@ -8,6 +8,11 @@
</div>
<div>
<a-avatar v-if="item.avatar" :src="getFileAccessHttpUrl(item.avatar)"></a-avatar>
<a-avatar v-else-if="item.avatarIcon" class="ant-btn-primary">
<template #icon>
<Icon :icon=" 'ant-design:'+item.avatarIcon " style="margin-top: 4px;font-size: 24px;"/>
</template>
</a-avatar>
<a-avatar v-else>
<template #icon><UserOutlined /></template>
</a-avatar>
@ -15,9 +20,10 @@
<div :style="nameStyle">
{{ item.realname }}
</div>
<div :style="departStyle">
<div :style="departStyle" class="ellipsis" :title="item.orgCodeTxt">
{{ item.orgCodeTxt }}
</div>
<div style="width: 1px"></div>
</div>
</a-list-item>
</template>
@ -166,10 +172,16 @@
<style lang="less">
.user-select-user-info {
display: flex;
width: 100%;
> div {
height: 36px;
line-height: 36px;
margin-right: 10px;
}
.ellipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
</style>

View File

@ -12,6 +12,9 @@
v-model:expandedKeys="expandedKeys"
@select="onSelect"
>
<template #title="{ title, key }">
<FolderFilled style="color: #9e9e9e"/><span style="margin-left: 5px">{{ title }}</span>
</template>
</a-tree>
</div>
</a-col>
@ -27,11 +30,13 @@
import { defHttp } from '/@/utils/http/axios';
import { computed, ref, watch } from 'vue';
import UserList from './UserList.vue';
import { FolderFilled } from '@ant-design/icons-vue';
export default {
name: 'DepartUserList',
components: {
UserList,
FolderFilled
},
props: {
searchText: {

View File

@ -2,7 +2,11 @@
<a-row>
<a-col :span="12">
<div :style="containerStyle">
<a-tree v-if="treeData.length > 0" showIcon :treeData="treeData" :selectedKeys="selectedKeys" @select="onSelect"> </a-tree>
<a-tree v-if="treeData.length > 0" showIcon :treeData="treeData" :selectedKeys="selectedKeys" @select="onSelect">
<template #title="{ title, key }">
<UserOutlined style="color: #9e9e9e"/><span style="margin-left: 5px">{{ title }}</span>
</template>
</a-tree>
</div>
</a-col>
<a-col :span="12" style="padding-left: 10px">
@ -17,11 +21,13 @@
import { computed, ref, watch } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import UserList from './UserList.vue';
import { UserOutlined } from '@ant-design/icons-vue';
export default {
name: 'RoleUserList',
components: {
UserList,
UserOutlined
},
props: {
searchText: {

View File

@ -92,8 +92,10 @@
const APagination = Pagination;
import { defHttp } from '/@/utils/http/axios';
import { computed, ref, toRaw } from 'vue';
import {computed, ref, toRaw, unref} from 'vue';
import { useUserStore } from '/@/store/modules/user';
import { mySelfData } from './useUserSelect'
export default {
name: 'UserSelectModal',
components: {
@ -120,6 +122,11 @@
type: Boolean,
default: false,
},
//
inSuperQuery:{
type: Boolean,
default: false,
}
},
emits: ['selected', 'register'],
setup(props, { emit }) {
@ -154,6 +161,8 @@
if (props.izExcludeMy) {
excludeUserIdList.value.push(userStore.getUserInfo.id);
}
//
loadUserList();
});
//
@ -215,14 +224,26 @@
}
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
if (data.success) {
const { records, total } = data.result;
let { records, total } = data.result;
//id0
if(unref(excludeUserIdList) && unref(excludeUserIdList).length>0){
total = total - unref(excludeUserIdList).length;
}
totalRecord.value = total;
initCurrentUserData(records);
userDataList.value = records;
} else {
console.error(data.message);
}
console.log('loadUserList', data);
}
//
function initCurrentUserData(records) {
if(pageNo.value==1 && props.inSuperQuery === true){
records.unshift({...mySelfData})
}
}
/*--------------加载数据---------------*/
/*--------------选中/取消选中---------------*/

View File

@ -19,7 +19,7 @@
<a-button v-if="showAddButton" shape="circle" @click="onShowModal"><PlusOutlined /></a-button>
</div>
<user-select-modal :multi="multi" :getContainer="getContainer" @register="registerModal" @selected="onSelected" :izExcludeMy="izExcludeMy"></user-select-modal>
<user-select-modal :inSuperQuery="inSuperQuery" :multi="multi" :getContainer="getContainer" @register="registerModal" @selected="onSelected" :izExcludeMy="izExcludeMy"></user-select-modal>
</div>
</template>
@ -31,6 +31,7 @@
import UserSelectModal from './UserSelectModal.vue';
import { defHttp } from '/@/utils/http/axios';
import SelectedUserItem from './SelectedUserItem.vue';
import { mySelfExpress, mySelfData } from './useUserSelect'
export default defineComponent({
name: 'UserSelect',
@ -74,6 +75,11 @@
izExcludeMy:{
type: Boolean,
default: false,
},
//
inSuperQuery:{
type: Boolean,
default: false,
}
},
emits: ['update:value', 'change'],
@ -150,19 +156,41 @@
);
async function getUserList(ids) {
const url = '/sys/user/list';
let params = {
[props.store]: ids,
};
let hasUserExpress = false;
let paramIds = ids;
let idList = [];
selectedUserList.value = [];
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
if (data.success) {
const { records } = data.result;
selectedUserList.value = records;
} else {
console.error(data.message);
if(ids){
// update-begin-author:sunjianlei date:20230330 for:
let tempArray = ids.split(',').map(s => s.trim()).filter(s => s != '');
if (tempArray.includes(mySelfExpress)) {
hasUserExpress = true;
idList = tempArray.filter(item => item != mySelfExpress);
} else {
idList = tempArray;
}
// update-end-author:sunjianlei date:20230330 for:
}
if(idList.length>0){
paramIds = idList.join(',')
const url = '/sys/user/list';
let params = {
[props.store]: paramIds,
};
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
console.log('getUserList', data);
if (data.success) {
const { records } = data.result;
selectedUserList.value = records;
} else {
console.error(data.message);
}
}
if(hasUserExpress){
let temp = selectedUserList.value;
temp.push({...mySelfData})
}
console.log('getUserList', data);
}
const showAddButton = computed(() => {

View File

@ -0,0 +1,11 @@
/**
*
*/
export const mySelfExpress = '#{sys_user_code}';
/**
*
*/
export const mySelfData = {
id: mySelfExpress, username: mySelfExpress, realname: '当前用户', avatarIcon: 'idcard-outlined', avatarColor: 'rgb(75 176 79)'
}

View File

@ -0,0 +1,69 @@
export const useCodeHinting = (CodeMirror, keywords, language) => {
const currentKeywords: any = [...keywords];
const codeHintingMount = (coder) => {
if (keywords.length) {
coder.setOption('mode', language);
setTimeout(() => {
coder!.on('cursorActivity', function () {
coder?.showHint({
completeSingle: false,
// container: containerRef.value
});
});
}, 1e3);
}
};
const codeHintingRegistry = () => {
const funcsHint = (cm, callback) => {
// 获取光标位置
const cur = cm.getCursor();
// 获取当前单词的信息
const token = cm.getTokenAt(cur);
const start = token.start;
const end = cur.ch;
const str = token.string;
if (str.length) {
const findIdx = (a, b) => a.toLowerCase().indexOf(b.toLowerCase());
let list = currentKeywords
.filter((item) => {
const index = findIdx(item, str);
return (index === 0 || index === 1) && (item.length != str.length || item.length - 1 != str.length);
})
.sort((a, b) => {
if (findIdx(a, str) < findIdx(b, str)) {
return -1;
} else {
return 1;
}
});
// 有点去掉点
// list = list.map(item => {
// if(item.indexOf(".") === 0){
// return item.substring(1);
// }
// return item;
// });
if (list.length === 1 && (list[0] === str || list[0].substring(1) === str)) {
list = [];
}
if (list.length) {
callback({
list: list,
from: CodeMirror.Pos(cur.line, start),
to: CodeMirror.Pos(cur.line, end),
});
}
}
};
funcsHint.async = true;
funcsHint.supportsSelection = true;
// 自动补全
keywords.length && CodeMirror.registerHelper('hint', language, funcsHint);
};
return {
codeHintingRegistry,
codeHintingMount,
};
};

View File

@ -3,7 +3,7 @@ import { inject, reactive, ref, computed, unref, watch, nextTick } from 'vue';
import { TreeActionType } from '/@/components/Tree';
import { listToTree } from '/@/utils/common/compUtils';
export function useTreeBiz(treeRef, getList, props) {
export function useTreeBiz(treeRef, getList, props, realProps) {
//接收下拉框选项
const selectOptions = inject('selectOptions', ref<Array<object>>([]));
//接收已选择的值
@ -19,7 +19,7 @@ export function useTreeBiz(treeRef, getList, props) {
//是否是打开弹框模式
const openModal = ref(false);
// 是否开启父子关联,如果不可以多选,就始终取消父子关联
const getCheckStrictly = computed(() => (props.multiple ? props.checkStrictly : true));
const getCheckStrictly = computed(() => (realProps.multiple ? props.checkStrictly : true));
// 是否是首次加载回显,只有首次加载,才会显示 loading
let isFirstLoadEcho = true;
@ -88,7 +88,7 @@ export function useTreeBiz(treeRef, getList, props) {
function onCheck(keys, info) {
if (props.checkable == true) {
// 如果不能多选,就只保留最后一个选中的
if (!props.multiple) {
if (!realProps.multiple) {
if (info.checked) {
//update-begin-author:taoyan date:20220408 for: 单选模式下设定rowKey无法选中数据-
checkedKeys.value = [info.node.eventKey];

View File

@ -147,6 +147,7 @@ export type ComponentType =
| 'LinkTableForQuery'
| 'CascaderPcaForQuery'
| 'UserSelect'
| 'RoleSelect'
| 'RangeDate'
| 'RangeNumber'
| 'linkRecordSelect'

View File

@ -4,14 +4,14 @@
<a-popover placement="bottomLeft" trigger="click" v-model="visible" :overlayClassName="`${prefixCls}-popover`">
<template #title>
<div class="flex justify-between">
<a-input :placeholder="t('component.icon.search')" @change="debounceHandleSearchChange" allowClear />
<a-input :placeholder="t('component.icon.search')" v-model:value="searchIconValue" @change="debounceHandleSearchChange" allowClear />
</div>
</template>
<template #content>
<div v-if="getPaginationList.length">
<ScrollContainer class="border border-solid border-t-0">
<ul class="flex flex-wrap px-2">
<ul class="flex flex-wrap px-2" style="padding-right: 0">
<li
v-for="icon in getPaginationList"
:key="icon"
@ -27,7 +27,7 @@
</ul>
</ScrollContainer>
<div class="flex py-2 items-center justify-center" v-if="getTotal >= pageSize">
<a-pagination showLessItems size="small" :pageSize="pageSize" :total="getTotal" @change="handlePageChange" />
<a-pagination showLessItems v-model:current="current" :page-size-options="pageSizeOptions" size="small" v-model:pageSize="pageSize" v-model:total="getTotal" @change="handlePageChange" />
</div>
</div>
<template v-else
@ -36,9 +36,9 @@
</template>
<span class="cursor-pointer px-2 py-1 flex items-center" v-if="isSvgMode && currentSelect">
<SvgIcon :name="currentSelect" />
<SvgIcon :name="currentSelect" @click="currentSelectClick"/>
</span>
<Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" v-else />
<Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" v-else @click="currentSelectClick"/>
</a-popover>
</template>
</a-input>
@ -85,7 +85,6 @@
const props = defineProps({
value: propTypes.string,
width: propTypes.string.def('100%'),
pageSize: propTypes.number.def(140),
copy: propTypes.bool.def(false),
mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
disabled: propTypes.bool.def(true),
@ -107,8 +106,16 @@
const debounceHandleSearchChange = useDebounceFn(handleSearchChange, 100);
const { clipboardRef, isSuccessRef } = useCopyToClipboard(props.value);
const { createMessage } = useMessage();
//
const current = ref<number>(1);
//
const pageSize = ref<number>(140);
//
const pageSizeOptions = ref<any>(['10', '20', '50', '100', '140'])
//
const searchIconValue = ref<string>('');
const { getPaginationList, getTotal, setCurrentPage } = usePagination(currentList, props.pageSize);
const { getPaginationList, getTotal, setCurrentPage, setPageSize } = usePagination(currentList, pageSize.value);
watchEffect(() => {
currentSelect.value = props.value;
@ -122,7 +129,12 @@
}
);
function handlePageChange(page: number) {
function handlePageChange(page: number,size: number) {
//update-begin---author:wangshuai ---date:20230522 forissues/4947------------
current.value = page;
pageSize.value = size;
setPageSize(size);
//update-end---author:wangshuai ---date:20230522 forissues/4947------------
setCurrentPage(page);
}
@ -147,13 +159,30 @@
function handleSearchChange(e: ChangeEvent) {
const value = e.target.value;
//update-begin---author:wangshuai ---date:20230522 forissues/4947------------
setCurrentPage(1);
current.value = 1;
//update-end---author:wangshuai ---date:20230522 forissues/4947------------
if (!value) {
setCurrentPage(1);
currentList.value = icons;
return;
}
currentList.value = icons.filter((item) => item.includes(value));
}
//update-begin---author:wangshuai ---date:20230522 forissues/4947------------
/**
* 图标点击重置页数
*/
function currentSelectClick() {
setCurrentPage(1);
setPageSize(140);
current.value = 1;
pageSize.value = 140;
currentList.value = icons;
searchIconValue.value = '';
}
//update-begin---author:wangshuai ---date:20230522 forissues/4947------------
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-icon-picker';
@ -164,7 +193,7 @@
}
&-popover {
width: 300px;
width: 400px;
.ant-popover-inner-content {
padding: 0;

View File

@ -18,8 +18,9 @@
const { innerValue, row, originColumn, cellProps, handleChangeCommon } = useJVxeComponent(props);
const groupId = ref<string>('j-vxe-popup');
const popupProps = computed(() => {
// update-begin--author:liaozhiyang---date:20231009---forissues/5371popup
return {
...cellProps,
...cellProps.value,
value: innerValue.value,
field: originColumn.value.field || originColumn.value.key,
code: originColumn.value.popupCode,
@ -45,6 +46,7 @@
}
},
};
// update-end--author:liaozhiyang---date:20231009---forissues/5371popup
});
// update-begin--author:liaozhiyang---date:20230811---forissues/675Popup
const handleFocus = () => {

View File

@ -10,7 +10,7 @@
import { useModalContext } from '../../Modal';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { getToken } from '/@/utils/auth';
import { getTenantId, getToken } from '/@/utils/auth';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
@ -23,6 +23,7 @@
},
emits: ['change', 'get', 'update:value'],
setup(props, { attrs, emit }) {
console.log("---Markdown---初始化---")
const wrapRef = ref<ElRef>(null);
const vditorRef = ref(null) as Ref<Nullable<Vditor>>;
const initedRef = ref(false);
@ -78,6 +79,7 @@
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1090 markdown
const uploadUrl = `${window._CONFIG['domianURL']}/sys/common/upload`;
const token = getToken();
const tenantId = getTenantId() ? getTenantId() : '0';
function formatResult(files, responseText): string {
let data: any = JSON.parse(responseText);
// {"success":true,"message":"markdown/aa_1653391146501.png","code":0,"result":null,"timestamp":1653391146501}'
@ -126,6 +128,7 @@
setHeaders() {
return {
'X-Access-Token': token as string,
'X-Tenant-Id': tenantId,
};
},
format(files, response) {

View File

@ -69,7 +69,7 @@
components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
inheritAttrs: false,
props: basicProps,
emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'],
emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible', 'fullScreen'],
setup(props, { emit, attrs , slots}) {
const visibleRef = ref(false);
const propsRef = ref<Partial<ModalProps> | null>(null);
@ -234,6 +234,12 @@
}
//update-end-author:taoyan date:2022-7-18 for: modal slot
// update-begin--author:liaozhiyang---date:20230804---forQQYUN-5866
watch(fullScreenRef,(val)=>{
emit('fullScreen',val);
});
// update-begin--author:liaozhiyang---date:20230804---forQQYUN-5866
return {
handleCancel,
getBindValue,

View File

@ -31,7 +31,9 @@
<!-- 增加对antdv3.x兼容 -->
<template #bodyCell="data">
<!-- update-begin--author:liaozhiyang---date:220230717---forissues-179antd3 一些警告以及报错(针对表格) -->
<!-- update-begin--author:liusq---date:20230921---forissues/770slotsBak异常报错的问题,增加判断column是否存在 -->
<template v-if="data.column?.slotsBak?.customRender">
<!-- update-end--author:liusq---date:20230921---forissues/770slotsBak异常报错的问题,增加判断column是否存在 -->
<slot :name="data.column.slotsBak.customRender" v-bind="data || {}"></slot>
</template>
<template v-else>
@ -46,7 +48,7 @@
<script lang="ts">
import type { BasicTableProps, TableActionType, SizeType, ColumnChangeParam, BasicColumn } from './types/table';
import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue';
import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect, watch, onUnmounted, onMounted } from 'vue';
import { Table } from 'ant-design-vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { PageWrapperFixedHeightKey } from '/@/components/Page/injectionKey';
@ -265,6 +267,10 @@
delete propsData.rowSelection
// update-end--author:sunjianlei---date:220230630---forQQYUN-5571
// update-begin--author:liaozhiyang---date:20230919---forQQYUN-6387
!propsData.isTreeTable && delete propsData.expandIconColumnIndex;
propsData.expandedRowKeys === null && delete propsData.expandedRowKeys;
// update-end--author:liaozhiyang---date:20230919---forQQYUN-6387
propsData = omit(propsData, ['class', 'onChange']);
return propsData;
});
@ -369,6 +375,39 @@
emit('register', tableAction, formActions);
// update-begin--author:liaozhiyang---date:20230804---forissues/638tabletable
if (getProps.value.showSummary) {
let tableBody;
const handleSroll = function () {
const scrollLeft = this.scrollLeft;
tableBody.forEach((elem) => (elem.scrollLeft = scrollLeft));
};
onMounted(() => {
const unwatch = watch(
getDataSourceRef,
(newVal) => {
if (newVal.length > 0) {
setTimeout(() => {
unwatch();
tableBody = wrapRef.value.querySelectorAll('.ant-table-body');
tableBody.forEach((elem) => {
elem.addEventListener('scroll', handleSroll, false);
});
}, 0);
console.log("---表格合计滚动---")
}
},
{ immediate: true }
);
});
onUnmounted(() => {
if (tableBody.length) {
tableBody.forEach((elem) => elem.removeEventListener('scroll', handleSroll));
}
});
}
// update-begin--author:liaozhiyang---date:20230804---forissues/638tabletable
return {
tableElRef,
getBindValues,

View File

@ -21,6 +21,11 @@
</template>
<Dropdown :trigger="['hover']" :dropMenuList="getDropdownList" popconfirm v-if="dropDownActions && getDropdownList.length > 0">
<slot name="more"></slot>
<!-- 设置插槽 -->
<template v-slot:[item.slot] v-for="(item, index) in getDropdownSlotList" :key="`${index}-${item.label}`">
<slot :name="item.slot"></slot>
</template>
<a-button type="link" size="small" v-if="!$slots.more"> <Icon icon="mdi-light:chevron-down"></Icon> </a-button>
</Dropdown>
</div>
@ -116,6 +121,9 @@
});
});
const getDropdownSlotList = computed((): any[] => {
return unref(getDropdownList).filter((item) => item.slot);
});
const getAlign = computed(() => {
const columns = (table as TableActionType)?.getColumns?.() || [];
const actionColumn = columns.find((item) => item.flag === ACTION_COLUMN_FLAG);
@ -139,7 +147,7 @@
isInButton && e.stopPropagation();
}
return { prefixCls, getActions, getDropdownList, getAlign, onCellClick, getTooltip };
return { prefixCls, getActions, getDropdownList, getDropdownSlotList, getAlign, onCellClick, getTooltip };
},
});
</script>

View File

@ -1,6 +1,7 @@
import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table';
import type { PaginationProps } from '../types/pagination';
import type { ComputedRef } from 'vue';
import { Table } from 'ant-design-vue';
import { computed, Ref, ref, toRaw, unref, watch, reactive } from 'vue';
import { renderEditCell } from '../components/editable';
import { usePermission } from '/@/hooks/web/usePermission';
@ -9,7 +10,7 @@ import { isArray, isBoolean, isFunction, isMap, isString } from '/@/utils/is';
import { cloneDeep, isEqual } from 'lodash-es';
import { formatToDate } from '/@/utils/dateUtil';
import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, INDEX_COLUMN_FLAG, PAGE_SIZE } from '../const';
import { CUS_SEL_COLUMN_KEY } from "./useCustomSelection";
import { CUS_SEL_COLUMN_KEY } from './useCustomSelection';
function handleItem(item: BasicColumn, ellipsis: boolean) {
const { key, dataIndex, children } = item;
@ -145,7 +146,7 @@ export function useColumns(
const viewColumns = sortFixedColumn(unref(getColumnsRef));
const columns = cloneDeep(viewColumns);
return columns
const result = columns
.filter((column) => {
return hasPermission(column.auth) && isIfShow(column);
})
@ -184,6 +185,22 @@ export function useColumns(
}
return reactive(column);
});
// update-begin--author:liaozhiyang---date:20230919---for【QQYUN-6387】展开写法去掉报错
if (propsRef.value.expandedRowKeys) {
let index = 0;
const findIndex = result.findIndex((item) => item.key === CUS_SEL_COLUMN_KEY);
if (findIndex != -1) {
index = findIndex + 1;
}
const next: any = result[index + 1];
let expand = Table.EXPAND_COLUMN;
if (next && (next['fixed'] == true || next['fixed'] == 'left')) {
expand = Object.assign(expand, { fixed: 'left' });
}
result.splice(index, 0, expand);
}
return result;
// update-end--author:liaozhiyang---date:20230919---for【QQYUN-6387】展开写法去掉报错
});
watch(
@ -260,6 +277,9 @@ export function useColumns(
columns = columns.filter((item) => item.key !== CUS_SEL_COLUMN_KEY);
// update-enb--author:sunjianlei---date:220230630---for【QQYUN-5571】自封装选择列解决数据行选择卡顿问题
// update-begin--author:liaozhiyang---date:220230804---for【issues/638】表格合计列自定义隐藏或展示时合计栏会错位
columns = columns.filter((item) => !item.defaultHidden);
// update-begin--author:liaozhiyang---date:220230804---for【issues/638】表格合计列自定义隐藏或展示时合计栏会错位
if (sort) {
columns = sortFixedColumn(columns);
}

View File

@ -108,6 +108,8 @@ export const basicProps = {
},
minHeight: propTypes.number,
maxHeight: propTypes.number,
// 统一设置列最大宽度
maxColumnWidth: propTypes.number,
dataSource: {
type: Array as PropType<Recordable[]>,
default: null,
@ -136,4 +138,8 @@ export const basicProps = {
type: String as PropType<SizeType>,
default: DEFAULT_SIZE,
},
expandedRowKeys: {
type: Array,
default: null,
},
};

View File

@ -189,6 +189,8 @@ export interface BasicTableProps<T = any> {
formConfig?: Partial<FormProps>;
// 列配置
columns: BasicColumn[];
// 统一设置列最大宽度
maxColumnWidth?: number;
// 是否显示序号列
showIndexColumn?: boolean;
// 序号列配置
@ -317,7 +319,7 @@ export interface BasicTableProps<T = any> {
* you need to add style .ant-table td { white-space: nowrap; }.
* @type object
*/
scroll?: { x?: number | true; y?: number };
scroll?: { x?: number | true | 'max-content'; y?: number };
/**
* Whether to show table header

View File

@ -24,7 +24,7 @@
import { useGlobSetting } from '/@/hooks/setting';
import { useI18n } from '/@/hooks/web/useI18n';
import { getToken } from '/@/utils/auth';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { getFileAccessHttpUrl, getHeaders } from '/@/utils/common/compUtils';
export default defineComponent({
name: 'TinymceImageUpload',
@ -44,7 +44,7 @@
//update-begin-author:taoyan date:2022-5-13 for:
function getheader() {
return { 'X-Access-Token': getToken() };
return getHeaders();
}
function getBizData() {

View File

@ -4,17 +4,16 @@
// colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration
export const plugins = [
'advlist anchor autolink autosave code codesample directionality fullscreen hr insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus template textpattern visualblocks visualchars wordcount image',
'advlist anchor autolink autosave code codesample directionality fullscreen hr insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace tabfocus template textpattern visualblocks visualchars wordcount image',
];
export const toolbar =
'fullscreen code preview | undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent lineheight|subscript superscript blockquote| numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | insertfile image media pageembed link anchor codesample insertdatetime hr| a11ycheck ltr rtl';
export const simplePlugins = ['lists image link media table textcolor wordcount contextmenu fullscreen'];
export const simplePlugins = 'lists image link fullscreen';
export const simpleToolbar = [
'undo redo formatselect bold italic alignleft aligncenter alignright alignjustify bullist numlist outdent indent',
'lists link unlink image media table removeformat fullscreen',
'undo redo formatselect bold italic alignleft aligncenter alignright alignjustify bullist numlist outdent indent lists image link fullscreen',
];
export const menubar = 'file edit insert view format table';

View File

@ -27,27 +27,20 @@
},
setup(props) {
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
const option = reactive({
title: {
text: '基础雷达图',
},
legend: {
data: ['文综', '理综'],
data: ['文综'],
},
radar: {
indicator: [
{ name: '历史', max: 100 },
{ name: '地理', max: 110 },
{ name: '生物', max: 120 },
{ name: '化学', max: 130 },
{ name: '物理', max: 140 },
{ name: '政治', max: 150 },
],
indicator: [{ name: '历史' }, { name: '地理' }, { name: '生物' }, { name: '化学' }, { name: '物理' }, { name: '政治' }],
},
series: [
{
type: 'radar',
type: 'radar' as 'custom',
data: [
{
value: [82, 70, 60, 55, 90, 66],
@ -86,7 +79,7 @@
//data
data.push(obj);
});
option.radar.indicator = indicator;
option.radar.axisName = indicator;
option.series[0]['data'] = data;
setOptions(option);
}

View File

@ -6,7 +6,7 @@
import { useECharts } from '/@/hooks/web/useECharts';
import { cloneDeep } from 'lodash-es';
export default defineComponent({
name: 'line',
name: 'single-line',
props: {
chartData: {
type: Array,

View File

@ -7,9 +7,6 @@
<span class="inner-button"><upload-outlined />上传</span>
</a-upload>
</span>
<span class="j-icon">
<span class="inner-button"><folder-outlined />从文件库选择?</span>
</span>
</template>
</a-alert>
@ -72,6 +69,7 @@
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { useUserStore } from '/@/store/modules/user';
import { saveOne, useCommentWithFile, useFileList } from './useComment';
import {useModal} from "/@/components/Modal";
import { Tooltip } from 'ant-design-vue';
import HistoryFileList from './HistoryFileList.vue';
@ -95,6 +93,7 @@
},
setup(props) {
// const { createMessage } = useMessage();
const [registerModel, { openModal }] = useModal();
const { userInfo } = useUserStore();
const dataList = ref([]);
const commentId = ref('');
@ -141,6 +140,19 @@
await loadFileList();
}
function showFileModal() {
openModal(true, {})
}
function onSelectFileOk(temp) {
let arr = selectFileList.value;
arr.push({
...temp,
exist: true
})
selectFileList.value = arr;
}
return {
selectFileList,
beforeUpload,
@ -153,7 +165,11 @@
queding,
buttonLoading,
getImageAsBackground,
viewImage
viewImage,
registerModel,
showFileModal,
onSelectFileOk,
};
},
};

View File

@ -107,7 +107,9 @@
props: {
tableName: propTypes.string.def(''),
dataId: propTypes.string.def(''),
datetime: propTypes.number.def(1)
datetime: propTypes.number.def(1),
//
otherHeight: propTypes.number.def(0),
},
setup(props) {
const { createMessage } = useMessage();
@ -163,8 +165,9 @@
const commentHeight = ref(300);
const allHeight = ref(300);
onMounted(() => {
commentHeight.value = window.innerHeight - 57 - 46 - 70 - 160;
allHeight.value = window.innerHeight - 57 - 46 - 53 -20;
let otherHeight = props.otherHeight || 0;
commentHeight.value = window.innerHeight - 57 - 46 - 70 - 160 - otherHeight;
allHeight.value = window.innerHeight - 57 - 46 - 53 -20 - otherHeight;
});
/**
@ -247,7 +250,7 @@
}
});
// const storageEmojiIndex = inject('$globalEmojiIndex')
//const storageEmojiIndex = inject('$globalEmojiIndex')
const storageEmojiIndex = getGloablEmojiIndex()
const { getHtml } = useEmojiHtml(storageEmojiIndex);
const bottomCommentRef = ref()

View File

@ -2,7 +2,7 @@
<div class="comment-tabs-warp" v-if="showStatus">
<a-tabs @change="handleChange" :animated="false">
<a-tab-pane v-if="showComment" tab="评论" key="comment" class="comment-list-tab">
<comment-list :tableName="tableName" :dataId="dataId" :datetime="datetime1"></comment-list>
<comment-list :tableName="tableName" :dataId="dataId" :datetime="datetime1" :otherHeight="otherHeight"></comment-list>
</a-tab-pane>
<a-tab-pane v-if="showFiles" tab="文件" key="file">
<comment-files :tableName="tableName" :dataId="dataId" :datetime="datetime2"></comment-files>
@ -41,6 +41,8 @@
showFiles: propTypes.bool.def(true),
//
showDataLog: propTypes.bool.def(true),
//
otherHeight: propTypes.number.def(0),
},
setup(props) {
const showStatus = computed(() => {

View File

@ -9,7 +9,7 @@
<edit-outlined v-else/>
</span>
<span class="log-item-content">
<a @click="handleClickPerson">@{{item.createBy}}</a>
<a @click="handleClickPerson">@{{item.createName || item.createBy}}</a>
{{ item.dataContent }}
</span>
<div class="log-item-date">

View File

@ -1,6 +1,6 @@
<template>
<div :class="{'comment-active': commentActive}" style="border: 1px solid #eee; margin: 0; position: relative" @click="handleClickBlank">
<textarea ref="commentRef" v-model="myComment" @input="handleCommentChange" @blur="handleBlur" class="comment-content" :rows="3" placeholder="请输入你的评论,可以@成员" />
<textarea ref="commentRef" v-model="myComment" @keyup.enter="sendComment" @input="handleCommentChange" @blur="handleBlur" class="comment-content" :rows="3" placeholder="请输入你的评论,可以@成员" />
<div class="comment-content comment-html-shower" :class="{'no-content':noConent, 'top-div': showHtml, 'bottom-div': showHtml == false }" v-html="commentHtml" @click="handleClickHtmlShower"></div>
<div class="comment-buttons" v-if="commentActive">
<div style="cursor: pointer">
@ -53,7 +53,7 @@
import { useModal } from '/@/components/Modal';
import UploadChunk from './UploadChunk.vue';
import 'emoji-mart-vue-fast/css/emoji-mart.css';
import { getGloablEmojiIndex, useEmojiHtml } from './useComment';
import {getGloablEmojiIndex, useEmojiHtml} from './useComment';
const optionsName = {
categories: {

View File

@ -7,9 +7,6 @@
<span class="inner-button"><upload-outlined />上传</span>
</a-upload>
</span>
<span class="j-icon">
<span class="inner-button"><folder-outlined />从文件库选择?</span>
</span>
</template>
</a-alert>
@ -53,6 +50,7 @@
</div>
</div>
</div>
</template>
<script lang="ts">
@ -60,6 +58,8 @@
import { useFileList } from './useComment';
import { Tooltip } from 'ant-design-vue';
import { UploadOutlined, FolderOutlined, DownloadOutlined, PaperClipOutlined, DeleteOutlined } from '@ant-design/icons-vue';
import {useModal} from "/@/components/Modal";
export default {
name: 'UploadChunk',
components: {
@ -80,6 +80,8 @@
setup(_p, {emit}) {
const { selectFileList, beforeUpload, handleRemove, getBackground, isImage, getImageSrc, viewImage } = useFileList();
const [registerModel, { openModal }] = useModal();
function getUploadFileList() {
let list = toRaw(selectFileList.value);
console.log(list);
@ -99,6 +101,19 @@
}
});
function showFileModal() {
openModal(true, {})
}
function onSelectFileOk(temp) {
let arr = selectFileList.value;
arr.push({
...temp,
exist: true
})
selectFileList.value = arr;
}
return {
selectFileList,
beforeUpload,
@ -108,7 +123,10 @@
clear,
isImage,
getImageSrc,
viewImage
viewImage,
registerModel,
showFileModal,
onSelectFileOk
};
},
};

View File

@ -11,6 +11,7 @@ import other from '/@/assets/svg/fileType/other.svg';
import pdf from '/@/assets/svg/fileType/pdf.svg';
import txt from '/@/assets/svg/fileType/txt.svg';
import word from '/@/assets/svg/fileType/word.svg';
import image from '/@/assets/svg/fileType/image.png';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { createImgPreview } from '/@/components/Preview';
import data from "emoji-mart-vue-fast/data/apple.json";
@ -171,11 +172,28 @@ export function useCommentWithFile(props) {
});
}
/**
* QQYUN-4310
* @param file
*/
async function saveSysFormFile(file){
let url = '/sys/comment/addFile';
let params = {
fileId: file.id,
commentId: uploadData.commentId
}
await defHttp.post({url, params}, { joinParamsToUrl: true, isTransformResponse: false });
}
async function uploadFiles(fileList) {
if (fileList && fileList.length > 0) {
for (let i = 0; i < fileList.length; i++) {
let file = toRaw(fileList[i]);
await uploadOne(file.originFileObj);
if(file.exist === true){
await saveSysFormFile(file);
}else{
await uploadOne(file.originFileObj);
}
}
}
}
@ -211,6 +229,7 @@ export function useFileList() {
txt: txt,
docx: word,
doc: word,
image
};
function getBackground(item) {
console.log('获取文件背景图', item);
@ -231,6 +250,10 @@ export function useFileList() {
}
}
function getImageTypeIcon() {
return typeMap['image'];
}
function getBase64(file, id){
return new Promise((resolve, reject) => {
//声明js的文件流
@ -298,6 +321,9 @@ export function useFileList() {
}
function getImageSrc(file){
if(file.exist){
return getFileAccessHttpUrl(file.url);
}
if(isImage(file)){
let id = file.uid;
if(id){
@ -318,7 +344,12 @@ export function useFileList() {
* @param item
*/
function getImageAsBackground(item){
let url = getImageSrc(item);
let url;
if(item.exist){
url = getFileAccessHttpUrl(item.url);
}else{
url = getImageSrc(item);
}
if(url){
return {
"backgroundImage": "url('"+url+"')"
@ -373,7 +404,8 @@ export function useFileList() {
isImage,
getImageSrc,
getImageAsBackground,
viewImage
viewImage,
getImageTypeIcon
};
}

View File

@ -1,10 +1,12 @@
import type { App } from 'vue';
import { Icon } from './Icon';
import AIcon from '/@/components/jeecg/AIcon.vue';
import { Button, JUploadButton } from './Button';
//Tinymce富文本
import Editor from '/@/components/Tinymce/src/Editor.vue'
import Editor from '/@/components/Tinymce/src/Editor.vue';
import { Button, JUploadButton } from './Button';
// 按需注册antd的组件
import {
// Need
Button as AntButton,
@ -56,14 +58,11 @@ import {
Cascader,
Rate,
} from 'ant-design-vue';
const compList = [AntButton.Group, Icon, AIcon, JUploadButton];
//敲敲云—仪表盘设计器(拖拽设计)
import DragEngine from '@qiaoqiaoyun/drag-free';
if (import.meta.env.DEV) {
import('@qiaoqiaoyun/drag-free/lib/index.css');
}
import('@qiaoqiaoyun/drag-free/lib/index.css');
console.log('---初始化--- 全局注册仪表盘--------------');
export function registerGlobComp(app: App) {
@ -71,9 +70,10 @@ export function registerGlobComp(app: App) {
app.component(comp.name || comp.displayName, comp);
});
//仪表盘依赖Tinymce需要提前加载没办法按需加载了
app.component(Editor.name, Editor)
app.component(Editor.name, Editor);
app.use(Select)
app
.use(Select)
.use(Alert)
.use(Button)
.use(Breadcrumb)

View File

@ -8,6 +8,7 @@ html {
--sider-dark-bg-color: #273352;
--sider-dark-darken-bg-color: #273352;
--sider-dark-lighten-bg-color: #273352;
--sider-logo-bg-color:linear-gradient(180deg, #000000, #021d37);
}
@white: #fff;
@ -64,6 +65,7 @@ html {
// =================================
// let -menu
@sider-logo-bg-color: var(--sider-logo-bg-color);
@sider-dark-bg-color: var(--sider-dark-bg-color);
@sider-dark-darken-bg-color: var(--sider-dark-darken-bg-color);
@sider-dark-lighten-bg-color: var(--sider-dark-lighten-bg-color);

View File

@ -35,6 +35,9 @@ export const LOGIN_INFO_KEY = 'LOGIN__INFO__';
// 聊天UID key
export const JEECG_CHAT_UID = 'JEECG_CHAT_UID';
// 免登录租户id与系统分开避免重复
export const OAUTH2_THIRD_LOGIN_TENANT_ID = 'THIRD_LOGIN_TENANT_ID';
export enum CacheTypeEnum {
SESSION,
LOCAL,

View File

@ -11,4 +11,6 @@ export enum PageEnum {
OAUTH2_LOGIN_PAGE_PATH = '/oauth2-app/login',
//文件路由
SYS_FILES_PATH = '/file/share',
// 邮件中的跳转地址
TOKEN_LOGIN = '/tokenLogin'
}

View File

@ -0,0 +1,50 @@
import type { UnwrapRef, Ref, WritableComputedRef, DeepReadonly } from 'vue';
import { reactive, readonly, computed, getCurrentInstance, watchEffect, unref, nextTick, toRaw } from 'vue';
import { Form } from 'ant-design-vue';
import { FormItemContext } from 'ant-design-vue/es/form/FormItemContext';
import { isEqual } from 'lodash-es';
export function useRuleFormItem<T extends Recordable, K extends keyof T, V = UnwrapRef<T[K]>>(
props: T,
key?: K,
changeEvent?,
emitData?: Ref<any[] | undefined>
): [WritableComputedRef<V>, (val: V) => void, DeepReadonly<V>, FormItemContext];
export function useRuleFormItem<T extends Recordable>(props: T, key: keyof T = 'value', changeEvent = 'change', emitData?: Ref<any[]>) {
const instance = getCurrentInstance();
const emit = instance?.emit;
const formItemContext = Form.useInjectFormItemContext();
const innerState = reactive({
value: props[key],
});
const defaultState = readonly(innerState);
const setState = (val: UnwrapRef<T[keyof T]>): void => {
innerState.value = val as T[keyof T];
};
watchEffect(() => {
innerState.value = props[key];
});
const state: any = computed({
get() {
return innerState.value == null ? "" : innerState.value;
},
set(value) {
if (isEqual(value, defaultState.value)) return;
innerState.value = value as T[keyof T];
nextTick(() => {
emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || []));
// https://antdv.com/docs/vue/migration-v3-cn
// antDv3升级后需要调用这个方法更新校验的值
nextTick(() => formItemContext.onFieldChange());
});
},
});
return [state, setState, defaultState, formItemContext];
}

View File

@ -10,6 +10,8 @@ import echarts from '/@/utils/lib/echarts';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
export function useECharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' | 'default' = 'default') {
console.log("---useECharts---初始化加载---")
const { getDarkMode: getSysDarkMode } = useRootSetting();
const getDarkMode = computed(() => {

View File

@ -120,6 +120,22 @@ function createWarningModal(options: ModalOptionsPartial) {
return Modal.warning(createModalOptions(options, 'warning'));
}
interface MOE extends Omit<ModalOptionsEx, 'iconType'> {
iconType?: ModalOptionsEx['iconType'];
}
// 提示框无需传入iconType默认为warning
function createConfirmSync(options: MOE) {
return new Promise((resolve) => {
createConfirm({
iconType: 'warning',
...options,
onOk: () => resolve(true),
onCancel: () => resolve(false),
});
});
}
notification.config({
placement: 'topRight',
duration: 3,
@ -133,6 +149,7 @@ export function useMessage() {
createMessage: Message,
notification: notification as NotifyApi,
createConfirm: createConfirm,
createConfirmSync,
createSuccessModal,
createErrorModal,
createInfoModal,

View File

@ -2,16 +2,17 @@
<Tooltip :title="t('layout.header.tooltipLock')" placement="bottom" :mouseEnterDelay="0.5" @click="handleLock">
<LockOutlined />
</Tooltip>
<LockModal @register="register" />
<LockModal ref="modalRef" v-if="lockModalVisible" @register="register" />
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { defineComponent, computed, ref } from 'vue';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import { Tooltip } from 'ant-design-vue';
import { LockOutlined } from '@ant-design/icons-vue';
import Icon from '/@/components/Icon';
import { useI18n } from '/@/hooks/web/useI18n';
import { useModal } from '/@/components/Modal';
import { getRefPromise } from '/@/utils/index';
export default defineComponent({
name: 'LockScreen',
@ -25,15 +26,21 @@
setup() {
const { t } = useI18n();
const [register, { openModal }] = useModal();
function handleLock() {
// update-begin--author:liaozhiyang---date:20230901---forQQYUN-6333访
const lockModalVisible = ref(false);
const modalRef = ref(null);
async function handleLock() {
lockModalVisible.value = true;
await getRefPromise(modalRef);
openModal(true);
}
// update-end--author:liaozhiyang---date:20230901---forQQYUN-6333访
return {
t,
register,
handleLock,
lockModalVisible,
modalRef,
};
},
});

View File

@ -23,7 +23,8 @@
import { useI18n } from '/@/hooks/web/useI18n';
import { useDesign } from '/@/hooks/web/useDesign';
import { BasicModal, useModalInner } from '/@/components/Modal/index';
import { BasicForm, useForm } from '/@/components/Form/index';
import BasicForm from '/@/components/Form/src/BasicForm.vue';
import { useForm } from '/@/components/Form/src/hooks/useForm';
import { useUserStore } from '/@/store/modules/user';
import { useLockStore } from '/@/store/modules/lock';

View File

@ -155,7 +155,6 @@
}
}
return {
prefixCls,
listData,

View File

@ -3,6 +3,7 @@ import { defHttp } from '/@/utils/http/axios';
enum Api {
listCementByUser = '/sys/annountCement/listByUser',
editCementSend = '/sys/sysAnnouncementSend/editByAnntIdAndUserId',
clearAllUnReadMessage = '/sys/annountCement/clearAllUnReadMessage',
}
/**
@ -12,3 +13,8 @@ enum Api {
export const listCementByUser = (params?) => defHttp.get({ url: Api.listCementByUser, params });
export const editCementSend = (anntId, params?) => defHttp.put({ url: Api.editCementSend, params: { anntId, ...params } });
/**
*
*/
export const clearAllUnReadMessage = () => defHttp.post({ url: Api.clearAllUnReadMessage },{ isTransformResponse: false });

View File

@ -50,7 +50,7 @@
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, watch, unref, defineExpose } from 'vue';
import { ref, computed, watch, unref } from 'vue';
import { Avatar } from 'ant-design-vue';
import { BasicModal } from '/@/components/Modal';
import { getUserDeparts, selectDepart } from '/@/views/system/depart/depart.api';

View File

@ -4,11 +4,12 @@
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, unref, defineExpose } from 'vue';
import { ref, unref } from 'vue';
import { rules } from '/@/utils/helper/validator';
import { defHttp } from '/@/utils/http/axios';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import BasicForm from '/@/components/Form/src/BasicForm.vue';
import { useForm } from '/@/components/Form/src/hooks/useForm';
import { useMessage } from '/@/hooks/web/useMessage';
// Emits
const emit = defineEmits(['register']);

View File

@ -27,9 +27,9 @@
</Menu>
</template>
</Dropdown>
<LockAction @register="register" />
<LockAction v-if="lockActionVisible" ref="lockActionRef" @register="register" />
<DepartSelect ref="loginSelectRef" />
<UpdatePassword ref="updatePasswordRef" />
<UpdatePassword v-if="passwordVisible" ref="updatePasswordRef" />
</template>
<script lang="ts">
// components
@ -56,6 +56,7 @@
import { DB_DICT_DATA_KEY } from '/src/enums/cacheEnum';
import { removeAuthCache, setAuthCache } from '/src/utils/auth';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { getRefPromise } from '/@/utils/index';
type MenuEvent = 'logout' | 'doc' | 'lock' | 'cache' | 'depart';
const { createMessage } = useMessage();
@ -79,6 +80,9 @@
const { getShowDoc, getUseLockPage } = useHeaderSetting();
const userStore = useUserStore();
const go = useGo();
const passwordVisible = ref(false);
const lockActionVisible = ref(false);
const lockActionRef = ref(null);
const getUserInfo = computed(() => {
const { realname = '', avatar, desc } = userStore.getUserInfo || {};
@ -99,10 +103,12 @@
* 多部门弹窗逻辑
*/
const loginSelectRef = ref();
function handleLock() {
// update-begin--author:liaozhiyang---date:20230901---forQQYUN-6333访
async function handleLock() {
await getRefPromise(lockActionRef);
openModal(true);
}
// update-end--author:liaozhiyang---date:20230901---forQQYUN-6333访
// login out
function handleLoginOut() {
userStore.confirmLoginOut();
@ -131,10 +137,13 @@
}
//
const updatePasswordRef = ref();
function updatePassword() {
// update-begin--author:liaozhiyang---date:20230901---forQQYUN-6333访
async function updatePassword() {
passwordVisible.value = true;
await getRefPromise(updatePasswordRef);
updatePasswordRef.value.show(userStore.getUserInfo.username);
}
// update-end--author:liaozhiyang---date:20230901---forQQYUN-6333访
function handleMenuClick(e: { key: MenuEvent }) {
switch (e.key) {
case 'logout':
@ -174,6 +183,8 @@
getUseLockPage,
loginSelectRef,
updatePasswordRef,
passwordVisible,
lockActionVisible,
};
},
});

View File

@ -7,6 +7,7 @@
`${prefixCls}__item`,
{
[`${prefixCls}__item--active`]: def === color,
[`${prefixCls}__item--black`]: color == '#ffffff',
},
]"
:style="{ background: color }"
@ -83,6 +84,11 @@
fill: @white !important;
}
}
&--black {
svg {
fill: #000 !important;
}
}
}
}
</style>

View File

@ -2,11 +2,13 @@ import { colorIsDark, lighten, darken } from '/@/utils/color';
import { useAppStore } from '/@/store/modules/app';
import { ThemeEnum } from '/@/enums/appEnum';
import { setCssVar } from './util';
import { SIDE_BAR_BG_COLOR_LIST, SIDER_LOGO_BG_COLOR_LIST } from '/@/settings/designSetting';
const HEADER_BG_COLOR_VAR = '--header-bg-color';
const HEADER_BG_HOVER_COLOR_VAR = '--header-bg-hover-color';
const HEADER_MENU_ACTIVE_BG_COLOR_VAR = '--header-active-menu-bg-color';
const SIDER_LOGO_BG_COLOR = '--sider-logo-bg-color';
const SIDER_DARK_BG_COLOR = '--sider-dark-bg-color';
const SIDER_DARK_DARKEN_BG_COLOR = '--sider-dark-darken-bg-color';
const SIDER_LIGHTEN_BG_COLOR = '--sider-dark-lighten-bg-color';
@ -59,6 +61,10 @@ export function updateSidebarBgColor(color?: string) {
color = appStore.getMenuSetting.bgColor;
}
}
// update-begin--author:liaozhiyang---date:20230811---for【QQYUN-5922】logo背景色渐变
let findIndex = SIDE_BAR_BG_COLOR_LIST.findIndex((item) => item === color);
setCssVar(SIDER_LOGO_BG_COLOR, findIndex == -1 ? 'linear-gradient(180deg, #000000, #282828)' : SIDER_LOGO_BG_COLOR_LIST[findIndex]);
// update-end--author:liaozhiyang---date:20230811---for【QQYUN-5922】llogo背景色渐变
setCssVar(SIDER_DARK_BG_COLOR, color);
setCssVar(SIDER_DARK_DARKEN_BG_COLOR, darken(color!, 6));
setCssVar(SIDER_LIGHTEN_BG_COLOR, lighten(color!, 5));

View File

@ -19,6 +19,7 @@ import { registerPackages } from '/@/utils/monorepo/registerPackages';
// 在本地开发中引入的,以提高浏览器响应速度
if (import.meta.env.DEV) {
// @ts-ignore
import('ant-design-vue/dist/antd.less');
}
async function bootstrap() {
@ -63,6 +64,8 @@ async function bootstrap() {
// 挂载应用
app.mount('#app', true);
console.log(" vue3 app 加载完成!")
}
bootstrap();

View File

@ -18,7 +18,7 @@ export const getParentLayout = (_name?: string) => {
return () =>
new Promise((resolve) => {
resolve({
name: PARENT_LAYOUT_NAME,
name: _name || PARENT_LAYOUT_NAME,
});
});
};

View File

@ -10,6 +10,8 @@ import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
import { RootRoute } from '/@/router/routes';
import { isOAuth2AppEnv } from '/@/views/sys/login/useLogin';
import { OAUTH2_THIRD_LOGIN_TENANT_ID } from "/@/enums/cacheEnum";
import { setAuthCache } from "/@/utils/auth";
const LOGIN_PATH = PageEnum.BASE_LOGIN;
//auth2登录路由
@ -18,11 +20,14 @@ const OAUTH2_LOGIN_PAGE_PATH = PageEnum.OAUTH2_LOGIN_PAGE_PATH;
//分享免登录路由
const SYS_FILES_PATH = PageEnum.SYS_FILES_PATH;
// 邮件中的跳转地址,对应此路由,携带token免登录直接去办理页面
const TOKEN_LOGIN = PageEnum.TOKEN_LOGIN;
const ROOT_PATH = RootRoute.path;
//update-begin---author:wangshuai ---date:20220629 for[issues/I5BG1I]vue3不支持auth2登录------------
//update-begin---author:wangshuai ---date:20221111 for: [VUEN-2472]分享免登录------------
const whitePathList: PageEnum[] = [LOGIN_PATH, OAUTH2_LOGIN_PAGE_PATH,SYS_FILES_PATH];
const whitePathList: PageEnum[] = [LOGIN_PATH, OAUTH2_LOGIN_PAGE_PATH,SYS_FILES_PATH, TOKEN_LOGIN ];
//update-end---author:wangshuai ---date:20221111 for: [VUEN-2472]分享免登录------------
//update-end---author:wangshuai ---date:20220629 for[issues/I5BG1I]vue3不支持auth2登录------------
@ -62,7 +67,12 @@ export function createPermissionGuard(router: Router) {
} else if (to.path === LOGIN_PATH && isOAuth2AppEnv() && !token) {
//退出登录进入此逻辑
//如果进入的页面是login页面并且当前是OAuth2app环境并且token为空就进入OAuth2登录页面
//update-begin---author:wangshuai ---date:20230224 for[QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------
if(to.query.tenantId){
setAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID,to.query.tenantId)
}
next({ path: OAUTH2_LOGIN_PAGE_PATH });
//update-end---author:wangshuai ---date:20230224 for[QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------
return;
//update-end---author:wangshuai ---date:20220629 for[issues/I5BG1I]vue3不支持auth2登录------------
}
@ -89,6 +99,20 @@ export function createPermissionGuard(router: Router) {
next();
}
} else {
//update-begin---author:wangshuai ---date:20230302 for只有首次登陆并且是企业微信或者钉钉的情况下才会调用------------
//----------【首次登陆并且是企业微信或者钉钉的情况下才会调用】-----------------------------------------------
//只有首次登陆并且是企业微信或者钉钉的情况下才会调用
let href = window.location.href;
//判断当前是auth2页面并且是钉钉/企业微信并且包含tenantId参数
if(isOAuth2AppEnv() && href.indexOf("/tenantId/")!= -1){
let params = to.params;
if(params && params.path && params.path.length>0){
//直接获取参数最后一位
setAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID,params.path[params.path.length-1])
}
}
//---------【首次登陆并且是企业微信或者钉钉的情况下才会调用】------------------------------------------------
//update-end---author:wangshuai ---date:20230302 for只有首次登陆并且是企业微信或者钉钉的情况下才会调用------------
// 如果当前是在OAuth2APP环境就跳转到OAuth2登录页面否则跳转到登录页面
path = isOAuth2AppEnv() ? OAUTH2_LOGIN_PAGE_PATH : LOGIN_PATH;
}
@ -100,16 +124,38 @@ export function createPermissionGuard(router: Router) {
//update-end---author:wangshuai ---date:20220629 for[issues/I5BG1I]vue3 Auth2未实现------------
replace: true,
};
if (to.path) {
//update-begin---author:scott ---date:2023-04-24 for【QQYUN-4713】登录代码调整逻辑有问题改造待观察--
if (to.fullPath) {
console.log("to.fullPath 1",to.fullPath)
console.log("to.path 2",to.path)
let getFullPath = to.fullPath;
if(getFullPath=='/' || getFullPath=='/500' || getFullPath=='/400' || getFullPath=='/login?redirect=/' || getFullPath=='/login?redirect=/login?redirect=/'){
return;
}
//update-end---author:scott ---date:2023-04-24 for【QQYUN-4713】登录代码调整逻辑有问题改造待观察--
redirectData.query = {
...redirectData.query,
redirect: to.path,
// update-begin-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
redirect: to.fullPath,
// update-end-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
};
}
next(redirectData);
return;
}
//==============================【首次登录并且是企业微信或者钉钉的情况下才会调用】==================
//判断是免登录页面,如果页面包含/tenantId/,那么就直接前往主页
if(isOAuth2AppEnv() && to.path.indexOf("/tenantId/") != -1){
next(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
return;
}
//==============================【首次登录并且是企业微信或者钉钉的情况下才会调用】==================
// Jump to the 404 page after processing the login
if (from.path === LOGIN_PATH && to.name === PAGE_NOT_FOUND_ROUTE.name && to.fullPath !== (userStore.getUserInfo.homePath || PageEnum.BASE_HOME)) {
next(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);

View File

@ -6,7 +6,7 @@ import { cloneDeep, omit } from 'lodash-es';
import { warn } from '/@/utils/log';
import { createRouter, createWebHashHistory } from 'vue-router';
import { getTenantId, getToken } from "/@/utils/auth";
import { URL_HASH_TAB } from '/@/utils';
import { URL_HASH_TAB, _eval } from '/@/utils';
//引入online lib路由
import { packageViews } from '/@/utils/monorepo/dynamicRouter';
import {useI18n} from "/@/hooks/web/useI18n";
@ -39,11 +39,11 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
if (item?.meta?.title) {
const { t } = useI18n();
if(item.meta.title.includes('t(\'') && t){
item.meta.title = eval(item.meta.title);
//console.log('译后: ',item.meta.title)
// update-begin--author:liaozhiyang---date:20230906---for【QQYUN-6390】eval替换成new Function解决build警告
item.meta.title = new Function('t', `return ${item.meta.title}`)(t);
// update-end--author:liaozhiyang---date:20230906---for【QQYUN-6390】eval替换成new Function解决build警告
}
}
// update-begin--author:sunjianlei---date:20210918---for:适配旧版路由选项 --------
// @ts-ignore 适配隐藏路由
if (item?.hidden) {
@ -61,7 +61,7 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
let tenantId = getTenantId();
// URL支持{{ window.xxx }}占位符变量
//update-begin---author:wangshuai ---date:20220711 for[VUEN-1638]菜单tenantId需要动态生成------------
item.component = (item.component || '').replace(/{{([^}}]+)?}}/g, (s1, s2) => eval(s2)).replace('${token}', token).replace('${tenantId}', tenantId);
item.component = (item.component || '').replace(/{{([^}}]+)?}}/g, (s1, s2) => _eval(s2)).replace('${token}', token).replace('${tenantId}', tenantId);
//update-end---author:wangshuai ---date:20220711 for[VUEN-1638]菜单tenantId需要动态生成------------
// 适配 iframe
if (/^\/?http(s)?/.test(item.component as string)) {

View File

@ -21,6 +21,15 @@ export const router = createRouter({
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();
});
// reset router
export function resetRouter() {
router.getRoutes().forEach((route) => {

View File

@ -39,7 +39,6 @@ export const LoginRoute: AppRouteRecordRaw = {
},
};
//update-begin---author:wangshuai ---date:20220629 forauth2登录页面路由------------
export const Oauth2LoginRoute: AppRouteRecordRaw = {
path: '/oauth2-app/login',
name: 'oauth2-app-login',
@ -50,7 +49,6 @@ export const Oauth2LoginRoute: AppRouteRecordRaw = {
title: t('routes.oauth2.login'),
},
};
//update-end---author:wangshuai ---date:20220629 forauth2登录页面路由------------
/**
* token

View File

@ -36,3 +36,18 @@ export const SIDE_BAR_BG_COLOR_LIST: string[] = [
'#344058',
'#383f45',
];
// sider logo line preset color [logo½¥±äÉ«]
export const SIDER_LOGO_BG_COLOR_LIST: string[] = [
'linear-gradient(180deg, #000000, #021d37)',
'linear-gradient(180deg, #000000, #282828)',
'linear-gradient(180deg, #1c253e, #2b385c)',
'linear-gradient(180deg, #ffffff, #faf8f8)',
'linear-gradient(180deg, #000000, #242735)',
'linear-gradient(180deg, #000000, #1d1f2a)',
'linear-gradient(180deg, #304156, #32455d)',
'linear-gradient(180deg, #000000, #001f39)',
'linear-gradient(180deg, #000000, #2b3743)',
'linear-gradient(180deg, #344058, #374560)',
'linear-gradient(180deg, #383f45, #3b434b)',
];

View File

@ -4,15 +4,13 @@ import { registerJVxeCustom } from '/@/components/JVxeCustom';
// 注册全局聊天表情包
import { Picker } from 'emoji-mart-vue-fast/src';
// import { EmojiIndex } from "emoji-mart-vue-fast/src";
// import data from "emoji-mart-vue-fast/data/apple.json";
// 注册全局dayjs
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import customParseFormat from 'dayjs/plugin/customParseFormat';
export async function registerThirdComp(app: App) {
//---------------------------------------------------------------------
// 注册 JVxeTable 组件
registerJVxeTable(app);
// 注册 JVxeTable 自定义组件
@ -20,20 +18,13 @@ export async function registerThirdComp(app: App) {
//---------------------------------------------------------------------
// 注册全局聊天表情包
app.component('Picker', Picker);
// let myEmojiIndex = new EmojiIndex(data, {
// function() {
// return true;
// },
// exclude:['recent','people','nature','foods','activity','places','objects','symbols','flags']
// });
// app.config.globalProperties.$globalEmojiIndex = myEmojiIndex;
// app.provide('$globalEmojiIndex', myEmojiIndex);
//---------------------------------------------------------------------
// 注册全局dayjs
dayjs.locale('zh-cn');
dayjs.extend(relativeTime);
dayjs.extend(customParseFormat);
app.config.globalProperties.$dayjs = dayjs;
app.provide('$dayjs', dayjs);
app.config.globalProperties.$dayjs = dayjs
app.provide('$dayjs', dayjs)
//---------------------------------------------------------------------
}

View File

@ -1,5 +1,5 @@
// github repo url
export const GITHUB_URL = 'https://github.com/jeecgboot/jeecgboot-vue3';
export const GITHUB_URL = 'https://github.com/jeecgboot/jeecg-boot';
// vue-Jeecg-admin-next-doc
export const DOC_URL = 'http://help.jeecg.com';

View File

@ -184,6 +184,7 @@ export const useMultipleTabStore = defineStore({
if (path !== tab.path) {
// Closed is not the activation tab
close(tab);
this.updateCacheTab();
return;
}

View File

@ -4,7 +4,7 @@ import { defineStore } from 'pinia';
import { store } from '/@/store';
import { RoleEnum } from '/@/enums/roleEnum';
import { PageEnum } from '/@/enums/pageEnum';
import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY, LOGIN_INFO_KEY, DB_DICT_DATA_KEY, TENANT_ID } from '/@/enums/cacheEnum';
import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY, LOGIN_INFO_KEY, DB_DICT_DATA_KEY, TENANT_ID, OAUTH2_THIRD_LOGIN_TENANT_ID } from '/@/enums/cacheEnum';
import { getAuthCache, setAuthCache, removeAuthCache } from '/@/utils/auth';
import { GetUserInfoModel, LoginParams, ThirdLoginParams } from '/@/api/sys/model/userModel';
import { doLogout, getUserInfo, loginApi, phoneLoginApi, thirdLogin } from '/@/api/sys/user';
@ -18,6 +18,7 @@ import { isArray } from '/@/utils/is';
import { useGlobSetting } from '/@/hooks/setting';
import { JDragConfigEnum } from '/@/enums/jeecgEnum';
import { useSso } from '/@/hooks/web/useSso';
import { isOAuth2AppEnv } from "/@/views/sys/login/useLogin";
interface UserState {
userInfo: Nullable<UserInfo>;
token?: string;
@ -26,6 +27,7 @@ interface UserState {
sessionTimeout?: boolean;
lastUpdateTime: number;
tenantid?: string | number;
shareTenantId?: Nullable<string | number>;
loginInfo?: Nullable<LoginInfo>;
}
@ -46,6 +48,9 @@ export const useUserStore = defineStore({
lastUpdateTime: 0,
//租户id
tenantid: '',
// 分享租户ID
// 用于分享页面所属租户与当前用户登录租户不一致的情况
shareTenantId: null,
//登录返回信息
loginInfo: null,
}),
@ -74,6 +79,10 @@ export const useUserStore = defineStore({
getTenant(): string | number {
return this.tenantid || getAuthCache<string | number>(TENANT_ID);
},
// 是否有分享租户id
hasShareTenantId(): boolean {
return this.shareTenantId != null && this.shareTenantId !== '';
},
},
actions: {
setToken(info: string | undefined) {
@ -101,6 +110,9 @@ export const useUserStore = defineStore({
this.tenantid = id;
setAuthCache(TENANT_ID, id);
},
setShareTenantId(id: NonNullable<typeof this.shareTenantId>) {
this.shareTenantId = id;
},
setSessionTimeout(flag: boolean) {
this.sessionTimeout = flag;
},
@ -169,6 +181,19 @@ export const useUserStore = defineStore({
//update-begin-author:liusq date:2022-5-5 for:登录成功后缓存拖拽模块的接口前缀
localStorage.setItem(JDragConfigEnum.DRAG_BASE_URL, useGlobSetting().domainUrl);
//update-end-author:liusq date:2022-5-5 for: 登录成功后缓存拖拽模块的接口前缀
// update-begin-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
let redirect = router.currentRoute.value?.query?.redirect as string;
// 判断是否有 redirect 重定向地址
//update-begin---author:wangshuai ---date:20230424 for【QQYUN-5195】登录之后直接刷新页面导致没有进入创建组织页面------------
if (redirect && goHome) {
//update-end---author:wangshuai ---date:20230424 for【QQYUN-5195】登录之后直接刷新页面导致没有进入创建组织页面------------
// 当前页面打开
window.open(redirect, '_self')
return;
}
// update-end-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
goHome && (await router.replace((userInfo && userInfo.homePath) || PageEnum.BASE_HOME));
}
return data;
@ -257,8 +282,25 @@ export const useUserStore = defineStore({
if (openSso == 'true') {
await useSso().ssoLoginOut();
}
//update-begin---author:wangshuai ---date:20230224 for[QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------
//退出登录的时候需要用的应用id
if(isOAuth2AppEnv()){
let tenantId = getAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID);
removeAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID);
goLogin && await router.push({ name:"Login",query:{ tenantId:tenantId }})
}else{
// update-begin-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
goLogin && (await router.push({
path: PageEnum.BASE_LOGIN,
query: {
// 传入当前的路由,登录成功后跳转到当前路由
redirect: router.currentRoute.value.fullPath,
}
}));
// update-end-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
goLogin && (await router.push(PageEnum.BASE_LOGIN));
}
//update-end---author:wangshuai ---date:20230224 for[QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------
},
/**
*

View File

@ -16,6 +16,7 @@ import {
DB_DICT_DATA_KEY,
TENANT_ID,
LOGIN_INFO_KEY,
OAUTH2_THIRD_LOGIN_TENANT_ID,
} from '/@/enums/cacheEnum';
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
import { toRaw } from 'vue';
@ -31,6 +32,7 @@ interface BasicStore {
[DB_DICT_DATA_KEY]: string;
[TENANT_ID]: string;
[LOGIN_INFO_KEY]: LoginInfo;
[OAUTH2_THIRD_LOGIN_TENANT_ID]: string
}
type LocalStore = BasicStore;

View File

@ -2,6 +2,9 @@ import { useGlobSetting } from '/@/hooks/setting';
import { merge, random } from 'lodash-es';
import { isArray } from '/@/utils/is';
import { FormSchema } from '/@/components/Form';
import { reactive } from "vue";
import { getTenantId, getToken } from "/@/utils/auth";
import { useUserStoreWithOut } from "/@/store/modules/user";
const globSetting = useGlobSetting();
const baseApiUrl = globSetting.domainUrl;
@ -365,3 +368,94 @@ export function checkChildrenHidden(menuTreeItem){
}
return menuTreeItem.children?.find((item) => item.hideMenu == false) != null;
}
/**
*
* @param fileSize
* @param unit
* @return
*/
export function calculateFileSize(fileSize, unit?) {
let unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
if (unit && unit.length > 0) {
unitArr = unit;
}
let size = fileSize;
let unitIndex = 0;
while (size >= 1024 && unitIndex < unitArr.length - 1) {
size /= 1024;
unitIndex++;
}
//保留两位小数,四舍五入
size = Math.round(size * 100) / 100;
return size + unitArr[unitIndex];
}
/**
* header
*/
export function getHeaders() {
let tenantId = getTenantId();
return reactive({
'X-Access-Token': getToken(),
'X-Tenant-Id': tenantId ? tenantId : '0',
});
}
/** 根据表达式获取相应的用户信息 */
export function getUserInfoByExpression(expression) {
if (!expression) {
return expression;
}
const userStore = useUserStoreWithOut();
let userInfo = userStore.getUserInfo;
if (userInfo) {
switch (expression) {
case 'sysUserId':
return userInfo.id;
// 当前登录用户登录账号
case 'sysUserCode':
case 'sys_user_code':
return userInfo.username;
// 当前登录用户真实名称
case 'sysUserName':
return userInfo.realname;
// 当前登录用户部门编号
case 'sysOrgCode':
case 'sys_org_code':
return userInfo.orgCode;
}
}
return expression;
}
/**
* #{xxx}
* @param expression
*/
export function replaceUserInfoByExpression(expression: string | any[]) {
if (!expression) {
return expression;
}
const isString = typeof expression === 'string';
const isArray = Array.isArray(expression)
if (!isString && !isArray) {
return expression;
}
const reg = /#{(.*?)}/g;
const replace = (str) => {
if (typeof str !== 'string') {
return str;
}
let result = str.match(reg);
if (result && result.length > 0) {
result.forEach((item) => {
let userInfo = getUserInfoByExpression(item.substring(2, item.length - 1));
str = str.replace(item, userInfo);
});
}
return str;
};
// @ts-ignore
return isString ? replace(expression) : expression.map(replace);
}

View File

@ -1,5 +1,5 @@
import { h } from 'vue';
import { Avatar, Tag, Tooltip } from 'ant-design-vue';
import { Avatar, Tag, Tooltip, Image } from 'ant-design-vue';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { Tinymce } from '/@/components/Tinymce';
import Icon from '/@/components/Icon';
@ -60,24 +60,26 @@ const render = {
*/
renderImage: ({ text }) => {
if (!text) {
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1084 【vue3】online表单测试发现的新问题 41、生成的代码树默认图大小未改
return h(
Avatar,
{ shape: 'square', size: 25 },
{
icon: () => h(Icon, { icon: 'ant-design:file-image-outlined', size: 25 }),
}
);
return h(Image, {
width: 30,
height: 30,
src: '',
fallback:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg==',
});
}
let avatarList = text.split(',');
return h(
'span',
avatarList.map((item) => {
return h(Avatar, {
return h(Image, {
src: getFileAccessHttpUrl(item),
shape: 'square',
size: 25,
width: 30,
height: 30,
style: { marginRight: '5px' },
previewMask: () => {
return h(Icon, { icon: 'ant-design:eye-outlined', size: 20 });
},
});
})
);

View File

@ -0,0 +1,65 @@
const whiteColor = '#ffffff'
const blackColor = '#666666'
export const Colors = [
// 背景颜色,文字颜色
['#2196F3', whiteColor],
['#08C9C9', whiteColor],
['#00C345', whiteColor],
['#FAD714', whiteColor],
['#FF9300', whiteColor],
['#F52222', whiteColor],
['#EB2F96', whiteColor],
['#7500EA', whiteColor],
['#2D46C4', whiteColor],
['#484848', whiteColor],
// --------------------
['#C9E6FC', blackColor],
['#C3F2F2', blackColor],
['#C2F1D2', blackColor],
['#FEF6C6', blackColor],
['#FFE5C2', blackColor],
['#FDCACA', blackColor],
['#FACDE6', blackColor],
['#DEC2FA', blackColor],
['#CCD2F1', blackColor],
['#D3D3D3', blackColor],
]
export const NONE_COLOR = ['#e9e9e9', blackColor]
/**
* 返回一个颜色迭代器每次调用返回一个颜色当颜色用完后再从头开始
* @param {number} initIndex 初始颜色索引
* @returns {{getIndex: function, next: function}}
*/
export function getColorIterator(initIndex = 0) {
let index = initIndex;
if (index < 0 || index >= Colors.length) {
index = 0;
}
return {
getIndex: () => index,
next() {
const color = Colors[index];
index = (index + 1) % Colors.length;
return color;
},
}
}
/**
* 根据颜色获取当前坐标和颜色
*/
export function getItemColor(color) {
if(!color){
return NONE_COLOR[1];
}
let colorIndex = Colors.findIndex((value)=>{
return value[0] === color;
})
if(colorIndex === -1){
return NONE_COLOR[1];
}
return Colors[colorIndex][1];
}

View File

@ -33,6 +33,7 @@ export default class signMd5Utils {
let jsonObj = this.mergeObject(urlParams, requestParams);
let requestBody = this.sortAsc(jsonObj);
delete requestBody._t;
console.log('sign requestBody:', requestBody);
return md5(JSON.stringify(requestBody) + signatureSecret).toUpperCase();
}

View File

@ -139,7 +139,7 @@ const transform: AxiosTransform = {
requestInterceptors: (config: Recordable, options) => {
// 请求之前处理config
const token = getToken();
let tenantid = getTenantId();
let tenantId: string | number = getTenantId();
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
// jwt token
config.headers.Authorization = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token;
@ -153,10 +153,20 @@ const transform: AxiosTransform = {
config.headers[ConfigEnum.Sign] = signMd5Utils.getSign(config.url, config.params);
//--update-end--author:liusq---date:20210831---for:将签名和时间戳,添加在请求接口 Header
//--update-begin--author:liusq---date:20211105---for: for:将多租户id添加在请求接口 Header
if (!tenantid) {
tenantid = 0;
if (!tenantId) {
tenantId = 0;
}
config.headers[ConfigEnum.TENANT_ID] = tenantid;
// update-begin--author:sunjianlei---date:220230428---for【QQYUN-5279】修复分享的应用租户和当前登录租户不一致时提示404的问题
const userStore = useUserStoreWithOut();
// 判断是否有临时租户id
if (userStore.hasShareTenantId && userStore.shareTenantId !== 0) {
// 临时租户id存在使用临时租户id
tenantId = userStore.shareTenantId!;
}
// update-end--author:sunjianlei---date:220230428---for【QQYUN-5279】修复分享的应用租户和当前登录租户不一致时提示404的问题
config.headers[ConfigEnum.TENANT_ID] = tenantId;
//--update-begin--author:liusq---date:20220325---for: 增加vue3标记
config.headers[ConfigEnum.VERSION] = 'v3';
//--update-end--author:liusq---date:20220325---for:增加vue3标记

View File

@ -104,6 +104,8 @@ export function cloneObject(obj) {
}
export const withInstall = <T>(component: T, alias?: string) => {
console.log("---初始化---", component)
const comp = component as any;
comp.install = (app: App) => {
app.component(comp.name || comp.displayName, component);

Some files were not shown because too many files have changed in this diff Show More