重大版本发布,1.3.0版本源码上传(全功能趋于稳定健壮)
parent
8b54e3be5e
commit
5aec20bfa0
|
@ -31,3 +31,4 @@ pnpm-debug.log*
|
|||
/os_del.cmd
|
||||
/.vscode/
|
||||
/.history/
|
||||
/svn clear.bat
|
||||
|
|
18
package.json
18
package.json
|
@ -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",
|
||||
|
|
|
@ -8,6 +8,7 @@ module.exports = {
|
|||
quoteProps: 'as-needed',
|
||||
bracketSpacing: true,
|
||||
trailingComma: 'es5',
|
||||
jsxBracketSameLine: false,
|
||||
jsxSingleQuote: false,
|
||||
arrowParens: 'always',
|
||||
insertPragma: false,
|
||||
|
|
|
@ -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 }
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 |
|
@ -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>>;
|
||||
|
|
|
@ -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>
|
|
@ -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 },
|
||||
};
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -233,6 +233,7 @@
|
|||
updateSchema,
|
||||
resetSchema,
|
||||
setProps,
|
||||
getProps,
|
||||
removeSchemaByFiled,
|
||||
appendSchemaByField,
|
||||
clearValidate,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 }) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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表达式
|
||||
|
|
|
@ -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),
|
||||
});
|
||||
//显示的文本
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
// 如果是联动组件的第一个组件,等待options加载完后从options中取值
|
||||
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>
|
|
@ -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>
|
||||
|
|
|
@ -117,15 +117,20 @@
|
|||
options.value = [];
|
||||
loading.value = true;
|
||||
// 字典code格式:table,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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 for:JTreeSelect组件pidValue还原成空,否则会影响自定义组件树示例------------
|
||||
pidValue: propTypes.string.def(''),
|
||||
//update-end---author:wangshuai ---date:20220620 for:JTreeSelect组件pidValue还原成空,否则会影响自定义组件树示例--------------
|
||||
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 15、严重——online树表单,添加的时候,父亲节点是空的
|
||||
watch(
|
||||
() => props.reload,
|
||||
async () => {
|
||||
treeData.value = [];
|
||||
await loadRoot();
|
||||
},
|
||||
{
|
||||
immediate: false,
|
||||
}
|
||||
);
|
||||
//update-end-author:taoyan date:2022-5-25 for: VUEN-1056 15、严重——online树表单,添加的时候,父亲节点是空的
|
||||
|
||||
/**
|
||||
* 根据code获取下拉数据并回显
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 }) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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' },
|
||||
},
|
||||
],
|
||||
};
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -141,5 +141,4 @@ export type ComponentType =
|
|||
| 'JSearchSelect'
|
||||
| 'JAddInput'
|
||||
| 'Time'
|
||||
| 'JOnlineSelectCascade'
|
||||
| 'JRangeNumber';
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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} {
|
||||
// 限制tag最大长度为100px,防止选中文字过多的选项时换行
|
||||
.ant-select .ant-select-selection-overflow-item {
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -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} {
|
||||
// 限制tag最大长度为100px,防止选中文字过多的选项时换行
|
||||
.ant-select .ant-select-selection-overflow-item {
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<MenuItem :key="item.path">
|
||||
<MenuItem :key="item.path" :title="item.title">
|
||||
<MenuItemContent v-bind="$props" :item="item" />
|
||||
</MenuItem>
|
||||
</template>
|
||||
|
|
|
@ -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 };
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 key|column
|
||||
*/
|
||||
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 || {};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -48,7 +48,8 @@ export function useTableHeader(propsRef: ComputedRef<BasicTableProps>, slots: Sl
|
|||
tableTop: () => getSlot(slots, 'tableTop'),
|
||||
}
|
||||
: {}),
|
||||
//添加tableTop插槽
|
||||
// 添加alertAfter插槽
|
||||
...(slots.alertAfter ? { alertAfter: () => getSlot(slots, 'alertAfter') } : {}),
|
||||
}
|
||||
),
|
||||
};
|
||||
|
|
|
@ -127,9 +127,15 @@ export interface FetchSetting {
|
|||
}
|
||||
|
||||
export interface TableSetting {
|
||||
// 是否显示刷新按钮
|
||||
redo?: boolean;
|
||||
// 是否显示尺寸调整按钮
|
||||
size?: boolean;
|
||||
// 是否显示字段调整按钮
|
||||
setting?: boolean;
|
||||
// 缓存“字段调整”配置的key,用于页面上有多个表格需要区分的情况
|
||||
cacheKey?: string;
|
||||
// 是否显示全屏按钮
|
||||
fullScreen?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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) => {
|
||||
// 过滤重复数据
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}))$/,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: 代码生成-原生表单用
|
||||
/**
|
||||
* 校验多个表单和子表table,用于原生的antd-vue的表单
|
||||
* @param activeKey 子表表单/vxe-table 所在tabs的 activeKey
|
||||
* @param refMap 子表表单/vxe-table对应的ref对象 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: 代码生成-原生表单用
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
@ -141,7 +141,14 @@
|
|||
props.onTitleClick && props.onTitleClick(item);
|
||||
}
|
||||
|
||||
return { prefixCls, getPagination, getData, handleTitleClick, isTitleClickable, PriorityTypes };
|
||||
return {
|
||||
prefixCls,
|
||||
getPagination,
|
||||
getData,
|
||||
handleTitleClick,
|
||||
isTitleClickable,
|
||||
PriorityTypes,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
/**
|
||||
|
|
|
@ -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:判断登录进来是否需要弹窗选择租户----
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ export default {
|
|||
forgetFormTitle: '重置密码',
|
||||
|
||||
signInTitle: 'Jeecg Boot',
|
||||
signInDesc: '是中国最具影响力的 企业级 低代码平台!',
|
||||
signInDesc: '是中国最具影响力的 企业级低代码平台!在线开发,可视化拖拽设计,零代码实现80%的基础功能~',
|
||||
policy: '我同意xxx隐私政策',
|
||||
scanSign: `扫码后,即可完成登录`,
|
||||
scanSuccess: `扫码成功,登录中`,
|
||||
|
|
|
@ -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 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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 子表按钮没控制
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -315,3 +315,13 @@ export function bindMapFormSchema<T>(spanMap, spanTypeDef: T) {
|
|||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串是否为null或null字符串
|
||||
* @param str
|
||||
* @return {boolean}
|
||||
*/
|
||||
export function stringIsNull(str) {
|
||||
// 两个 == 可以同时判断 null 和 undefined
|
||||
return str == null || str === 'null' || str === 'undefined';
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue