重大版本发布,1.3.0版本源码上传(全功能趋于稳定健壮)

pull/170/head
zhangdaiscott 2022-06-21 17:53:49 +08:00
parent 8b54e3be5e
commit 5aec20bfa0
139 changed files with 2834 additions and 811 deletions

1
.gitignore vendored
View File

@ -31,3 +31,4 @@ pnpm-debug.log*
/os_del.cmd
/.vscode/
/.history/
/svn clear.bat

View File

@ -1,6 +1,6 @@
{
"name": "jeecgboot-vue3",
"version": "1.2.0",
"version": "1.3.0",
"author": {
"name": "jeecg",
"email": "jeecgos@163.com",
@ -36,11 +36,12 @@
"dependencies": {
"@jeecg/online": "1.0.1",
"@iconify/iconify": "^2.0.4",
"@fullcalendar/core": "^5.10.1",
"@fullcalendar/daygrid": "^5.10.1",
"@fullcalendar/interaction": "^5.10.1",
"@fullcalendar/timegrid": "^5.10.1",
"@fullcalendar/vue3": "^5.10.1",
"@fullcalendar/core": "5.10.1",
"@fullcalendar/daygrid": "5.10.1",
"@fullcalendar/interaction": "5.10.1",
"@fullcalendar/resource-timeline": "5.10.1",
"@fullcalendar/timegrid": "5.10.1",
"@fullcalendar/vue3": "5.10.1",
"@vueuse/core": "^6.6.2",
"@zxcvbn-ts/core": "^1.0.0-beta.0",
"ant-design-vue": "2.2.8",
@ -66,6 +67,7 @@
"path-to-regexp": "^6.2.0",
"pinia": "2.0.0-rc.14",
"print-js": "^1.6.0",
"qiankun": "^2.5.1",
"qrcode": "^1.4.4",
"qrcodejs2": "0.0.2",
"resize-observer-polyfill": "^1.5.1",
@ -81,12 +83,11 @@
"vue-print-nb-jeecg": "^1.0.10",
"vue-router": "^4.0.12",
"vue-types": "^4.1.1",
"vuedraggable": "^4.1.0",
"vxe-table": "4.1.0",
"vxe-table-plugin-antd": "^3.0.3",
"xe-utils": "^3.3.1",
"xlsx": "^0.17.3",
"qiankun": "^2.5.1",
"vuedraggable": "^4.1.0",
"vue-json-pretty": "^2.0.4"
},
"devDependencies": {
@ -291,6 +292,7 @@
"vue-print-nb-jeecg/src/printarea",
"vue-router",
"vue-types",
"vuedraggable",
"vxe-table",
"vxe-table-plugin-antd",
"xe-utils",

View File

@ -8,6 +8,7 @@ module.exports = {
quoteProps: 'as-needed',
bracketSpacing: true,
trailingComma: 'es5',
jsxBracketSameLine: false,
jsxSingleQuote: false,
arrowParens: 'always',
insertPragma: false,

View File

@ -1,4 +1,5 @@
import { defHttp } from '/@/utils/http/axios';
import { message } from 'ant-design-vue';
import { useGlobSetting } from '/@/hooks/setting';
const globSetting = useGlobSetting();
const baseUploadUrl = globSetting.uploadUrl;
@ -95,3 +96,48 @@ export const loadCategoryData = (params) => {
export const uploadFile = (params, success) => {
return defHttp.uploadFile({ url: uploadUrl }, params, { success });
};
/**
*
* @param url
* @param fileName
* @param parameter
* @returns {*}
*/
export const downloadFile = (url, fileName?, parameter?) => {
return getFileblob(url, parameter).then((data) => {
if (!data || data.size === 0) {
message.warning('文件下载失败');
return;
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data]), fileName);
} else {
let url = window.URL.createObjectURL(new Blob([data]));
let link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(url); //释放掉blob对象
}
});
};
/**
* excel
* @param url
* @param parameter
* @returns {*}
*/
export const getFileblob = (url, parameter) => {
return defHttp.get(
{
url: url,
params: parameter,
responseType: 'blob',
},
{ isTransformResponse: false }
);
};

View File

@ -80,7 +80,7 @@ export function phoneLoginApi(params: LoginParams, mode: ErrorMessageMode = 'mod
export function getUserInfo() {
return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }).catch((e) => {
// update-begin--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
if (e && e.message.includes('timeout')) {
if (e && (e.message.includes('timeout') || e.message.includes('401'))) {
//接口不通时跳转到登录界面
const userStore = useUserStoreWithOut();
userStore.setToken('');

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,11 +1,11 @@
import { withInstall } from '/@/utils';
import type { ExtractPropTypes } from 'vue';
import button from './src/BasicButton.vue';
import uploadButton from './src/UploadButton.vue';
import jUploadButton from './src/JUploadButton.vue';
import popConfirmButton from './src/PopConfirmButton.vue';
import { buttonProps } from './src/props';
export const Button = withInstall(button);
export const UploadButton = withInstall(uploadButton);
export const JUploadButton = withInstall(jUploadButton);
export const PopConfirmButton = withInstall(popConfirmButton);
export declare type ButtonProps = Partial<ExtractPropTypes<typeof buttonProps>>;

View File

@ -0,0 +1,41 @@
<template>
<a-upload name="file" :showUploadList="false" :customRequest="(file) => onClick(file)">
<Button :type="type" :class="getButtonClass">
<template #default="data">
<Icon :icon="preIcon" v-if="preIcon" :size="iconSize" />
<slot v-bind="data || {}"></slot>
<Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
</template>
</Button>
</a-upload>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'JUploadButton',
inheritAttrs: false,
});
</script>
<script lang="ts" setup>
import { computed, unref } from 'vue';
import { Button } from 'ant-design-vue';
import Icon from '/@/components/Icon/src/Icon.vue';
import { buttonProps } from './props';
import { useAttrs } from '/@/hooks/core/useAttrs';
const props = defineProps(buttonProps);
// get component class
const attrs = useAttrs({ excludeDefaultKeys: false });
const getButtonClass = computed(() => {
const { color, disabled } = props;
return [
{
[`ant-btn-${color}`]: !!color,
[`is-disabled`]: disabled,
},
];
});
// get inherit binding value
const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
</script>

View File

@ -13,9 +13,9 @@ export const buttonProps = {
type: { type: String },
/**
* preIcon and postIcon icon size.
* @default: 14
* @default: 15
*/
iconSize: { type: Number, default: 14 },
iconSize: { type: Number, default: 15 },
isUpload: { type: Boolean, default: false },
onClick: { type: Function as PropType<(...args) => any>, default: null },
};

View File

@ -129,7 +129,10 @@
try {
setModalProps({ confirmLoading: true });
const result = await uploadApi({ name: 'file', file: blob, filename });
emit('uploadSuccess', { source: previewSource.value, data: result.data || result.message });
emit('uploadSuccess', {
source: previewSource.value,
data: result.data || result.message,
});
closeModal();
} finally {
setModalProps({ confirmLoading: false });

View File

@ -233,6 +233,7 @@
updateSchema,
resetSchema,
setProps,
getProps,
removeSchemaByFiled,
appendSchemaByField,
clearValidate,

View File

@ -42,7 +42,6 @@ import JUpload from './jeecg/components/JUpload/JUpload.vue';
import JSearchSelect from './jeecg/components/JSearchSelect.vue';
import JAddInput from './jeecg/components/JAddInput.vue';
import { Time } from '/@/components/Time';
import JOnlineSelectCascade from './jeecg/components/JOnlineSelectCascade.vue';
import JRangeNumber from './jeecg/components/JRangeNumber.vue';
const componentMap = new Map<ComponentType, Component>();
@ -110,7 +109,6 @@ componentMap.set('JSelectUserByDept', JSelectUserByDept);
componentMap.set('JUpload', JUpload);
componentMap.set('JSearchSelect', JSearchSelect);
componentMap.set('JAddInput', JAddInput);
componentMap.set('JOnlineSelectCascade', JOnlineSelectCascade);
componentMap.set('JRangeNumber', JRangeNumber);
export function add(compName: ComponentType, component: Component) {

View File

@ -5,13 +5,11 @@
<!-- update-begin-author:zyf Date:20211213 for调换按钮前后位置-->
<slot name="submitBefore"></slot>
<Button type="primary" class="mr-2" v-bind="getSubmitBtnOptions" @click="submitAction" v-if="showSubmitButton">
<Icon icon="ant-design:search-outlined"></Icon>
{{ getSubmitBtnOptions.text }}
</Button>
<slot name="resetBefore"></slot>
<Button type="default" class="mr-2" v-bind="getResetBtnOptions" @click="resetAction" v-if="showResetButton">
<Icon icon="ic:baseline-restart-alt"></Icon>
{{ getResetBtnOptions.text }}
</Button>
<!-- update-end-author:zyf Date:20211213 for调换按钮前后位置-->
@ -89,6 +87,7 @@
return Object.assign(
{
text: t('common.resetText'),
preIcon: 'ic:baseline-restart-alt',
},
props.resetButtonOptions
);
@ -96,8 +95,10 @@
const getSubmitBtnOptions = computed(() => {
return Object.assign(
{},
{
text: t('common.queryText'),
preIcon: 'ant-design:search-outlined',
},
props.submitButtonOptions
);

View File

@ -110,17 +110,18 @@ export function useForm(props?: Props): UseFormReturnType {
*/
validate: async (nameList?: NamePath[]): Promise<Recordable> => {
const form = await getForm();
let getProps = props || form.getProps;
let values = form.validate(nameList).then((values) => {
for (let key in values) {
if (values[key] instanceof Array) {
let valueType = getValueType(props, key);
let valueType = getValueType(getProps, key);
if (valueType === 'string') {
values[key] = values[key].join(',');
}
}
}
//--@updateBy-begin----author:liusq---date:20210916------for:处理区域事件字典信息------
return handleRangeValue(props, values);
return handleRangeValue(getProps, values);
//--@updateBy-end----author:liusq---date:20210916------for:处理区域事件字典信息------
});
return values;

View File

@ -2,7 +2,7 @@
<div v-for="(param, index) in dynamicInput.params" :key="index" style="display: flex">
<a-input placeholder="请输入参数key" v-model:value="param.label" style="width: 30%; margin-bottom: 5px" @input="emitChange" />
<a-input placeholder="请输入参数value" v-model:value="param.value" style="width: 30%; margin: 0 0 5px 5px" @input="emitChange" />
<MinusCircleOutlined v-if="dynamicInput.params.length > 1" class="dynamic-delete-button" @click="remove(param)" style="width: 50px"></MinusCircleOutlined>
<MinusCircleOutlined v-if="dynamicInput.params.length > min" class="dynamic-delete-button" @click="remove(param)" style="width: 50px"></MinusCircleOutlined>
</div>
<div>
<a-button type="dashed" style="width: 60%" @click="add">
@ -26,6 +26,10 @@
name: 'JAddInput',
props: {
value: propTypes.string.def(''),
//update-begin---author:wangshuai ---date:20220516 for[VUEN-1043]------------
//
min: propTypes.integer.def(1),
//update-end---author:wangshuai ---date:20220516 for[VUEN-1043]--------------
},
emits: ['change', 'update:value'],
setup(props, { emit }) {

View File

@ -1,5 +1,5 @@
<template>
<Cascader v-bind="attrs" v-model:value="state" :options="getOptions" @change="handleChange" />
<Cascader v-bind="attrs" :value="state" :options="getOptions" @change="handleChange" />
</template>
<script lang="ts">
import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted } from 'vue';
@ -40,6 +40,23 @@
return provinceAndCityDataPlus;
}
});
/**
* 监听value变化
*/
watchEffect(() => {
props.value && initValue();
});
/**
* 将字符串值转化为数组
*/
function initValue() {
let value = props.value ? props.value : [];
if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
state.value = value.split(',');
}
}
function handleChange(_, ...args) {
emitData.value = args;
console.info(emitData);

View File

@ -1,19 +1,19 @@
<template>
<div class="area-select">
<!--省份-->
<a-select v-model:value="province" @change="proChange" allowClear>
<a-select v-model:value="province" @change="proChange" allowClear :disabled="disabled">
<template v-for="item in provinceOptions" :key="`${item.value}`">
<a-select-option :value="item.value">{{ item.label }}</a-select-option>
</template>
</a-select>
<!--城市-->
<a-select v-if="level >= 2" v-model:value="city" @change="cityChange">
<a-select v-if="level >= 2" v-model:value="city" @change="cityChange" :disabled="disabled">
<template v-for="item in cityOptions" :key="`${item.value}`">
<a-select-option :value="item.value">{{ item.label }}</a-select-option>
</template>
</a-select>
<!--地区-->
<a-select v-if="level >= 3" v-model:value="area" @change="areaChange">
<a-select v-if="level >= 3" v-model:value="area" @change="areaChange" :disabled="disabled">
<template v-for="item in areaOptions" :key="`${item.value}`">
<a-select-option :value="item.value">{{ item.label }}</a-select-option>
</template>
@ -34,6 +34,7 @@
city: [String],
area: [String],
level: propTypes.number.def(3),
disabled: propTypes.bool.def(false),
},
emits: ['change', 'update:value'],
setup(props, { emit, refs }) {

View File

@ -96,7 +96,8 @@
},
},
});
let innerValue = '';
// props.value
let innerValue = props.value ?? '';
//
const isFullScreen = ref(false);
const fullScreenIcon = computed(() => (isFullScreen.value ? 'fullscreen-exit' : 'fullscreen'));
@ -175,6 +176,17 @@
isFullScreen.value = !isFullScreen.value;
}
//update-begin-author:taoyan date:2022-5-9 for: codeEditor
watch(
() => props.disabled,
(val) => {
if (coder) {
coder.setOption('readOnly', val);
}
}
);
//update-end-author:taoyan date:2022-5-9 for: codeEditor
const getBindValue = Object.assign({}, unref(props), unref(attrs));
return {
state,

View File

@ -122,7 +122,16 @@
const month = ref('*');
const week = ref('?');
const year = ref('*');
const inputValues = reactive({ second: '', minute: '', hour: '', day: '', month: '', week: '', year: '', cron: '' });
const inputValues = reactive({
second: '',
minute: '',
hour: '',
day: '',
month: '',
week: '',
year: '',
cron: '',
});
const preTimeList = ref('执行预览,会忽略年份参数。');
// cron

View File

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

View File

@ -0,0 +1,60 @@
<template>
<div :class="disabled ? 'jeecg-form-container-disabled' : ''">
<fieldset :disabled="disabled">
<slot name="detail"></slot>
</fieldset>
<slot name="edit"></slot>
<fieldset disabled>
<slot></slot>
</fieldset>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'JFormContainer',
props: {
disabled: {
type: Boolean,
default: false,
required: false,
},
},
});
</script>
<style lang="less" scoped>
.jeecg-form-container-disabled {
cursor: not-allowed;
fieldset[disabled] {
-ms-pointer-events: none;
pointer-events: none;
}
.ant-select {
-ms-pointer-events: none;
pointer-events: none;
}
.ant-upload-select {
display: none;
}
.ant-upload-list {
cursor: grabbing;
}
}
.jeecg-form-container-disabled fieldset[disabled] .ant-upload-list {
-ms-pointer-events: auto !important;
pointer-events: auto !important;
}
.jeecg-form-container-disabled .ant-upload-list-item-actions .anticon-delete,
.jeecg-form-container-disabled .ant-upload-list-item .anticon-close {
display: none;
}
</style>

View File

@ -18,7 +18,7 @@
<UploadOutlined v-else />
<div class="ant-upload-text">{{ text }}</div>
</div>
<a-button v-if="listType == 'picture'">
<a-button v-if="listType == 'picture'" :disabled="disabled">
<UploadOutlined></UploadOutlined>
{{ text }}
</a-button>

View File

@ -35,7 +35,7 @@
height: propTypes.number.def(150),
disabled: propTypes.bool.def(false),
// ID
popContainer: propTypes.string.def(''),
popContainer: propTypes.oneOfType([propTypes.string, propTypes.func]).def(''),
});
const attrs = useAttrs();
const emit = defineEmits(['change', 'update:value']);
@ -75,6 +75,8 @@
function getPopupContainer(node) {
if (!props.popContainer) {
return node.parentNode;
} else if (typeof props.popContainer === 'function') {
return props.popContainer(node);
} else {
return document.getElementById(props.popContainer);
}

View File

@ -1,220 +0,0 @@
<template>
<!-- 级联下拉框 form组件 暂且只在online使用 不对外提供api -->
<a-select :placeholder="placeholder" :value="selectedValue" @change="handleChange" allowClear style="width: 100%">
<a-select-option v-for="(item, index) in dictOptions" :key="index" :value="item.store">
<span style="display: inline-block; width: 100%" :title="item.label">{{ item.label }}</span>
</a-select-option>
</a-select>
</template>
<script lang="ts">
import { defineComponent, watch, ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
/**获取下拉选项*/
const SELECT_OPTIONS_URL = '/online/cgform/api/querySelectOptions';
export default defineComponent({
name: 'JOnlineSelectCascade',
props: {
table: { type: String, default: '' },
txt: { type: String, default: '' },
store: { type: String, default: '' },
idField: { type: String, default: '' },
pidField: { type: String, default: '' },
pidValue: { type: String, default: '-1' },
origin: { type: Boolean, default: false },
condition: { type: String, default: '' },
value: { type: String, default: '' },
isNumber: { type: Boolean, default: false },
placeholder: { type: String, default: '请选择' },
},
emits: ['change', 'next'],
setup(props, { emit }) {
const { createMessage: $message } = useMessage();
//
const selectedValue = ref<any>('');
//
const dictOptions = ref<any[]>([]);
const optionsLoad = ref(true);
//
function handleChange(value) {
console.log('handleChange', value);
// value id
let temp = value || '';
emit('change', temp);
valueChangeThenEmitNext(temp);
}
// condition
watch(
() => props.condition,
(val) => {
optionsLoad.value = true;
if (val) {
loadOptions();
}
},
{ immediate: true }
);
// pidValue
watch(
() => props.pidValue,
(val) => {
if (val === '-1') {
dictOptions.value = [];
} else {
loadOptions();
}
}
);
//
watch(
() => props.value,
(newVal, oldVal) => {
console.log('值改变事件', newVal, oldVal);
if (!newVal) {
// value--
selectedValue.value = [];
if (oldVal) {
// oldVal
emit('change', '');
emit('next', '-1');
}
} else {
// value
selectedValue.value = newVal;
}
if (newVal && !oldVal) {
// options
handleFirstValueSetting(newVal);
}
},
{ immediate: true }
);
/**
* 第一次加载赋值
*/
async function handleFirstValueSetting(value) {
if (props.idField === props.store) {
// id
emit('next', value);
} else {
if (props.origin === true) {
// optionsoptions
await getSelfOptions();
valueChangeThenEmitNext(value);
} else {
// value
let arr = await loadValueText();
valueChangeThenEmitNext(value, arr);
}
}
}
function loadOptions() {
let params = getQueryParams();
if (props.origin === true) {
params['condition'] = props.condition;
} else {
params['pidValue'] = props.pidValue;
}
console.log('请求参数', params);
dictOptions.value = [];
defHttp.get({ url: SELECT_OPTIONS_URL, params }, { isTransformResponse: false }).then((res) => {
if (res.success) {
dictOptions.value = [...res.result];
console.log('请求结果', res.result, dictOptions);
} else {
$message.warning('联动组件数据加载失败,请检查配置!');
}
});
}
function getQueryParams() {
let params = {
table: props.table,
txt: props.txt,
key: props.store,
idField: props.idField,
pidField: props.pidField,
};
return params;
}
function loadValueText() {
return new Promise((resolve) => {
if (!props.value) {
selectedValue.value = [];
resolve([]);
} else {
let params = getQueryParams();
if (props.isNumber === true) {
params['condition'] = `${props.store} = ${props.value}`;
} else {
params['condition'] = `${props.store} = '${props.value}'`;
}
defHttp.get({ url: SELECT_OPTIONS_URL, params }, { isTransformResponse: false }).then((res) => {
if (res.success) {
resolve(res.result);
} else {
$message.warning('联动组件数据加载失败,请检查配置!');
resolve([]);
}
});
}
});
}
/**
* 获取下拉选项
*/
function getSelfOptions() {
return new Promise((resolve) => {
let index = 0;
(function next(index) {
if (index > 10) {
resolve([]);
}
let arr = dictOptions.value;
if (arr && arr.length > 0) {
resolve(arr);
} else {
setTimeout(() => {
next(index++);
}, 300);
}
})(index);
});
}
/**
* 值改变后 需要往外抛事件 触发下级节点的选项改变
*/
function valueChangeThenEmitNext(value, arr: any = []) {
if (value && value.length > 0) {
if (!arr || arr.length == 0) {
arr = dictOptions.value;
}
let selected = arr.filter((item) => item.store === value);
if (selected && selected.length > 0) {
let id = selected[0].id;
emit('next', id);
}
}
}
return {
selectedValue,
dictOptions,
handleChange,
};
},
});
</script>
<style scoped></style>

View File

@ -6,9 +6,11 @@
<template #prefix>
<Icon icon="ant-design:cluster-outlined"></Icon>
</template>
<template #suffix>
<Icon icon="ant-design:close-circle-outlined" @click="handleEmpty" title="清空" v-if="showText"></Icon>
</template>
<!-- update-begin-author:taoyan date:2022-5-31 for: VUEN-1157 popup 选中后有两个清除图标后边这个清除只是把输入框中数据清除实际值并没有清除 -->
<!-- <template #suffix>
<Icon icon="ant-design:close-circle-outlined" @click="handleEmpty" title="清空" v-if="showText"></Icon>
</template>-->
<!-- update-begin-author:taoyan date:2022-5-31 for: VUEN-1157 popup 选中后有两个清除图标后边这个清除只是把输入框中数据清除实际值并没有清除 -->
</a-input>
<!--popup弹窗-->
<JPopupOnlReportModal @register="regModal" :code="code" :multi="multi" :sorter="sorter" :groupId="uniqGroupId" :param="param" @ok="callBack"></JPopupOnlReportModal>

View File

@ -117,15 +117,20 @@
options.value = [];
loading.value = true;
// codetable,text,code
defHttp.get({ url: `/sys/dict/loadDict/${props.dict}`, params: { keyword: value, pageSize: props.pageSize } }).then((res) => {
loading.value = false;
if (res && res.length > 0) {
if (currentLoad != unref(lastLoad)) {
return;
defHttp
.get({
url: `/sys/dict/loadDict/${props.dict}`,
params: { keyword: value, pageSize: props.pageSize },
})
.then((res) => {
loading.value = false;
if (res && res.length > 0) {
if (currentLoad != unref(lastLoad)) {
return;
}
options.value = res;
}
options.value = res;
}
});
});
}
/**
* 初始化value
@ -186,12 +191,17 @@
} else {
//
loading.value = true;
defHttp.get({ url: `/sys/dict/loadDict/${dict}`, params: { pageSize: pageSize, keyword: '' } }).then((res) => {
loading.value = false;
if (res && res.length > 0) {
options.value = res;
}
});
defHttp
.get({
url: `/sys/dict/loadDict/${dict}`,
params: { pageSize: pageSize, keyword: '' },
})
.then((res) => {
loading.value = false;
if (res && res.length > 0) {
options.value = res;
}
});
}
}
}

View File

@ -37,7 +37,7 @@
//
const selectOptions = ref<SelectTypes['options']>([]);
//
let selectValues = reactive<object>({
let selectValues = reactive<Recordable>({
value: [],
});
//
@ -57,14 +57,16 @@
*/
watchEffect(() => {
props.value && initValue();
// update-begin-author:taoyan date:20220401 for: resetFields
if (props.value === '' || props.value === undefined) {
state.value = [];
selectValues.value = [];
}
// update-end-author:taoyan date:20220401 for: resetFields
});
//update-begin-author:liusq---date:20220609--for: form ---
watch(
() => props.value,
() => {
initValue();
}
);
//update-end-author:liusq---date:20220609--for: form ---
/**
* 监听selectValues变化
*/
@ -100,6 +102,9 @@
if (value && typeof value === 'string') {
state.value = value.split(',');
selectValues.value = value.split(',');
} else {
// VUEN-857
selectValues.value = value;
}
}

View File

@ -1,6 +1,6 @@
<!--字典下拉多选-->
<template>
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder" allowClear :getPopupContainer="getParentContainer">
<a-select :value="arrayValue" @change="onChange" mode="multiple" :filter-option="filterOption" :disabled="disabled" :placeholder="placeholder" allowClear :getPopupContainer="getParentContainer">
<a-select-option v-for="(item, index) in dictOptions" :key="index" :getPopupContainer="getParentContainer" :value="item.value">
{{ item.text || item.label }}
</a-select-option>
@ -39,7 +39,7 @@
triggerChange: {
type: Boolean,
required: false,
default: false,
default: true,
},
spliter: {
type: String,
@ -55,6 +55,10 @@
type: String,
required: false,
},
disabled: {
type: Boolean,
default: false,
},
},
emits: ['options-change', 'change', 'input', 'update:value'],
setup(props, { emit, refs }) {
@ -103,7 +107,14 @@
// code
function loadDictOptions() {
getDictItems(props.dictCode).then((res) => {
//update-begin-author:taoyan date:2022-6-21 for:
let temp = props.dictCode || '';
if (temp.indexOf(',') > 0 && temp.indexOf(' ') > 0) {
//
temp = encodeURI(temp);
}
//update-end-author:taoyan date:2022-6-21 for:
getDictItems(temp).then((res) => {
if (res) {
dictOptions.value = res.map((item) => ({ value: item.value, label: item.text }));
//console.info('res', dictOptions.value);
@ -114,6 +125,12 @@
});
}
//update-begin-author:taoyan date:2022-5-31 for: VUEN-1145
function filterOption(input, option) {
return option.children[0].el.data.toLowerCase().indexOf(input.toLowerCase()) >= 0;
}
//update-end-author:taoyan date:2022-5-31 for: VUEN-1145
return {
state,
attrs,
@ -121,6 +138,7 @@
onChange,
arrayValue,
getParentContainer,
filterOption,
};
},
});

View File

@ -47,7 +47,7 @@
//
const selectOptions = ref<SelectTypes['options']>([]);
//
let selectValues = reactive<object>({
let selectValues = reactive<Recordable>({
value: [],
change: false,
});
@ -68,6 +68,10 @@
*/
watchEffect(() => {
props.value && initValue();
//
if (!props.value) {
selectValues.value = [];
}
});
/**
@ -97,6 +101,8 @@
if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
state.value = value.split(',');
selectValues.value = value.split(',');
} else {
selectValues.value = value;
}
}

View File

@ -48,7 +48,7 @@
//
const selectOptions = ref<SelectTypes['options']>([]);
//
let selectValues = reactive<object>({
let selectValues = reactive<Recordable>({
value: [],
change: false,
});
@ -102,6 +102,9 @@
if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
state.value = value.split(',');
selectValues.value = value.split(',');
} else {
// VUEN-857
selectValues.value = value;
}
}

View File

@ -1,6 +1,6 @@
<template>
<div :class="prefixCls">
<a-select v-if="query" :options="selectOptions" :disabled="disabled" style="width: 100%" v-bind="attrs" @change="onSelectChange" />
<a-select v-if="query" v-model:value="value" :options="selectOptions" :disabled="disabled" style="width: 100%" v-bind="attrs" @change="onSelectChange" />
<a-switch v-else v-model:checked="checked" :disabled="disabled" v-bind="attrs" @change="onSwitchChange" />
</div>
</template>

View File

@ -39,11 +39,14 @@
dict: propTypes.string.def('id'),
parentCode: propTypes.string.def(''),
pidField: propTypes.string.def('pid'),
pidValue: propTypes.string.def('0'),
//update-begin---author:wangshuai ---date:20220620 forJTreeSelectpidValue------------
pidValue: propTypes.string.def(''),
//update-end---author:wangshuai ---date:20220620 forJTreeSelectpidValue--------------
hasChildField: propTypes.string.def(''),
condition: propTypes.string.def(''),
multiple: propTypes.bool.def(false),
loadTriggleChange: propTypes.bool.def(false),
reload: propTypes.number.def(1),
});
const attrs = useAttrs();
const emit = defineEmits(['change', 'update:value']);
@ -75,6 +78,19 @@
{ deep: true, immediate: true }
);
//update-begin-author:taoyan date:2022-5-25 for: VUEN-1056 15online
watch(
() => props.reload,
async () => {
treeData.value = [];
await loadRoot();
},
{
immediate: false,
}
);
//update-end-author:taoyan date:2022-5-25 for: VUEN-1056 15online
/**
* 根据code获取下拉数据并回显
*/

View File

@ -367,6 +367,12 @@
display: none;
}
}
/* update-begin-author:taoyan date:2022-5-24 for:VUEN-1093详情界面 图片下载按钮显示不全*/
.upload-download-handler {
right: 6px !important;
}
/* update-end-author:taoyan date:2022-5-24 for:VUEN-1093详情界面 图片下载按钮显示不全*/
}
.ant-upload-list-item {

View File

@ -17,13 +17,15 @@
:open="false"
:disabled="disabled"
:options="options"
:maxTagCount="maxTagCount"
@change="handleChange"
style="width: 100%"
@click="!disabled && openModal(false)"
></a-select>
</a-col>
<a-col v-if="showButton" class="right">
<a-button type="primary" @click="openModal(true)" :disabled="disabled">选择</a-button>
<a-button v-if="buttonIcon" :preIcon="buttonIcon" type="primary" @click="openModal(true)" :disabled="disabled"></a-button>
<a-button v-else type="primary" @click="openModal(true)" :disabled="disabled">选择</a-button>
</a-col>
</a-row>
</div>
@ -52,6 +54,10 @@
},
//
loading: propTypes.bool.def(false),
// tag
maxTagCount: propTypes.number,
// buttonIcon
buttonIcon: propTypes.string.def(''),
},
emits: ['handleOpen', 'change'],
setup(props, { emit, refs }) {

View File

@ -1,7 +1,7 @@
<!--部门选择框-->
<template>
<div>
<BasicModal v-bind="$attrs" @register="register" title="部门选择" width="500px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="500px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<BasicTree
ref="treeRef"
:treeData="treeData"
@ -47,6 +47,11 @@
},
props: {
...treeProps,
//
modalTitle: {
type: String,
default: '部门选择',
},
},
emits: ['register', 'getSelectResult'],
setup(props, { emit, refs }) {
@ -55,7 +60,7 @@
const attrs = useAttrs();
const treeRef = ref<Nullable<TreeActionType>>(null);
const getBindValue = Object.assign({}, unref(props), unref(attrs));
const queryUrl = props.sync ? queryDepartTreeSync : queryTreeList;
const queryUrl = getQueryUrl();
const [{ visibleChange, checkedKeys, getCheckStrictly, getSelectTreeData, onCheck, onLoadData, treeData, checkALL, expandAll, onSelect }] = useTreeBiz(treeRef, queryUrl, getBindValue);
const searchInfo = ref(props.params);
const tree = ref([]);
@ -75,6 +80,12 @@
});
}
/** 获取查询数据方法 */
function getQueryUrl() {
let queryFn = props.sync ? queryDepartTreeSync : queryTreeList;
return (params) => queryFn(Object.assign({}, params, { primaryKey: props.primaryKey }));
}
return {
tree,
handleOk,

View File

@ -78,7 +78,9 @@
//BasicTable
BasicModal,
SearchFormItem: createAsyncComponent(() => import('/@/components/jeecg/OnLine/SearchFormItem.vue'), { loading: false }),
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
loading: true,
}),
},
props: ['multi', 'code', 'sorter', 'groupId', 'param'],
emits: ['ok', 'register'],
@ -101,7 +103,7 @@
const tableScroll = ref({ x: true });
const getBindValue = Object.assign({}, unref(props), unref(attrs));
const [
{ visibleChange, loadColumnsInfo, dynamicParamHandler, loadData, handleChangeInTable, combineRowKey, clickThenCheck, filterUnuseSelect },
{ visibleChange, loadColumnsInfo, dynamicParamHandler, loadData, handleChangeInTable, combineRowKey, clickThenCheck, filterUnuseSelect, getOkSelectRows },
{ visible, rowSelection, checkedKeys, selectRows, pagination, dataSource, columns, loading, title, iSorter, queryInfo, queryParam, dictOptions },
] = usePopBiz(getBindValue);
@ -149,6 +151,19 @@
}
});
//update-begin-author:taoyan date:2022-5-31 for: VUEN-1156 popup popup
watch(
() => pagination.current,
(current) => {
if (current) {
tableRef.value.setPagination({
current: current,
});
}
}
);
//update-end-author:taoyan date:2022-5-31 for: VUEN-1156 popup popup
function handleToggleSearch() {
toggleSearchStatus.value = !unref(toggleSearchStatus);
}
@ -174,7 +189,10 @@
createMessage.warning('至少选择一条记录');
return false;
}
emit('ok', unref(selectRows));
//update-begin-author:taoyan date:2022-5-31 for: VUEN-1155 popup
let rows = getOkSelectRows!();
emit('ok', rows);
//update-end-author:taoyan date:2022-5-31 for: VUEN-1155 popup
handleCancel();
}

View File

@ -1,7 +1,7 @@
<!--职务选择框-->
<template>
<div>
<BasicModal v-bind="$attrs" @register="register" title="职务选择" width="900px" wrapClassName="j-user-select-modal" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="900px" wrapClassName="j-user-select-modal" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<a-row>
<a-col :span="showSelected ? 18 : 24">
<BasicTable
@ -42,10 +42,17 @@
components: {
//BasicTable
BasicModal,
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
loading: true,
}),
},
props: {
...selectProps,
//
modalTitle: {
type: String,
default: '职务选择',
},
},
emits: ['register', 'getSelectResult'],
setup(props, { emit, refs }) {
@ -118,7 +125,13 @@
dataIndex: 'name',
width: 40,
},
{ title: '操作', dataIndex: 'action', align: 'center', width: 40, slots: { customRender: 'action' } },
{
title: '操作',
dataIndex: 'action',
align: 'center',
width: 40,
slots: { customRender: 'action' },
},
],
};
/**

View File

@ -1,7 +1,7 @@
<!--角色选择框-->
<template>
<div>
<BasicModal v-bind="$attrs" @register="register" title="角色选择" width="800px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="800px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<BasicTable
:columns="columns"
v-bind="config"
@ -29,10 +29,17 @@
components: {
//BasicTable
BasicModal,
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
loading: true,
}),
},
props: {
...selectProps,
//
modalTitle: {
type: String,
default: '角色选择',
},
},
emits: ['register', 'getSelectResult'],
setup(props, { emit, refs }) {
@ -44,7 +51,7 @@
canResize: false,
bordered: true,
size: 'small',
rowKey: 'id',
rowKey: unref(props).rowKey,
};
const getBindValue = Object.assign({}, unref(props), unref(attrs), config);
const [{ rowSelection, indexColumnProps, visibleChange, getSelectResult }] = useSelectBiz(getRoleList, getBindValue);

View File

@ -1,6 +1,6 @@
<!--通过部门选择用户-->
<template>
<BasicModal v-bind="$attrs" @register="register" title="用户选择" width="1200px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="1200px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<a-row :gutter="10">
<a-col :md="7" :sm="24">
<a-card :style="{ minHeight: '613px', overflow: 'auto' }">
@ -43,10 +43,17 @@
//BasicTable
BasicModal,
BasicTree,
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
loading: true,
}),
},
props: {
...selectProps,
//
modalTitle: {
type: String,
default: '部门用户选择',
},
},
emits: ['register', 'getSelectResult'],
setup(props, { emit, refs }) {

View File

@ -1,11 +1,13 @@
<!--用户选择框-->
<template>
<div>
<BasicModal v-bind="$attrs" @register="register" title="用户选择" width="900px" wrapClassName="j-user-select-modal" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="900px" wrapClassName="j-user-select-modal" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
<a-row>
<a-col :span="showSelected ? 18 : 24">
<BasicTable
ref="tableRef"
:columns="columns"
:scroll="tableScroll"
v-bind="getBindValue"
:useSearchForm="true"
:formConfig="formConfig"
@ -13,7 +15,11 @@
:searchInfo="searchInfo"
:rowSelection="rowSelection"
:indexColumnProps="indexColumnProps"
></BasicTable>
>
<!-- update-begin-author:taoyan date:2022-5-25 for: VUEN-1112一对多 用户选择 未显示选择条数及清空 -->
<template #tableTitle></template>
<!-- update-end-author:taoyan date:2022-5-25 for: VUEN-1112一对多 用户选择 未显示选择条数及清空 -->
</BasicTable>
</a-col>
<a-col :span="showSelected ? 6 : 0">
<BasicTable v-bind="selectedTable" :dataSource="selectRows" :useSearchForm="true" :formConfig="{ showActionButtonGroup: false, baseRowStyle: { minHeight: '40px' } }">
@ -41,15 +47,39 @@
components: {
//BasicTable
BasicModal,
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
loading: true,
}),
},
props: {
...selectProps,
//
modalTitle: {
type: String,
default: '选择用户',
},
},
emits: ['register', 'getSelectResult'],
setup(props, { emit, refs }) {
// update-begin-author:taoyan date:2022-5-24 for: VUEN-1086
const tableScroll = ref<any>({ x: false });
const tableRef = ref();
//
const [register, { closeModal }] = useModalInner();
const [register, { closeModal }] = useModalInner(() => {
if (window.innerWidth < 900) {
tableScroll.value = { x: 900 };
} else {
tableScroll.value = { x: false };
}
//update-begin-author:taoyan date:2022-6-2 for: VUEN-1112
setTimeout(() => {
if (tableRef.value) {
tableRef.value.setSelectedRowKeys(selectValues['value'] || []);
}
}, 800);
//update-end-author:taoyan date:2022-6-2 for: VUEN-1112
});
// update-end-author:taoyan date:2022-5-24 for: VUEN-1086
const attrs = useAttrs();
//
const config = {
@ -58,7 +88,7 @@
size: 'small',
};
const getBindValue = Object.assign({}, unref(props), unref(attrs), config);
const [{ rowSelection, visibleChange, indexColumnProps, getSelectResult, handleDeleteSelected, selectRows }] = useSelectBiz(getUserList, getBindValue);
const [{ rowSelection, visibleChange, selectValues, indexColumnProps, getSelectResult, handleDeleteSelected, selectRows }] = useSelectBiz(getUserList, getBindValue);
const searchInfo = ref(props.params);
//form
const formConfig = {
@ -71,6 +101,16 @@
xl: 6,
xxl: 6,
},
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1086 ---
actionColOptions: {
xs: 24,
sm: 8,
md: 8,
lg: 8,
xl: 8,
xxl: 8,
},
//update-end-author:taoyan date:2022-5-24 for: VUEN-1086 ---
schemas: [
{
label: '账号',
@ -133,7 +173,13 @@
dataIndex: 'realname',
width: 40,
},
{ title: '操作', dataIndex: 'action', align: 'center', width: 40, slots: { customRender: 'action' } },
{
title: '操作',
dataIndex: 'action',
align: 'center',
width: 40,
slots: { customRender: 'action' },
},
],
};
/**
@ -163,6 +209,8 @@
selectRows,
selectedTable,
handleDeleteSelected,
tableScroll,
tableRef,
};
},
});

View File

@ -5,7 +5,7 @@ export function useSelectBiz(getList, props) {
//接收下拉框选项
const selectOptions = inject('selectOptions', ref<Array<object>>([]));
//接收已选择的值
const selectValues = <object>inject('selectValues', reactive({}));
const selectValues = <object>inject('selectValues', reactive({ value: [], change: false }));
// 是否正在加载回显
const loadingEcho = inject<Ref<boolean>>('loadingEcho', ref(false));
//数据集
@ -62,7 +62,9 @@ export function useSelectBiz(getList, props) {
*
*/
const rowSelection = {
type: 'checkbox',
//update-begin-author:liusq---date:20220517--for: 动态设置rowSelection的type值,默认是'checkbox' ---
type: props.isRadioSelection ? 'radio' : 'checkbox',
//update-end-author:liusq---date:20220517--for: 动态设置rowSelection的type值,默认是'checkbox' ---
columnWidth: 20,
selectedRowKeys: checkedKeys,
onChange: onSelectChange,
@ -141,6 +143,20 @@ export function useSelectBiz(getList, props) {
selectRows.value = [];
}
return [
{ onSelectChange, getDataSource, visibleChange, selectOptions, selectValues, rowSelection, indexColumnProps, checkedKeys, selectRows, dataSource, getSelectResult, handleDeleteSelected, reset },
{
onSelectChange,
getDataSource,
visibleChange,
selectOptions,
selectValues,
rowSelection,
indexColumnProps,
checkedKeys,
selectRows,
dataSource,
getSelectResult,
handleDeleteSelected,
reset,
},
];
}

View File

@ -2,6 +2,13 @@
import { propTypes } from '/@/utils/propTypes';
export const selectProps = {
//是否多选
isRadioSelection: {
type: Boolean,
//update-begin---author:wangshuai ---date:20220527 for部门用户组件默认应该单选否则其他地方有问题------------
default: false,
//update-end---author:wangshuai ---date:20220527 for部门用户组件默认应该单选否则其他地方有问题--------------
},
//回传value字段名
rowKey: {
type: String,

View File

@ -1,5 +1,5 @@
import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface';
import type { VNode } from 'vue';
import type { VNode, ComputedRef } from 'vue';
import type { ButtonProps as AntdButtonProps } from '/@/components/Button';
import type { FormItem } from './formItem';
import type { ColEx, ComponentType } from './index';
@ -34,6 +34,7 @@ export interface FormActionType {
updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
setProps: (formProps: Partial<FormProps>) => Promise<void>;
getProps: ComputedRef<Partial<FormProps>>;
removeSchemaByFiled: (field: string | string[]) => Promise<void>;
appendSchemaByField: (schema: FormSchema, prefixField: string | undefined, first?: boolean | undefined) => Promise<void>;
validateFields: (nameList?: NamePath[]) => Promise<any>;
@ -189,6 +190,9 @@ export interface FormSchema {
dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[];
// 这个属性自定义的 用于自定义的业务 比如在表单打开的时候修改表单的禁用状态但是又不能重写componentProps因为他的内容太多了所以使用dynamicDisabled和buss实现
buss?: any;
}
export interface HelpComponentProps {
maxWidth: string;

View File

@ -141,5 +141,4 @@ export type ComponentType =
| 'JSearchSelect'
| 'JAddInput'
| 'Time'
| 'JOnlineSelectCascade'
| 'JRangeNumber';

View File

@ -98,6 +98,11 @@ const jeecgAreaData = new Area();
// 根据code找文本
const getAreaTextByCode = function (code) {
//update-begin-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串是的话获取最后一位的code ---
if (code && code.includes(',')) {
code = code.substr(code.lastIndexOf(',') + 1);
}
//update-end-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串是的话获取最后一位的code ---
return jeecgAreaData.getText(code);
};

View File

@ -58,7 +58,13 @@ export function handleRangeNumberValue(props, values) {
if (!field || !startNumberKey || !endNumberKey || !values[field]) {
continue;
}
const [startNumber, endNumber]: number[] = values[field];
//update-begin-author:taoyan date:2022-5-10 for: 用于数值的范围查询 数组格式的中间转换不知道哪里出了问题,这里会变成字符串,需要再强制转成数组
let temp = values[field];
if (typeof temp === 'string') {
temp = temp.split(',');
}
const [startNumber, endNumber]: number[] = temp;
//update-end-author:taoyan date:2022-5-10 for: 用于数值的范围查询 数组格式的中间转换不知道哪里出了问题,这里会变成字符串,需要再强制转成数组
values[startNumberKey] = startNumber;
values[endNumberKey] = endNumber;
Reflect.deleteProperty(values, field);

View File

@ -1,5 +1,5 @@
<template>
<a-input disabled :style="{ width }" :placeholder="t('component.icon.placeholder')" :class="prefixCls" v-model:value="currentSelect">
<a-input :disabled="disabled" :style="{ width }" :placeholder="t('component.icon.placeholder')" :class="prefixCls" v-model:value="currentSelect">
<template #addonAfter>
<a-popover placement="bottomLeft" trigger="click" v-model="visible" :overlayClassName="`${prefixCls}-popover`">
<template #title>
@ -88,6 +88,7 @@
pageSize: propTypes.number.def(140),
copy: propTypes.bool.def(false),
mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
disabled: propTypes.bool.def(true),
});
const emit = defineEmits(['change', 'update:value']);

View File

@ -23,7 +23,9 @@
<!-- <a-icon style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>-->
<!-- </span>-->
<!-- </div>-->
<JSelectDept :value="selectedValue" :showButton="false" v-bind="cellProps" @change="handleChange" />
<div :class="[prefixCls]">
<JSelectDept :value="selectedValue" :maxTagCount="1" :showButton="false" v-bind="cellProps" @change="handleChange" />
</div>
</template>
<script lang="ts">
@ -32,8 +34,6 @@
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
// import { isArray, isEmpty, isString } from '/@/utils/is'
// import JSelectDepartModal from '@/components/jeecgbiz/modal/JSelectDepartModal'
import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';
import { isArray, isEmpty, isString } from '/@/utils/is';
@ -43,32 +43,24 @@
components: { JSelectDept },
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { innerValue, cellProps, handleChangeCommon } = useJVxeComponent(props);
// const selectedValue = computed(() => {
// let val: any = innerValue.value
// if (isEmpty(val)) {
// return []
// }
// if (isArray(val)) {
// return val
// }
// if (isString(val)) {
// // @ts-ignore
// return val.split(',')
// }
// return [val]
// })
const { innerValue, cellProps, handleChangeCommon, useCellDesign } = useJVxeComponent(props);
const { prefixCls } = useCellDesign('depart-select');
const selectedValue = computed(() => {
let val = innerValue.value;
let val: any = innerValue.value;
if (val == null) {
return val;
}
if (isEmpty(val)) {
return '';
return [];
}
if (isArray(val)) {
return (<any>val).join(',');
return val;
}
return val;
if (isString(val)) {
return (<string>val).split(',');
}
return [val];
});
const multiple = computed(() => cellProps.value['multi'] != false);
@ -78,6 +70,7 @@
}
return {
prefixCls,
selectedValue,
multiple,
cellProps,
@ -191,4 +184,14 @@
});
</script>
<style scoped></style>
<style lang="less">
// noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-vxe-cell-depart-select';
.@{prefix-cls} {
// tag100px
.ant-select .ant-select-selection-overflow-item {
max-width: 100px;
}
}
</style>

View File

@ -1,5 +1,7 @@
<template>
<JSelectUser :value="selectedValue" :showButton="false" v-bind="cellProps" @change="handleChange" />
<div :class="[prefixCls]">
<JSelectUser :value="selectedValue" :maxTagCount="1" :showButton="false" v-bind="cellProps" @change="handleChange" />
</div>
</template>
<script lang="ts">
@ -15,10 +17,14 @@
components: { JSelectUser },
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { innerValue, cellProps, handleChangeCommon } = useJVxeComponent(props);
const { innerValue, cellProps, handleChangeCommon, useCellDesign } = useJVxeComponent(props);
const { prefixCls } = useCellDesign('user-select');
const selectedValue = computed(() => {
let val: any = innerValue.value;
if (val == null) {
return val;
}
if (isEmpty(val)) {
return [];
}
@ -39,6 +45,7 @@
}
return {
prefixCls,
selectedValue,
multiple,
cellProps,
@ -66,4 +73,14 @@
});
</script>
<style scoped></style>
<style lang="less">
// noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-vxe-cell-user-select';
.@{prefix-cls} {
// tag100px
.ant-select .ant-select-selection-overflow-item {
max-width: 100px;
}
}
</style>

View File

@ -10,6 +10,8 @@
import { useModalContext } from '../../Modal';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { getToken } from '/@/utils/auth';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
@ -73,6 +75,34 @@
}
return lang;
});
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1090 markdown
const uploadUrl = `${window._CONFIG['domianURL']}/sys/common/upload`;
const token = getToken();
function formatResult(files, responseText): string {
let data: any = JSON.parse(responseText);
// {"success":true,"message":"markdown/aa_1653391146501.png","code":0,"result":null,"timestamp":1653391146501}'
let filename = files[0].name as string;
let result = {
msg: '',
code: 0,
data: {
errFiles: [''],
succMap: {},
},
};
if (data.success) {
result.data.errFiles = [];
result.data.succMap = {
[data.message]: getFileAccessHttpUrl(data.message),
};
} else {
result.code = 1;
result.msg = data.message;
result.data.errFiles = [filename];
}
return JSON.stringify(result);
}
//update-end-author:taoyan date:2022-5-24 for: VUEN-1090 markdown
function init() {
const wrapEl = unref(wrapRef) as HTMLElement;
if (!wrapEl) return;
@ -87,6 +117,22 @@
preview: {
actions: [],
},
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1090 markdown
upload: {
accept: 'image/*',
url: uploadUrl,
fieldName: 'file',
extraData: { biz: 'markdown' },
setHeaders() {
return {
'X-Access-Token': token as string,
};
},
format(files, response) {
return formatResult(files, response);
},
},
//update-end-author:taoyan date:2022-5-24 for: VUEN-1090 markdown
input: (v) => {
valueRef.value = v;
emit('update:value', v);

View File

@ -109,13 +109,15 @@
}
);
async function handleMenuClick({ key }: { key: string; keyPath: string[] }) {
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online
async function handleMenuClick({ item, key }: { item: any; key: string; keyPath: string[] }) {
const { beforeClickFn } = props;
if (beforeClickFn && isFunction(beforeClickFn)) {
const flag = await beforeClickFn(key);
if (!flag) return;
}
emit('menuClick', key);
emit('menuClick', key, item);
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online
isClickGo.value = true;
// const parentPath = await getCurrentParentPath(key);

View File

@ -1,5 +1,5 @@
<template>
<MenuItem :key="item.path">
<MenuItem :key="item.path" :title="item.title">
<MenuItemContent v-bind="$props" :item="item" />
</MenuItem>
</template>

View File

@ -24,6 +24,8 @@
import { useMenuItem } from './useMenu';
import { Tooltip } from 'ant-design-vue';
import { useSimpleRootMenuContext } from './useSimpleMenuContext';
import { useLocaleStore } from '/@/store/modules/locale';
export default defineComponent({
name: 'MenuItem',
components: { Tooltip },
@ -36,6 +38,7 @@
},
setup(props, { slots }) {
const instance = getCurrentInstance();
const localeStore = useLocaleStore();
const active = ref(false);
@ -92,6 +95,8 @@
}
});
//
storePathTitle(props.name);
rootMenuEmitter.emit('on-update-active-name:submenu', uidList);
} else {
active.value = false;
@ -100,6 +105,22 @@
{ immediate: true }
);
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online
function storePathTitle(path) {
console.log('storePathTitle', path);
let title = '';
if (instance!.attrs) {
let item: any = instance!.attrs.item;
if (item) {
title = item.title;
}
}
if (localeStore) {
localeStore.setPathTitle(path, title);
}
}
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online
return { getClass, prefixCls, getItemStyle, getCollapse, handleClickItem, showTooptip };
},
});

View File

@ -1,16 +1,22 @@
<template>
<div :class="[prefixCls, getAlign]" @click="onCellClick">
<template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
<Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
<PopConfirmButton v-bind="action">
<template v-if="action.slot">
<slot name="customButton"></slot>
</template>
<template v-else>
<Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
<PopConfirmButton v-bind="action">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
</Tooltip>
<PopConfirmButton v-else v-bind="action">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
</Tooltip>
<PopConfirmButton v-else v-bind="action">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
</template>
<Divider type="vertical" class="action-divider" v-if="divider && index < getActions.length - 1" />
</template>
<Dropdown :trigger="['hover']" :dropMenuList="getDropdownList" popconfirm v-if="dropDownActions && getDropdownList.length > 0">
@ -93,21 +99,21 @@
});
const getDropdownList = computed((): any[] => {
return (toRaw(props.dropDownActions) || [])
.filter((action) => {
return hasPermission(action.auth) && isIfShow(action);
})
.map((action, index) => {
const { label, popConfirm } = action;
return {
...action,
...popConfirm,
onConfirm: popConfirm?.confirm,
onCancel: popConfirm?.cancel,
text: label,
divider: index < props.dropDownActions.length - 1 ? props.divider : false,
};
});
//dropdown,线
const list = (toRaw(props.dropDownActions) || []).filter((action) => {
return hasPermission(action.auth) && isIfShow(action);
});
return list.map((action, index) => {
const { label, popConfirm } = action;
return {
...action,
...popConfirm,
onConfirm: popConfirm?.confirm,
onCancel: popConfirm?.cancel,
text: label,
divider: index < list.length - 1 ? props.divider : false,
};
});
});
const getAlign = computed(() => {

View File

@ -18,7 +18,7 @@
<template #content>
<TableSetting mode="mobile" :setting="tableSetting" v-if="showTableSetting" @columns-change="handleColumnChange" />
</template>
<a-button :class="`${prefixCls}__toolbar-mobile`" type="text" preIcon="ant-design:menu" shape="circle" />
<a-button :class="`${prefixCls}__toolbar-mobile`" v-if="showTableSetting" type="text" preIcon="ant-design:menu" shape="circle" />
</a-popover>
</div>
</div>

View File

@ -3,7 +3,14 @@
<template #title>
<span>{{ t('component.table.settingColumn') }}</span>
</template>
<Popover placement="bottomLeft" trigger="click" @visibleChange="handleVisibleChange" :overlayClassName="`${prefixCls}__cloumn-list`" :getPopupContainer="getPopupContainer">
<Popover
v-model:visible="popoverVisible"
placement="bottomLeft"
trigger="click"
@visibleChange="handleVisibleChange"
:overlayClassName="`${prefixCls}__cloumn-list`"
:getPopupContainer="getPopupContainer"
>
<template #title>
<div :class="`${prefixCls}__popover-title`">
<Checkbox :indeterminate="indeterminate" v-model:checked="checkAll" @change="onCheckAllChange">
@ -21,10 +28,6 @@
<!-- >-->
<!-- {{ t('component.table.settingSelectColumnShow') }}-->
<!-- </Checkbox>-->
<a-button size="small" type="link" @click="reset">
{{ t('common.resetText') }}
</a-button>
</div>
</template>
@ -33,7 +36,7 @@
<CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef">
<template v-for="item in plainOptions" :key="item.value">
<div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)">
<DragOutlined class="table-coulmn-drag-icon" />
<DragOutlined class="table-column-drag-icon" />
<Checkbox :value="item.value">
{{ item.label }}
</Checkbox>
@ -75,6 +78,12 @@
</template>
</CheckboxGroup>
</ScrollContainer>
<div :class="`${prefixCls}__popover-footer`">
<a-button size="small" @click="reset">
{{ t('common.resetText') }}
</a-button>
<a-button size="small" type="primary" @click="saveSetting"> </a-button>
</div>
</template>
<SettingOutlined />
</Popover>
@ -89,14 +98,18 @@
import { ScrollContainer } from '/@/components/Container';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import { useColumnsCache } from '../../hooks/useColumnsCache';
import { useDesign } from '/@/hooks/web/useDesign';
import { useSortable } from '/@/hooks/web/useSortable';
// import { useSortable } from '/@/hooks/web/useSortable';
import { isFunction, isNullAndUnDef } from '/@/utils/is';
import { getPopupContainer as getParentContainer } from '/@/utils';
import { omit } from 'lodash-es';
import { cloneDeep, omit } from 'lodash-es';
import Sortablejs from 'sortablejs';
import type Sortable from 'sortablejs';
interface State {
checkAll: boolean;
isInit?: boolean;
checkedList: string[];
defaultCheckList: string[];
}
@ -128,12 +141,13 @@
setup(props, { emit, attrs }) {
const { t } = useI18n();
const table = useTableContext();
const popoverVisible = ref(false);
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
let inited = false;
const cachePlainOptions = ref<Options[]>([]);
const plainOptions = ref<Options[]>([]);
const plainOptions = ref<Options[] | any>([]);
const plainSortOptions = ref<Options[]>([]);
@ -162,9 +176,26 @@
return obj;
});
let sortable: Sortable;
const sortableOrder = ref<string[]>();
//
const { saveSetting, resetSetting } = useColumnsCache(
{
state,
popoverVisible,
plainOptions,
plainSortOptions,
sortableOrder,
checkIndex,
},
setColumns,
handleColumnFixed
);
watchEffect(() => {
const columns = table.getColumns();
if (columns.length) {
if (columns.length && !state.isInit) {
init();
}
});
@ -217,6 +248,7 @@
}
});
}
state.isInit = true;
state.checkedList = checkList;
}
@ -234,16 +266,15 @@
const indeterminate = computed(() => {
const len = plainOptions.value.length;
let checkdedLen = state.checkedList.length;
unref(checkIndex) && checkdedLen--;
return checkdedLen > 0 && checkdedLen < len;
let checkedLen = state.checkedList.length;
unref(checkIndex) && checkedLen--;
return checkedLen > 0 && checkedLen < len;
});
// Trigger when check/uncheck a column
function onChange(checkedList: string[]) {
const len = plainOptions.value.length;
const len = plainSortOptions.value.length;
state.checkAll = checkedList.length === len;
const sortList = unref(plainSortOptions).map((item) => item.value);
checkedList.sort((prev, next) => {
return sortList.indexOf(prev) - sortList.indexOf(next);
@ -258,6 +289,10 @@
plainOptions.value = unref(cachePlainOptions);
plainSortOptions.value = unref(cachePlainOptions);
setColumns(table.getCacheColumns());
if (sortableOrder.value) {
sortable.sort(sortableOrder.value);
}
resetSetting();
}
// Open the pop-up window for drag and drop initialization
@ -269,15 +304,18 @@
const el = columnListEl.$el as any;
if (!el) return;
// Drag and drop sort
const { initSortable } = useSortable(el, {
handle: '.table-coulmn-drag-icon ',
sortable = Sortablejs.create(unref(el), {
animation: 500,
delay: 400,
delayOnTouchOnly: true,
handle: '.table-column-drag-icon ',
onEnd: (evt) => {
const { oldIndex, newIndex } = evt;
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
return;
}
// Sort column
const columns = getColumns();
const columns = cloneDeep(plainSortOptions.value);
if (oldIndex > newIndex) {
columns.splice(newIndex, 0, columns[oldIndex]);
@ -288,11 +326,13 @@
}
plainSortOptions.value = columns;
plainOptions.value = columns;
setColumns(columns);
},
});
initSortable();
// order
if (!sortableOrder.value) {
sortableOrder.value = sortable.toArray();
}
inited = true;
});
}
@ -325,13 +365,13 @@
if (isFixed && !item.width) {
item.width = 100;
}
table.setCacheColumnsByField?.(item.dataIndex, { fixed: isFixed });
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
setColumns(columns);
}
function setColumns(columns: BasicColumn[] | string[]) {
table.setColumns(columns);
const data: ColumnChangeParam[] = unref(plainOptions).map((col) => {
const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
const visible = columns.findIndex((c: BasicColumn | string) => c === col.value || (typeof c !== 'string' && c.dataIndex === col.value)) !== -1;
return { dataIndex: col.value, fixed: col.fixed, visible };
});
@ -347,11 +387,13 @@
getBindProps,
t,
...toRefs(state),
popoverVisible,
indeterminate,
onCheckAllChange,
onChange,
plainOptions,
reset,
saveSetting,
prefixCls,
columnListRef,
handleVisibleChange,
@ -369,7 +411,7 @@
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-column-setting';
.table-coulmn-drag-icon {
.table-column-drag-icon {
margin: 0 5px;
cursor: move;
}
@ -382,6 +424,19 @@
justify-content: space-between;
}
/* 卡片底部样式 */
&__popover-footer {
position: relative;
top: 7px;
text-align: right;
padding: 4px 0 0;
border-top: 1px solid #f0f0f0;
.ant-btn {
margin-right: 6px;
}
}
&__check-item {
display: flex;
align-items: center;

View File

@ -188,11 +188,13 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPagination
}
});
}
// update-begin--author:sunjianlei---date:20220523---for: 【VUEN-1089】合并vben最新版代码解决表格字段排序问题
/**
* set columns
* @param columnList keycolumn
*/
function setColumns(columnList: Partial<BasicColumn>[] | string[]) {
function setColumns(columnList: Partial<BasicColumn>[] | (string | string[])[]) {
const columns = cloneDeep(columnList);
if (!isArray(columns)) return;
@ -205,34 +207,27 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPagination
const cacheKeys = cacheColumns.map((item) => item.dataIndex);
if (!isString(firstColumn)) {
if (!isString(firstColumn) && !isArray(firstColumn)) {
columnsRef.value = columns as BasicColumn[];
} else {
const columnKeys = columns as string[];
const columnKeys = (columns as (string | string[])[]).map((m) => m.toString());
const newColumns: BasicColumn[] = [];
cacheColumns.forEach((item) => {
if (columnKeys.includes(item.dataIndex! || (item.key as string))) {
newColumns.push({
...item,
defaultHidden: false,
});
} else {
newColumns.push({
...item,
defaultHidden: true,
});
}
newColumns.push({
...item,
defaultHidden: !columnKeys.includes(item.dataIndex?.toString() || (item.key as string)),
});
});
// Sort according to another array
if (!isEqual(cacheKeys, columns)) {
newColumns.sort((prev, next) => {
return cacheKeys.indexOf(prev.dataIndex as string) - cacheKeys.indexOf(next.dataIndex as string);
return columnKeys.indexOf(prev.dataIndex?.toString() as string) - columnKeys.indexOf(next.dataIndex?.toString() as string);
});
}
columnsRef.value = newColumns;
}
}
// update-end--author:sunjianlei---date:20220523---for: 【VUEN-1089】合并vben最新版代码解决表格字段排序问题
function getColumns(opt?: GetColumnsParams) {
const { ignoreIndex, ignoreAction, sort } = opt || {};

View File

@ -0,0 +1,137 @@
import { computed, nextTick, unref, watchEffect } from 'vue';
import { router } from '/@/router';
import { createLocalStorage } from '/@/utils/cache';
import { useTableContext } from './useTableContext';
import { useMessage } from '/@/hooks/web/useMessage';
/**
*
*/
export function useColumnsCache(opt, setColumns, handleColumnFixed) {
let isInit = false;
const table = useTableContext();
const $ls = createLocalStorage();
const { createMessage: $message } = useMessage();
// 列表配置缓存key
const cacheKey = computed(() => {
let { fullPath } = router.currentRoute.value;
let key = fullPath.replace(/[\/\\]/g, '_');
let cacheKey = table.getBindValues.value.tableSetting?.cacheKey;
if (cacheKey) {
key += ':' + cacheKey;
}
return 'columnCache:' + key;
});
watchEffect(() => {
const columns = table.getColumns();
if (columns.length) {
init();
}
});
async function init() {
if (isInit) {
return;
}
isInit = true;
let columnCache = $ls.get(cacheKey.value);
if (columnCache && columnCache.checkedList) {
const { checkedList, sortedList, sortableOrder, checkIndex } = columnCache;
await nextTick();
// checkbox的排序缓存
opt.sortableOrder.value = sortableOrder;
// checkbox的选中缓存
opt.state.checkedList = checkedList;
// tableColumn的排序缓存
opt.plainSortOptions.value.sort((prev, next) => {
return sortedList.indexOf(prev.value) - sortedList.indexOf(next.value);
});
// 重新排序tableColumn
checkedList.sort((prev, next) => sortedList.indexOf(prev) - sortedList.indexOf(next));
// 是否显示行号列
if (checkIndex) {
table.setProps({ showIndexColumn: true });
}
setColumns(checkedList);
// 设置固定列
setColumnFixed(columnCache);
}
}
/** 设置被固定的列 */
async function setColumnFixed(columnCache) {
const { fixedColumns } = columnCache;
const columns = opt.plainOptions.value;
for (const column of columns) {
let fixedCol = fixedColumns.find((fc) => fc.key === (column.key || column.dataIndex));
if (fixedCol) {
await nextTick();
handleColumnFixed(column, fixedCol.fixed);
}
}
}
// 判断列固定状态
const fixedReg = /^(true|left|right)$/;
/** 获取被固定的列 */
function getFixedColumns() {
let fixedColumns: any[] = [];
const columns = opt.plainOptions.value;
for (const column of columns) {
if (fixedReg.test((column.fixed ?? '').toString())) {
fixedColumns.push({
key: column.key || column.dataIndex,
fixed: column.fixed === true ? 'left' : column.fixed,
});
}
}
return fixedColumns;
}
/** 保存列配置 */
function saveSetting() {
const { checkedList } = opt.state;
const sortedList = unref(opt.plainSortOptions).map((item) => item.value);
$ls.set(cacheKey.value, {
// 保存的列
checkedList,
// 排序后的列
sortedList,
// 是否显示行号列
checkIndex: unref(opt.checkIndex),
// checkbox原始排序
sortableOrder: unref(opt.sortableOrder),
// 固定列
fixedColumns: getFixedColumns(),
});
$message.success('保存成功');
// 保存之后直接关闭
opt.popoverVisible.value = false;
}
/** 重置(删除)列配置 */
async function resetSetting() {
// 重置固定列
await resetFixedColumn();
$ls.remove(cacheKey.value);
$message.success('重置成功');
}
async function resetFixedColumn() {
const columns = opt.plainOptions.value;
for (const column of columns) {
column.fixed;
if (fixedReg.test((column.fixed ?? '').toString())) {
await nextTick();
handleColumnFixed(column, null);
}
}
}
return {
saveSetting,
resetSetting,
};
}

View File

@ -48,7 +48,8 @@ export function useTableHeader(propsRef: ComputedRef<BasicTableProps>, slots: Sl
tableTop: () => getSlot(slots, 'tableTop'),
}
: {}),
//添加tableTop插槽
// 添加alertAfter插槽
...(slots.alertAfter ? { alertAfter: () => getSlot(slots, 'alertAfter') } : {}),
}
),
};

View File

@ -127,9 +127,15 @@ export interface FetchSetting {
}
export interface TableSetting {
// 是否显示刷新按钮
redo?: boolean;
// 是否显示尺寸调整按钮
size?: boolean;
// 是否显示字段调整按钮
setting?: boolean;
// 缓存“字段调整”配置的key用于页面上有多个表格需要区分的情况
cacheKey?: string;
// 是否显示全屏按钮
fullScreen?: boolean;
}

View File

@ -66,7 +66,7 @@
},
toolbar: {
type: Array as PropType<string[]>,
type: [Array as PropType<string[]>, String],
default: toolbar,
},
plugins: {
@ -74,7 +74,7 @@
default: plugins,
},
menubar: {
type: Object,
type: [Object, String],
default: menubar,
},
modelValue: {

View File

@ -1,6 +1,6 @@
<template>
<div :class="[prefixCls, { fullscreen }]">
<Upload name="file" multiple @change="handleChange" :action="uploadUrl" :showUploadList="false" accept=".jpg,.jpeg,.gif,.png,.webp">
<Upload name="file" multiple @change="handleChange" :action="uploadUrl" :showUploadList="false" :data="getBizData()" :headers="getheader()" accept=".jpg,.jpeg,.gif,.png,.webp">
<a-button type="primary" v-bind="{ ...getButtonProps }">
{{ t('component.upload.imgUpload') }}
</a-button>
@ -14,6 +14,8 @@
import { useDesign } from '/@/hooks/web/useDesign';
import { useGlobSetting } from '/@/hooks/setting';
import { useI18n } from '/@/hooks/web/useI18n';
import { getToken } from '/@/utils/auth';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
export default defineComponent({
name: 'TinymceImageUpload',
@ -31,7 +33,20 @@
setup(props, { emit }) {
let uploading = false;
const { uploadUrl } = useGlobSetting();
//update-begin-author:taoyan date:2022-5-13 for:
function getheader() {
return { 'X-Access-Token': getToken() };
}
function getBizData() {
return {
biz: 'jeditor',
jeditor: '1',
};
}
const { domainUrl } = useGlobSetting();
const uploadUrl = domainUrl + '/sys/common/upload';
//update-end-author:taoyan date:2022-5-13 for:
const { t } = useI18n();
const { prefixCls } = useDesign('tinymce-img-upload');
@ -45,7 +60,7 @@
function handleChange(info: Recordable) {
const file = info.file;
const status = file?.status;
const url = file?.response?.url;
//const url = file?.response?.url;
const name = file?.name;
if (status === 'uploading') {
@ -54,7 +69,10 @@
uploading = true;
}
} else if (status === 'done') {
emit('done', name, url);
//update-begin-author:taoyan date:2022-5-13 for:
let realUrl = getFileAccessHttpUrl(file.response.message);
emit('done', name, realUrl);
//update-end-author:taoyan date:2022-5-13 for:
uploading = false;
} else if (status === 'error') {
emit('error');
@ -66,6 +84,8 @@
prefixCls,
handleChange,
uploadUrl,
getheader,
getBizData,
t,
getButtonProps,
};

View File

@ -15,7 +15,9 @@
export default defineComponent({
components: {
BasicModal: createAsyncComponent(() => import('/@/components/Modal/src/BasicModal.vue'), { loading: true }),
BasicModal: createAsyncComponent(() => import('/@/components/Modal/src/BasicModal.vue'), {
loading: true,
}),
},
props: {
trigger: {

View File

@ -76,6 +76,12 @@
let className = target.className || '';
className = isString(className) ? className : className.toString();
// td
let td = getParentNodeByTagName(target, 'td');
//
if (td && td.querySelector('.j-vxe-drag-box')) {
return;
}
// expand
if (className.includes('vxe-table--expand-btn')) {
return;
@ -119,7 +125,11 @@
});
} else {
let num = ++level;
console.warn('【JVxeSubPopover】table or tr 获取失败,正在进行第 ' + num + '次重试', { event, table: parentElem, tr });
console.warn('【JVxeSubPopover】table or tr 获取失败,正在进行第 ' + num + '次重试', {
event,
table: parentElem,
tr,
});
window.setTimeout(() => open(event, num), 100);
}
}

View File

@ -64,7 +64,9 @@
return btns.filter((btn) => {
// batch_delete
if (btn === 'remove') {
return hasBtnAuth(btn) || hasBtnAuth('batch_delete');
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1162
return hasBtnAuth(btn) && hasBtnAuth('batch_delete');
//update-end-author:taoyan date:2022-6-1 for: VUEN-1162
}
return hasBtnAuth(btn);
});

View File

@ -1,5 +1,5 @@
<template>
<JInputPop :value="innerValue" :width="300" :height="210" v-bind="cellProps" style="width: 100%" @blur="handleBlurCommon" @change="handleChangeCommon" />
<JInputPop :value="innerValue" :width="300" :height="210" :pop-container="getPopupContainer" v-bind="cellProps" style="width: 100%" @blur="handleBlurCommon" @change="handleChangeCommon" />
</template>
<script lang="ts">
@ -15,7 +15,12 @@
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { innerValue, cellProps, handleChangeCommon, handleBlurCommon } = useJVxeComponent(props);
return { innerValue, cellProps, handleChangeCommon, handleBlurCommon };
function getPopupContainer() {
return document.body;
}
return { innerValue, cellProps, handleChangeCommon, handleBlurCommon, getPopupContainer };
},
// JVxeComponent.Enhanced
enhanced: {

View File

@ -262,7 +262,13 @@ async function handleDict({ col, methods }: HandleArgs) {
// 查询字典
if (!isPromise(col.params.optionsPromise)) {
col.params.optionsPromise = new Promise(async (resolve) => {
const dictOptions: any = await initDictOptions(col.params.dictCode);
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1180 【代码生成】子表不支持带条件?
let dictCodeString = col.params.dictCode;
if (dictCodeString) {
dictCodeString = encodeURI(dictCodeString);
}
const dictOptions: any = await initDictOptions(dictCodeString);
//update-end-author:taoyan date:2022-6-1 for: VUEN-1180 【代码生成】子表不支持带条件?
let options = col.params.options ?? [];
dictOptions.forEach((dict) => {
// 过滤重复数据

View File

@ -4,7 +4,7 @@ import { cloneDeep } from 'lodash-es';
export function useDataSource(props, data: JVxeDataProps, methods: JVxeTableMethods, refs: JVxeRefs) {
watch(
() => [props.dataSource, props.disabledRows],
() => props.dataSource,
async () => {
data.disabledRowIds = [];
data.vxeDataSource.value = cloneDeep(props.dataSource);

View File

@ -1,5 +1,6 @@
import { computed, nextTick, ref, unref, watch } from 'vue';
import { propTypes } from '/@/utils/propTypes';
import { useDesign } from '/@/hooks/web/useDesign';
import { getEnhanced, replaceProps, vModel } from '../utils/enhancedUtils';
import { JVxeRenderType } from '../types/JVxeTypes';
import { isBoolean, isFunction, isObject, isPromise } from '/@/utils/is';
@ -59,6 +60,19 @@ export function useJVxeComponent(props: JVxeComponent.Props) {
if (renderOptions.disabled === true) {
cellProps['disabled'] = true;
}
//update-begin-author:taoyan date:2022-5-25 for: VUEN-1111 一对多子表 部门选择 不应该级联
if (col.checkStrictly === true) {
cellProps['checkStrictly'] = true;
}
//update-end-author:taoyan date:2022-5-25 for: VUEN-1111 一对多子表 部门选择 不应该级联
//update-begin-author:taoyan date:2022-5-27 for: 用户组件 控制单选多选新的参数配置
if (col.isRadioSelection === true) {
cellProps['isRadioSelection'] = true;
} else if (col.isRadioSelection === false) {
cellProps['isRadioSelection'] = false;
}
//update-end-author:taoyan date:2022-5-27 for: 用户组件 控制单选多选新的参数配置
return cellProps;
});
@ -182,10 +196,19 @@ export function useJVxeComponent(props: JVxeComponent.Props) {
return event;
}
/**
*
* @param scope
*/
function useCellDesign(scope: string) {
return useDesign(`vxe-cell-${scope}`);
}
return {
...context,
enhanced,
trigger,
useCellDesign,
};
}

View File

@ -1,3 +1,4 @@
import { watch } from 'vue';
import XEUtils from 'xe-utils';
import { simpleDebounce } from '/@/utils/common/compUtils';
import { JVxeDataProps, JVxeRefs, JVxeTableProps, JVxeTypes } from '../types';
@ -242,6 +243,12 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
xTable.updateData();
}
// 监听 disabledRows更改时重新计算禁用行
watch(
() => props.disabledRows,
() => recalcDisableRows()
);
// 返回值决定是否允许展开、收起行
function handleExpandToggleMethod({ expanded }) {
return !(expanded && props.disabled);

View File

@ -82,12 +82,22 @@ const fooPatterns = [
{ title: '6到16位数字', value: 'n6-16', pattern: /^\d{6,16}$/ },
{ title: '6到16位任意字符', value: '*6-16', pattern: /^.{6,16}$/ },
{ title: '6到18位字母', value: 's6-18', pattern: /^[a-z|A-Z]{6,18}$/ },
{ title: '网址', value: 'url', pattern: /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/ },
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1160 对多子表,网址校验不正确
{
title: '网址',
value: 'url',
pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/,
},
//update-end-author:taoyan date:2022-6-1 for: VUEN-1160 对多子表,网址校验不正确
{ title: '电子邮件', value: 'e', pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/ },
{ title: '手机号码', value: 'm', pattern: /^1[3456789]\d{9}$/ },
{ title: '邮政编码', value: 'p', pattern: /^[1-9]\d{5}$/ },
{ title: '字母', value: 's', pattern: /^[A-Z|a-z]+$/ },
{ title: '数字', value: 'n', pattern: /^-?\d+(\.?\d+|\d?)$/ },
{ title: '整数', value: 'z', pattern: /^-?\d+$/ },
{ title: '金额', value: 'money', pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/ },
{
title: '金额',
value: 'money',
pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,5}))$/,
},
];

View File

@ -16,7 +16,7 @@ export function getJVxeAuths(prefix) {
}
// 将所有vxe用到的权限取出来
for (let auth of allAuthList) {
if (auth.status == '1' && auth.action.startsWith(prefix)) {
if (auth.status == '1' && (auth.action || '').startsWith(prefix)) {
authsMap.set(auth.action, { ...auth, isAuth: false });
}
}
@ -27,6 +27,14 @@ export function getJVxeAuths(prefix) {
getAuth.isAuth = true;
}
}
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
let onlineButtonAuths = permissionStore.getOnlineSubTableAuth(prefix);
if (onlineButtonAuths && onlineButtonAuths.length > 0) {
for (let auth of onlineButtonAuths) {
authsMap.set(prefix + 'btn:' + auth, { action: auth, type: 1, status: 1, isAuth: false });
}
}
//update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
return authsMap;
}

View File

@ -21,8 +21,8 @@ export function isRegistered(type: JVxeTypes | string) {
* vxe
*
* @param type
* @param component
* @param spanComponent
* @param component
* @param spanComponent
*/
export function registerComponent(type: JVxeTypes, component: Component, spanComponent?: Component) {
addComponent(type, component, spanComponent);

View File

@ -68,7 +68,9 @@
name: 'JPopupOnlReport',
components: {
SearchFormItem: createAsyncComponent(() => import('/@/components/jeecg/OnLine/SearchFormItem.vue'), { loading: true }),
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
loading: true,
}),
},
props: ['multi', 'code', 'id', 'sorter', 'groupId', 'param', 'clickToRowSelect'],
emits: ['ok', 'register'],

View File

@ -239,7 +239,16 @@
'max-width': labelTextMaxWidth,
},
};
return { labelTextMaxWidth, labelCol, single_mode, getDictOptionKey, getDictCode, getSqlByDictCode, DateTypeEnum, CompTypeEnum };
return {
labelTextMaxWidth,
labelCol,
single_mode,
getDictOptionKey,
getDictCode,
getSqlByDictCode,
DateTypeEnum,
CompTypeEnum,
};
},
});
</script>

View File

@ -1,4 +1,4 @@
import { reactive, ref, unref, defineAsyncComponent } from 'vue';
import { reactive, ref, unref, defineAsyncComponent, toRaw, markRaw } from 'vue';
import { httpGroupRequest } from '/@/components/Form/src/utils/GroupRequest';
import { defHttp } from '/@/utils/http/axios';
import { filterMultiDictText } from '/@/utils/dict/JDictSelectUtil.js';
@ -7,6 +7,7 @@ import { OnlineColumn } from '/@/components/jeecg/OnLine/types/onlineConfig';
import { h } from 'vue';
import { useRouter } from 'vue-router';
import { useMethods } from '/@/hooks/system/useMethods';
import { importViewsFile } from '/@/utils';
export function usePopBiz(props, tableRef?) {
const { createMessage } = useMessage();
@ -707,7 +708,12 @@ export function usePopBiz(props, tableRef?) {
destroyOnClose: true,
style: dialogStyle,
// dialogStyle: dialogStyle,
bodyStyle: { padding: '8px', height: 'calc(100vh - 108px)', overflow: 'auto', overflowX: 'hidden' },
bodyStyle: {
padding: '8px',
height: 'calc(100vh - 108px)',
overflow: 'auto',
overflowX: 'hidden',
},
// 隐藏掉取消按钮
cancelButtonProps: { style: { display: 'none' } },
},
@ -739,11 +745,67 @@ export function usePopBiz(props, tableRef?) {
}
hrefComponent.value.model.visible = true;
hrefComponent.value.model.title = '操作';
hrefComponent.value.is = defineAsyncComponent(() => import(/* @vite-ignore */ '/@/views/' + (path.startsWith('/') ? path.slice(1) : path)));
hrefComponent.value.is = markRaw(defineAsyncComponent(() => importViewsFile(path)));
}
//update-begin-author:taoyan date:2022-5-31 for: VUEN-1155 popup 选择数据时,会选择多条重复数据
/**
* emit
*/
function getOkSelectRows(): any[] {
let arr = unref(selectRows);
let selectedRowKeys = checkedKeys.value;
console.log('arr', arr);
if (!selectedRowKeys || selectedRowKeys.length <= 0) {
return [];
}
if (!arr || arr.length <= 0) {
return [];
}
let rows: any = [];
for (let key of selectedRowKeys) {
for (let i = 0; i < arr.length; i++) {
let combineKey = combineRowKey(arr[i]);
if (key === combineKey) {
rows.push(toRaw(arr[i]));
break;
}
}
}
return rows;
}
//update-end-author:taoyan date:2022-5-31 for: VUEN-1155 popup 选择数据时,会选择多条重复数据
return [
{ visibleChange, loadColumnsInfo, loadColumnsAndData, dynamicParamHandler, loadData, handleChangeInTable, combineRowKey, clickThenCheck, filterUnuseSelect, handleExport },
{ hrefComponent, visible, rowSelection, checkedKeys, selectRows, pagination, dataSource, columns, indexColumnProps, loading, title, iSorter, queryInfo, queryParam, dictOptions },
{
visibleChange,
loadColumnsInfo,
loadColumnsAndData,
dynamicParamHandler,
loadData,
handleChangeInTable,
combineRowKey,
clickThenCheck,
filterUnuseSelect,
handleExport,
getOkSelectRows,
},
{
hrefComponent,
visible,
rowSelection,
checkedKeys,
selectRows,
pagination,
dataSource,
columns,
indexColumnProps,
loading,
title,
iSorter,
queryInfo,
queryParam,
dictOptions,
},
];
}

View File

@ -1,7 +1,7 @@
import type { App } from 'vue';
import { Icon } from './Icon';
import AIcon from '/@/components/jeecg/AIcon.vue';
import { Button, UploadButton } from './Button';
import { Button, JUploadButton } from './Button';
import {
// Need
Button as AntButton,
@ -47,9 +47,12 @@ import {
InputNumber,
Carousel,
Popconfirm,
Skeleton,
Cascader,
Rate,
} from 'ant-design-vue';
const compList = [AntButton.Group, Icon, AIcon, UploadButton];
const compList = [AntButton.Group, Icon, AIcon, JUploadButton];
export function registerGlobComp(app: App) {
compList.forEach((comp) => {
@ -99,5 +102,8 @@ export function registerGlobComp(app: App) {
.use(Slider)
.use(InputNumber)
.use(Carousel)
.use(Popconfirm);
.use(Popconfirm)
.use(Skeleton)
.use(Cascader)
.use(Rate);
}

View File

@ -12,7 +12,9 @@ export interface UseEventParams {
isDebounce?: boolean;
wait?: number;
}
export function useEventListener({ el = window, name, listener, options, autoRemove = true, isDebounce = true, wait = 80 }: UseEventParams): { removeEvent: RemoveEventFn } {
export function useEventListener({ el = window, name, listener, options, autoRemove = true, isDebounce = true, wait = 80 }: UseEventParams): {
removeEvent: RemoveEventFn;
} {
/* eslint-disable-next-line */
let remove: RemoveEventFn = () => {};
const isAddRef = ref(false);

View File

@ -44,7 +44,7 @@ export function useJvxeMethod(requestAddOrEdit, classifyIntoFormData, tableRefs,
getAllTable()
.then((tables) => {
let values = formRef.value.getFieldsValue();
return validateFormModelAndTables(formRef.value.validate, values, tables);
return validateFormModelAndTables(formRef.value.validate, values, tables, formRef.value.getProps);
})
.then((allValues) => {
/** 一次性验证一对一的所有子表 */
@ -86,3 +86,95 @@ export function useJvxeMethod(requestAddOrEdit, classifyIntoFormData, tableRefs,
}
return [handleChangeTabs, handleSubmit, requestSubTableData, formRef];
}
//update-begin-author:taoyan date:2022-6-16 for: 代码生成-原生表单用
/**
* tableantd-vue
* @param activeKey /vxe-table tabs activeKey
* @param refMap /vxe-tableref map
*
* useValidateAntFormAndTable(activeKey, {
* 'tableA': tableARef,
* 'formB': formBRef
* })
*/
export function useValidateAntFormAndTable(activeKey, refMap) {
/**
*
*/
async function getSubFormAndTableData() {
let formData = {};
let all = Object.keys(refMap);
let key = '';
for (let i = 0; i < all.length; i++) {
key = all[i];
let instance = refMap[key].value;
if (instance.isForm) {
let subFormData = await validateFormAndGetData(instance, key);
if (subFormData) {
formData[key + 'List'] = [subFormData];
}
} else {
let arr = await validateTableAndGetData(instance, key);
if (arr && arr.length > 0) {
formData[key + 'List'] = arr;
}
}
}
return formData;
}
/**
*
* @param data
*/
function transformData(data) {
if (data) {
Object.keys(data).map((k) => {
if (data[k] instanceof Array) {
data[k] = data[k].join(',');
}
});
}
return data;
}
/**
* table
* @param instance
* @param key
*/
async function validateTableAndGetData(instance, key) {
const errors = await instance.validateTable();
if (!errors) {
return instance.getTableData();
} else {
activeKey.value = key;
// 自动重置scrollTop状态防止出现白屏
instance.resetScrollTop(0);
return Promise.reject(1);
}
}
/**
*
* @param instance
* @param key
*/
async function validateFormAndGetData(instance, key) {
try {
let data = await instance.getFormData();
transformData(data);
return data;
} catch (e) {
activeKey.value = key;
return Promise.reject(e);
}
}
return {
getSubFormAndTableData,
transformData,
};
}
//update-end-author:taoyan date:2022-6-16 for: 代码生成-原生表单用

View File

@ -1,4 +1,4 @@
import { reactive, ref, Ref } from 'vue';
import { reactive, ref, Ref, unref } from 'vue';
import { merge } from 'lodash-es';
import { DynamicProps } from '/#/utils';
import { BasicTableProps, TableActionType, useTable } from '/@/components/Table';
@ -20,7 +20,7 @@ interface ListPageOptions {
pagination?: boolean;
// 导出配置
exportConfig?: {
url: string;
url: string | (() => string);
// 导出文件名
name?: string | (() => string);
//导出参数
@ -28,7 +28,9 @@ interface ListPageOptions {
};
// 导入配置
importConfig?: {
url: string;
//update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
url: string | (() => string);
//update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
// 导出成功后的回调
success?: (fileInfo?: any) => void;
};
@ -63,17 +65,32 @@ export function useListPage(options: ListPageOptions) {
async function onExportXls() {
//update-begin---author:wangshuai ---date:20220411 for导出新增自定义参数------------
let { url, name, params } = options?.exportConfig ?? {};
if (url) {
let realUrl = typeof url === 'function' ? url() : url;
if (realUrl) {
let title = typeof name === 'function' ? name() : name;
let paramsForm = await getForm().validate();
//如果参数不为空,则整合到一起
if (params) {
paramsForm = Object.assign({}, paramsForm, params);
//update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
let paramsForm = {};
try {
paramsForm = await getForm().validate();
} catch (e) {
console.error(e);
}
//update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
//如果参数不为空,则整合到一起
//update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
if (params) {
Object.keys(params).map((k) => {
let temp = (params as object)[k];
if (temp) {
paramsForm[k] = unref(temp);
}
});
}
//update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
if (selectedRowKeys.value && selectedRowKeys.value.length > 0) {
paramsForm['selections'] = selectedRowKeys.value.join(',');
}
return handleExportXls(title as string, url, filterObj(paramsForm));
return handleExportXls(title as string, realUrl, filterObj(paramsForm));
//update-end---author:wangshuai ---date:20220411 for导出新增自定义参数--------------
} else {
$message.createMessage.warn('没有传递 exportConfig.url 参数');
@ -84,8 +101,11 @@ export function useListPage(options: ListPageOptions) {
// 导入 excel
function onImportXls(file) {
let { url, success } = options?.importConfig ?? {};
if (url) {
return handleImportXls(file, url, success || reload);
//update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
let realUrl = typeof url === 'function' ? url() : url;
if (realUrl) {
return handleImportXls(file, realUrl, success || reload);
//update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
} else {
$message.createMessage.warn('没有传递 importConfig.url 参数');
return Promise.reject();

View File

@ -152,7 +152,11 @@ export function useThirdLogin() {
if (!unref(thirdCaptcha)) {
cmsFailed('请输入验证码');
}
let params = { mobile: unref(thirdPhone), captcha: unref(thirdCaptcha), thirdUserUuid: unref(thirdUserUuid) };
let params = {
mobile: unref(thirdPhone),
captcha: unref(thirdCaptcha),
thirdUserUuid: unref(thirdUserUuid),
};
defHttp.post({ url: '/sys/thirdLogin/bindingThirdPhone', params }, { isTransformResponse: false }).then((res) => {
if (res.success) {
bindingPhoneModal.value = false;

View File

@ -18,12 +18,29 @@ import { isArray } from '/@/utils/is';
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
// User permissions related operations
export function usePermission() {
export function usePermission(formData?) {
const userStore = useUserStore();
const appStore = useAppStore();
const permissionStore = usePermissionStore();
const { closeAll } = useTabs(router);
//==================================工作流权限判断-begin=========================================
function hasBpmPermission(code, type) {
// 禁用-type=2
// 显示-type=1
let codeList: string[] = [];
let permissionList = formData.permissionList;
if (permissionList && permissionList.length > 0) {
for (let item of permissionList) {
if (item.type == type) {
codeList.push(item.action);
}
}
}
return codeList.indexOf(code) >= 0;
}
//==================================工作流权限判断-end=========================================
/**
* Change permission mode
*/
@ -71,6 +88,14 @@ export function usePermission() {
if (PermissionModeEnum.BACK === permMode) {
const allCodeList = permissionStore.getPermCodeList as string[];
if (!isArray(value) && allCodeList && allCodeList.length > 0) {
//=============================工作流权限判断-显示-begin==============================================
if (formData) {
let code = value as string;
if (hasBpmPermission(code, '1') === true) {
return true;
}
}
//=============================工作流权限判断-显示-end==============================================
return allCodeList.includes(value);
}
return (intersection(value, allCodeList) as string[]).length > 0;
@ -81,6 +106,19 @@ export function usePermission() {
*
*/
function isDisabledAuth(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean {
//=============================工作流权限判断-禁用-begin==============================================
if (formData) {
let code = value as string;
if (hasBpmPermission(code, '2') === true) {
return true;
}
//update-begin-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
if (isCodingButNoConfig(code) == true) {
return false;
}
//update-end-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
}
//=============================工作流权限判断-禁用-end==============================================
return !hasPermission(value);
}
@ -107,5 +145,25 @@ export function usePermission() {
resume();
}
//update-begin-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
/**
*
*/
function isCodingButNoConfig(code) {
let all = permissionStore.allAuthList;
if (all && all instanceof Array) {
let temp = all.filter((item) => item.action == code);
if (temp && temp.length > 0) {
if (temp[0].status == '0') {
return true;
}
} else {
return true;
}
}
return false;
}
//update-end-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
return { changeRole, hasPermission, togglePermissionMode, refreshMenu, isDisabledAuth };
}

View File

@ -141,7 +141,14 @@
props.onTitleClick && props.onTitleClick(item);
}
return { prefixCls, getPagination, getData, handleTitleClick, isTitleClickable, PriorityTypes };
return {
prefixCls,
getPagination,
getData,
handleTitleClick,
isTitleClickable,
PriorityTypes,
};
},
});
</script>

View File

@ -50,7 +50,16 @@
import { readAllMsg } from '/@/views/monitor/mynews/mynews.api';
import { getToken } from '/@/utils/auth';
export default defineComponent({
components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList, DetailModal, DynamicNotice },
components: {
Popover,
BellOutlined,
Tabs,
TabPane: Tabs.TabPane,
Badge,
NoticeList,
DetailModal,
DynamicNotice,
},
setup() {
const { prefixCls } = useDesign('header-notify');
const instance: any = getCurrentInstance();
@ -89,7 +98,9 @@
//
async function loadData() {
try {
let { anntMsgList, sysMsgList, anntMsgTotal, sysMsgTotal } = await listCementByUser({ pageSize: 5 });
let { anntMsgList, sysMsgList, anntMsgTotal, sysMsgTotal } = await listCementByUser({
pageSize: 5,
});
listData.value[0].list = anntMsgList.map(mapAnnouncement);
listData.value[1].list = sysMsgList.map(mapAnnouncement);
listData.value[0].count = anntMsgTotal;

View File

@ -182,7 +182,10 @@
if (!unref(isMultiDepart)) {
resolve();
} else {
const result = await selectDepart({ username: userStore.getUserInfo.username, orgCode: unref(departSelected) });
const result = await selectDepart({
username: userStore.getUserInfo.username,
orgCode: unref(departSelected),
});
if (result.userInfo) {
const userInfo = result.userInfo;
userStore.setUserInfo(userInfo);

View File

@ -85,7 +85,14 @@
return { realname, avatar: avatar || headerImg, desc };
});
const getAvatarUrl = computed(() => getFileAccessHttpUrl(getUserInfo.value?.avatar));
const getAvatarUrl = computed(() => {
let { avatar } = getUserInfo.value;
if (avatar == headerImg) {
return avatar;
} else {
return getFileAccessHttpUrl(avatar);
}
});
const [register, { openModal }] = useModal();
/**

View File

@ -65,8 +65,6 @@
import LoginSelect from '/@/views/sys/login/LoginSelect.vue';
import { useUserStore } from '/@/store/modules/user';
import { getUserTenantId, setAuthCache } from '/@/utils/auth';
import { TENANT_ID } from '/@/enums/cacheEnum';
export default defineComponent({
name: 'LayoutHeader',
@ -150,15 +148,10 @@
function showLoginSelect() {
//update-begin---author:liusq Date:20220101 for----
//
const userTenantId = getUserTenantId(userStore.getUserInfo.username);
if (!userTenantId && userTenantId != 0) {
//
const loginInfo = toRaw(userStore.getLoginInfo) || {};
//
const loginInfo = toRaw(userStore.getLoginInfo) || {};
if (!!loginInfo.isLogin) {
loginSelectRef.value.show(loginInfo);
} else {
//
setAuthCache(TENANT_ID, userTenantId);
}
//update-end---author:liusq Date:20220101 for----
}

View File

@ -19,6 +19,7 @@
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { useDesign } from '/@/hooks/web/useDesign';
import { useLocaleStore } from '/@/store/modules/locale';
export default defineComponent({
name: 'LayoutMenu',
@ -92,10 +93,15 @@
* click menu
* @param menu
*/
function handleMenuClick(path: string) {
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online
const localeStore = useLocaleStore();
function handleMenuClick(path: string, item) {
if (item) {
localeStore.setPathTitle(path, item.title || '');
}
go(path);
}
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online
/**
* before click menu

View File

@ -1,9 +1,9 @@
<template>
<div :class="prefixCls">
<!-- <a-button type="primary" block @click="handleCopy">-->
<!-- <CopyOutlined class="mr-2" />-->
<!-- {{ t('layout.setting.copyBtn') }}-->
<!-- </a-button>-->
<a-button type="primary" block @click="handleCopy">
<CopyOutlined class="mr-2" />
{{ t('layout.setting.copyBtn') }}
</a-button>
<a-button color="warning" block @click="handleResetSetting" class="my-3">
<RedoOutlined class="mr-2" />

View File

@ -27,6 +27,7 @@
import { useI18n } from '/@/hooks/web/useI18n';
import { useTabDropdown } from '../useTabDropdown';
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
import { useLocaleStore } from '/@/store/modules/locale';
export default defineComponent({
name: 'TabContent',
@ -42,10 +43,17 @@
const { prefixCls } = useDesign('multiple-tabs-content');
const { t } = useI18n();
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online
const localeStore = useLocaleStore();
const getTitle = computed(() => {
const { tabItem: { meta } = {} } = props;
const { tabItem: { meta, fullPath } = {} } = props;
let title = localeStore.getPathTitle(fullPath);
if (title) {
return title;
}
return meta && t(meta.title as string);
});
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online
const getIsTabs = computed(() => !props.isExtra);

View File

@ -68,7 +68,7 @@ export default {
forgetFormTitle: '重置密码',
signInTitle: 'Jeecg Boot',
signInDesc: '是中国最具影响力的 企业级 低代码平台!',
signInDesc: '是中国最具影响力的 企业级低代码平台!在线开发可视化拖拽设计零代码实现80%的基础功能~',
policy: '我同意xxx隐私政策',
scanSign: `扫码后,即可完成登录`,
scanSuccess: `扫码成功,登录中`,

View File

@ -13,12 +13,14 @@ const lsLocaleSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting;
interface LocaleState {
localInfo: LocaleSetting;
pathTitleMap: object;
}
export const useLocaleStore = defineStore({
id: 'app-locale',
state: (): LocaleState => ({
localInfo: lsLocaleSetting,
pathTitleMap: {},
}),
getters: {
getShowPicker(): boolean {
@ -27,6 +29,11 @@ export const useLocaleStore = defineStore({
getLocale(): LocaleType {
return this.localInfo?.locale ?? 'zh_CN';
},
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
getPathTitle: (state) => {
return (path) => state.pathTitleMap[path];
},
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
},
actions: {
/**
@ -46,6 +53,11 @@ export const useLocaleStore = defineStore({
...this.localInfo,
});
},
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
setPathTitle(path, title) {
this.pathTitleMap[path] = title;
},
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
},
});

View File

@ -53,6 +53,8 @@ interface PermissionState {
allAuthList: AuthItem[];
// 系统安全模式
sysSafeMode: boolean;
// online子表按钮权限
onlineSubTableAuthMap: object;
}
export const usePermissionStore = defineStore({
id: 'app-permission',
@ -69,6 +71,7 @@ export const usePermissionStore = defineStore({
authList: [],
allAuthList: [],
sysSafeMode: false,
onlineSubTableAuthMap: {},
}),
getters: {
getPermCodeList(): string[] | number[] {
@ -86,6 +89,12 @@ export const usePermissionStore = defineStore({
getIsDynamicAddedRoute(): boolean {
return this.isDynamicAddedRoute;
},
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
getOnlineSubTableAuth: (state) => {
return (code) => state.onlineSubTableAuthMap[code];
},
//update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
},
actions: {
setPermCodeList(codeList: string[]) {
@ -278,6 +287,12 @@ export const usePermissionStore = defineStore({
setAllAuthList(authList: AuthItem[]) {
this.allAuthList = authList;
},
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
setOnlineSubTableAuth(code, hideBtnList) {
this.onlineSubTableAuthMap[code] = hideBtnList;
},
//update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
},
});

View File

@ -5,7 +5,7 @@ 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 { getAuthCache, setAuthCache } from '/@/utils/auth';
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';
import { useI18n } from '/@/hooks/web/useI18n';
@ -16,6 +16,7 @@ import { RouteRecordRaw } from 'vue-router';
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
import { isArray } from '/@/utils/is';
import { useGlobSetting } from '/@/hooks/setting';
import { JDragConfigEnum } from '/@/enums/jeecgEnum';
import { useSso } from '/@/hooks/web/useSso';
interface UserState {
userInfo: Nullable<UserInfo>;
@ -99,10 +100,6 @@ export const useUserStore = defineStore({
setTenant(id) {
this.tenantid = id;
setAuthCache(TENANT_ID, id);
//update-begin---author:liusq Date:20220102 for保存用户租户id----
// @ts-ignore
setAuthCache(this.userInfo.username, id);
//update-end---author:liusq Date:20220102 for保存用户租户id----
},
setSessionTimeout(flag: boolean) {
this.sessionTimeout = flag;
@ -167,7 +164,10 @@ export const useUserStore = defineStore({
router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw);
permissionStore.setDynamicAddedRoute(true);
}
await this.setLoginInfo(data);
await this.setLoginInfo({ ...data, isLogin: true });
//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: 登录成功后缓存拖拽模块的接口前缀
goHome && (await router.replace((userInfo && userInfo.homePath) || PageEnum.BASE_HOME));
}
return data;
@ -233,11 +233,22 @@ export const useUserStore = defineStore({
console.log('注销Token失败');
}
}
// //update-begin-author:taoyan date:2022-5-5 for: src/layouts/default/header/index.vue showLoginSelect方法 获取tenantId 退出登录后再次登录依然能获取到值,没有清空
// let username:any = this.userInfo && this.userInfo.username;
// if(username){
// removeAuthCache(username)
// }
// //update-end-author:taoyan date:2022-5-5 for: src/layouts/default/header/index.vue showLoginSelect方法 获取tenantId 退出登录后再次登录依然能获取到值,没有清空
this.setToken('');
setAuthCache(TOKEN_KEY, null);
this.setSessionTimeout(false);
this.setUserInfo(null);
this.setLoginInfo(null);
//update-begin-author:liusq date:2022-5-5 for:退出登录后清除拖拽模块的接口前缀
localStorage.removeItem(JDragConfigEnum.DRAG_BASE_URL);
//update-end-author:liusq date:2022-5-5 for: 退出登录后清除拖拽模块的接口前缀
//如果开启单点登录,则跳转到单点统一登录中心
const openSso = useGlobSetting().openSso;

View File

@ -10,7 +10,7 @@ const isLocal = permissionCacheType === CacheTypeEnum.LOCAL;
* token
*/
export function getToken() {
return getAuthCache(TOKEN_KEY);
return getAuthCache<string>(TOKEN_KEY);
}
/**
*
@ -22,13 +22,7 @@ export function getLoginBackInfo() {
* id
*/
export function getTenantId() {
return getAuthCache(TENANT_ID);
}
/**
* id
*/
export function getUserTenantId(username) {
return getAuthCache(username);
return getAuthCache<string>(TENANT_ID);
}
export function getAuthCache<T>(key: BasicKeys) {
@ -40,6 +34,25 @@ export function setAuthCache(key: BasicKeys, value) {
const fn = isLocal ? Persistent.setLocal : Persistent.setSession;
return fn(key, value, true);
}
/**
* key
* @param key
* @param value
*/
export function setCacheByDynKey(key, value) {
const fn = isLocal ? Persistent.setLocal : Persistent.setSession;
return fn(key, value, true);
}
/**
* key
* @param key
*/
export function getCacheByDynKey<T>(key) {
const fn = isLocal ? Persistent.getLocal : Persistent.getSession;
return fn(key) as T;
}
/**
*
* @param key

View File

@ -315,3 +315,13 @@ export function bindMapFormSchema<T>(spanMap, spanTypeDef: T) {
);
};
}
/**
* nullnull
* @param str
* @return {boolean}
*/
export function stringIsNull(str) {
// 两个 == 可以同时判断 null 和 undefined
return str == null || str === 'null' || str === 'undefined';
}

View File

@ -5,8 +5,9 @@ import { Tinymce } from '/@/components/Tinymce';
import Icon from '/@/components/Icon';
import { getDictItemsByCode } from '/@/utils/dict/index';
import { filterMultiDictText } from '/@/utils/dict/JDictSelectUtil.js';
import { loadCategoryData } from '/@/api/common/api';
import { isEmpty } from '/@/utils/is';
import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage } = useMessage();
const render = {
/**
@ -18,7 +19,12 @@ const render = {
return h(
'span',
avatarList.map((item) => {
return h(Avatar, { src: getFileAccessHttpUrl(item), shape: 'square', size: 'default', style: { marginRight: '5px' } });
return h(Avatar, {
src: getFileAccessHttpUrl(item),
shape: 'square',
size: 'default',
style: { marginRight: '5px' },
});
})
);
} else {
@ -39,7 +45,7 @@ const render = {
*/
renderDict: (v, code, renderTag = false) => {
let text = '';
let array = getDictItemsByCode(code);
let array = getDictItemsByCode(code) || [];
let obj = array.filter((item) => {
return item.value == v;
});
@ -54,11 +60,12 @@ 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: 'large' },
{ shape: 'square', size: 25 },
{
icon: () => h(Icon, { icon: 'ant-design:file-image-outlined', size: 30 }),
icon: () => h(Icon, { icon: 'ant-design:file-image-outlined', size: 25 }),
}
);
}
@ -66,9 +73,15 @@ const render = {
return h(
'span',
avatarList.map((item) => {
return h(Avatar, { src: getFileAccessHttpUrl(item), shape: 'square', size: 'large', style: { marginRight: '5px' } });
return h(Avatar, {
src: getFileAccessHttpUrl(item),
shape: 'square',
size: 25,
style: { marginRight: '5px' },
});
})
);
//update-end-author:taoyan date:2022-5-24 for: VUEN-1084 【vue3】online表单测试发现的新问题 41、生成的代码树默认图大小未改
},
/**
* Tooltip
@ -141,4 +154,22 @@ const render = {
return isEmpty(text) ? h('span', text) : h(Tag, { color }, () => text);
},
};
export { render };
/**
*
*/
function downloadFile(url) {
if (!url) {
createMessage.warning('未知的文件');
return;
}
if (url.indexOf(',') > 0) {
url = url.substring(0, url.indexOf(','));
}
url = getFileAccessHttpUrl(url.split(',')[0]);
if (url) {
window.open(url);
}
}
export { render, downloadFile };

View File

@ -1,3 +1,5 @@
import { getValueType } from '/@/utils';
export const VALIDATE_FAILED = Symbol();
/**
* ()
@ -5,7 +7,7 @@ export const VALIDATE_FAILED = Symbol();
* @param cases JEditableTable
* @returns {Promise<any>}
*/
export async function validateFormModelAndTables(validate, formData, cases, autoJumpTab?) {
export async function validateFormModelAndTables(validate, formData, cases, props, autoJumpTab?) {
if (!(validate && typeof validate === 'function')) {
throw `validate 参数需要的是一个方法,而传入的却是${typeof validate}`;
}
@ -14,6 +16,18 @@ export async function validateFormModelAndTables(validate, formData, cases, auto
// 验证主表表单
validate()
.then(() => {
//update-begin---author:wangshuai ---date:20220507 for[VUEN-912]一对多用户组件(所有风格,单表和树没问题)保存报错------------
for (let data in formData) {
//如果该数据是数组
if (formData[data] instanceof Array) {
let valueType = getValueType(props, data);
//如果是字符串类型的需要变成以逗号分割的字符串
if (valueType === 'string') {
formData[data] = formData[data].join(',');
}
}
}
//update-end---author:wangshuai ---date:20220507 for[VUEN-912]一对多用户组件(所有风格,单表和树没问题)保存报错--------------
resolve(formData);
})
.catch(() => {

View File

@ -9,9 +9,10 @@ import { ajaxGetDictItems, getDictItemsByCode } from './index';
/**
* 获取字典数组
* @param dictCode 字典Code
* @param isTransformResponse 是否转换返回结果
* @return List<Map>
*/
export async function initDictOptions(dictCode) {
export async function initDictOptions(dictCode, isTransformResponse = true) {
if (!dictCode) {
return '字典Code不能为空!';
}
@ -20,11 +21,14 @@ export async function initDictOptions(dictCode) {
let res = {};
res.result = getDictItemsByCode(dictCode);
res.success = true;
return res;
if (isTransformResponse) {
return res.result;
} else {
return res;
}
}
//获取字典数组
let res = await ajaxGetDictItems(dictCode);
return res;
return await ajaxGetDictItems(dictCode, {}, { isTransformResponse });
}
/**
@ -125,7 +129,10 @@ export function filterDictTextByCache(dictCode, key) {
export async function getDictItems(dictCode, params) {
//优先从缓存中读取字典配置
if (getDictItemsByCode(dictCode)) {
let desformDictItems = getDictItemsByCode(dictCode).map((item) => ({ ...item, label: item.text }));
let desformDictItems = getDictItemsByCode(dictCode).map((item) => ({
...item,
label: item.text,
}));
return desformDictItems;
}

View File

@ -24,12 +24,19 @@ export const initDictOptions = (code) => {
});
}
//2.获取字典数组
//update-begin-author:taoyan date:2022-6-21 for: 字典数据请求前将参数编码处理,但是不能直接编码,因为可能之前已经编码过了
if (code.indexOf(',') > 0 && code.indexOf(' ') > 0) {
// 编码后类似sys_user%20where%20username%20like%20xxx' 是不包含空格的,这里判断如果有空格和逗号说明需要编码处理
code = encodeURI(code);
}
//update-end-author:taoyan date:2022-6-21 for: 字典数据请求前将参数编码处理,但是不能直接编码,因为可能之前已经编码过了
return defHttp.get({ url: `/sys/dict/getDictItems/${code}` });
};
/**
*
* @param code Code
* @param params
* @param options
* @return List<Map>
*/
export const ajaxGetDictItems = (code, params) => defHttp.get({ url: `/sys/dict/getDictItems/${code}`, params });
export const ajaxGetDictItems = (code, params, options?) => defHttp.get({ url: `/sys/dict/getDictItems/${code}`, params }, options);

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