Browse Source

Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin

# Conflicts:
#	src/components/SimpleMenu/src/SimpleSubMenu.vue
shizhongming 2 years ago
parent
commit
fa8ada29bb
  1. 3
      .vscode/extensions.json
  2. 8
      apps/test-server/service/FileService.ts
  3. 12
      index.html
  4. 14
      src/components/Application/src/search/useMenuSearch.ts
  5. 16
      src/components/Container/src/collapse/CollapseContainer.vue
  6. 8
      src/components/Container/src/collapse/CollapseHeader.vue
  7. 28
      src/components/Description/src/Description.vue
  8. 2
      src/components/Form/src/BasicForm.vue
  9. 2
      src/components/Form/src/components/ApiCascader.vue
  10. 2
      src/components/Form/src/components/ApiRadioGroup.vue
  11. 6
      src/components/Form/src/components/ApiSelect.vue
  12. 2
      src/components/Form/src/components/ApiTransfer.vue
  13. 17
      src/components/Form/src/hooks/useFormEvents.ts
  14. 6
      src/components/Form/src/hooks/useFormValues.ts
  15. 2
      src/components/Menu/src/components/MenuItemContent.vue
  16. 52
      src/components/Scrollbar/src/bar.ts
  17. 1
      src/components/SimpleMenu/src/SimpleSubMenu.vue
  18. 1
      src/components/Table/src/components/HeaderCell.vue
  19. 2
      src/components/Table/src/components/TableImg.vue
  20. 40
      src/components/Table/src/components/editable/EditableCell.vue
  21. 12
      src/components/Table/src/components/settings/ColumnSetting.vue
  22. 2
      src/components/Table/src/hooks/useDataSource.ts
  23. 6
      src/components/Table/src/hooks/usePagination.ts
  24. 2
      src/components/Table/src/hooks/useTableExpand.ts
  25. 34
      src/components/VirtualScroll/src/VirtualScroll.vue
  26. 12
      src/components/VxeTable/src/components/common.tsx
  27. 5
      src/components/VxeTable/src/const.ts
  28. 13
      src/components/VxeTable/src/helper.ts
  29. 3
      src/design/ant/input.less
  30. 2
      src/enums/appEnum.ts
  31. 1
      src/hooks/web/usePermission.ts
  32. 4
      src/layouts/default/header/components/Breadcrumb.vue
  33. 2
      src/layouts/default/sider/MixSider.vue
  34. 4
      src/router/helper/menuHelper.ts
  35. 2
      src/utils/helper/tsxHelper.ts
  36. 2
      src/utils/index.ts
  37. 6
      src/views/demo/comp/qrcode/index.vue
  38. 28
      src/views/demo/comp/scroll/VirtualScroll.vue
  39. 12
      src/views/demo/page/account/center/index.vue
  40. 4
      src/views/demo/page/account/setting/AccountBind.vue
  41. 14
      src/views/demo/page/account/setting/BaseSetting.vue
  42. 4
      src/views/demo/page/account/setting/MsgNotify.vue
  43. 4
      src/views/demo/page/account/setting/SecureSetting.vue
  44. 4
      src/views/form-design/components/VFormDesign/components/ComponentProps.vue
  45. 2
      src/views/form-design/components/VFormDesign/components/FormProps.vue
  46. 2
      src/views/form-design/components/VFormDesign/config/componentPropsConfig.ts

3
.vscode/extensions.json

@ -9,6 +9,5 @@
"antfu.iconify",
"antfu.unocss",
"mikestead.dotenv",
"vue.vscode-typescript-vue-plugin"
]
}
}

8
apps/test-server/service/FileService.ts

@ -10,8 +10,8 @@ export default class FileService {
let fileReader, fileResource, writeStream;
const fileFunc = function (file) {
fileReader = fs.createReadStream(file.path);
fileResource = filePath + `/${file.name}`;
fileReader = fs.createReadStream(file.filepath);
fileResource = filePath + `/${file.originalFilename}`;
console.log(fileResource);
writeStream = fs.createWriteStream(fileResource);
@ -22,7 +22,7 @@ export default class FileService {
if (flag) {
let url = '';
for (let i = 0; i < files.length; i++) {
url += uploadUrl + `/${files[i].name},`;
url += uploadUrl + `/${files[i].originalFilename},`;
}
url = url.replace(/,$/gi, '');
ctx.body = {
@ -32,7 +32,7 @@ export default class FileService {
};
} else {
ctx.body = {
url: uploadUrl + `/${files.name}`,
url: uploadUrl + `/${files.originalFilename}`,
code: 0,
message: 'upload Success!',
};

12
index.html

@ -125,18 +125,6 @@
}
}
@keyframes ant-rotate {
to {
transform: rotate(405deg);
}
}
@keyframes ant-spin-move {
to {
opacity: 1;
}
}
@keyframes ant-spin-move {
to {
opacity: 1;

14
src/components/Application/src/search/useMenuSearch.ts

@ -1,13 +1,13 @@
import { type Menu } from '@/router/types';
import { type AnyFunction } from '@vben/types';
import { ref, onBeforeMount, unref, Ref, nextTick } from 'vue';
import { useI18n } from '@/hooks/web/useI18n';
import { useGo } from '@/hooks/web/usePage';
import { getMenus } from '@/router/menus';
import { cloneDeep } from 'lodash-es';
import { type Menu } from '@/router/types';
import { filter, forEach } from '@/utils/helper/treeHelper';
import { useGo } from '@/hooks/web/usePage';
import { useScrollTo } from '@vben/hooks';
import { type AnyFunction } from '@vben/types';
import { onKeyStroke, useDebounceFn } from '@vueuse/core';
import { useI18n } from '@/hooks/web/useI18n';
import { cloneDeep } from 'lodash-es';
import { Ref, nextTick, onBeforeMount, ref, unref } from 'vue';
export interface SearchResult {
name: string;
@ -42,7 +42,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref, emit: A
const list = await getMenus();
menuList = cloneDeep(list);
forEach(menuList, (item) => {
item.name = t(item.name);
item.name = t(item.meta?.title || item.name);
});
});

16
src/components/Container/src/collapse/CollapseContainer.vue

@ -1,12 +1,12 @@
<script lang="tsx">
import { ref, unref, defineComponent, type PropType, type ExtractPropTypes } from 'vue';
import { isNil } from 'lodash-es';
import { Skeleton } from 'ant-design-vue';
import { useTimeoutFn } from '@vben/hooks';
import { CollapseTransition } from '@/components/Transition';
import CollapseHeader from './CollapseHeader.vue';
import { triggerWindowResize } from '@/utils/event';
import { useDesign } from '@/hooks/web/useDesign';
import { triggerWindowResize } from '@/utils/event';
import { useTimeoutFn } from '@vben/hooks';
import { Skeleton } from 'ant-design-vue';
import { isNil } from 'lodash-es';
import { defineComponent, ref, unref, type ExtractPropTypes, type PropType } from 'vue';
import CollapseHeader from './CollapseHeader.vue';
const collapseContainerProps = {
title: { type: String, default: '' },
@ -14,7 +14,7 @@
/**
* Can it be expanded
*/
canExpan: { type: Boolean, default: true },
canExpand: { type: Boolean, default: true },
/**
* Warm reminder on the right side of the title
*/
@ -69,7 +69,7 @@
/>
<div class="p-2">
<CollapseTransition enable={props.canExpan}>
<CollapseTransition enable={props.canExpand}>
{props.loading ? (
<Skeleton active={props.loading} />
) : (

8
src/components/Container/src/collapse/CollapseHeader.vue

@ -1,13 +1,13 @@
<script lang="tsx">
import { defineComponent, computed, unref, type ExtractPropTypes, PropType } from 'vue';
import { useDesign } from '@/hooks/web/useDesign';
import { BasicArrow, BasicTitle } from '@/components/Basic';
import { useDesign } from '@/hooks/web/useDesign';
import { PropType, computed, defineComponent, unref, type ExtractPropTypes } from 'vue';
const collapseHeaderProps = {
prefixCls: String,
title: String,
show: Boolean,
canExpan: Boolean,
canExpand: Boolean,
helpMessage: {
type: [Array, String] as PropType<string[] | string>,
default: '',
@ -33,7 +33,7 @@
<div class={`${unref(_prefixCls)}__action`}>
{slots.action
? slots.action({ expand: props.show, onClick: () => emit('expand') })
: props.canExpan && (
: props.canExpand && (
<BasicArrow up expand={props.show} onClick={() => emit('expand')} />
)}
</div>

28
src/components/Description/src/Description.vue

@ -1,23 +1,23 @@
<script lang="tsx">
import type { DescriptionProps, DescInstance, DescItem } from './typing';
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
import type { CollapseContainerOptions } from '@/components/Container';
import { CollapseContainer } from '@/components/Container';
import { useDesign } from '@/hooks/web/useDesign';
import { getSlot } from '@/utils/helper/tsxHelper';
import { isFunction } from '@/utils/is';
import { useAttrs } from '@vben/hooks';
import { Descriptions } from 'ant-design-vue';
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
import { get } from 'lodash-es';
import {
type CSSProperties,
type PropType,
defineComponent,
computed,
defineComponent,
ref,
unref,
toRefs,
unref,
type CSSProperties,
type PropType,
} from 'vue';
import { get } from 'lodash-es';
import { Descriptions } from 'ant-design-vue';
import { CollapseContainer } from '@/components/Container';
import { useDesign } from '@/hooks/web/useDesign';
import { isFunction } from '@/utils/is';
import { getSlot } from '@/utils/helper/tsxHelper';
import { useAttrs } from '@vben/hooks';
import type { DescInstance, DescItem, DescriptionProps } from './typing';
const props = {
useCollapse: { type: Boolean, default: true },
@ -175,7 +175,7 @@
const { title } = unref(getMergeProps);
return (
<CollapseContainer title={title} canExpan={canExpand} helpMessage={helpMessage}>
<CollapseContainer title={title} canExpand={canExpand} helpMessage={helpMessage}>
{{
default: () => content,
action: () => getSlot(slots, 'action'),

2
src/components/Form/src/BasicForm.vue

@ -285,7 +285,7 @@
if (!autoSubmitOnEnter) return;
if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
const target: HTMLElement = e.target as HTMLElement;
if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
if (target && target.tagName && target.tagName.toUpperCase() === 'INPUT') {
handleSubmit();
}
}

2
src/components/Form/src/components/ApiCascader.vue

@ -46,7 +46,7 @@
type: Array,
},
api: {
type: Function as PropType<(arg?: any) => Promise<Option[]>>,
type: Function as PropType<(arg?: any) => Promise<Option[] | Recordable<any>>>,
default: null,
},
numberToString: propTypes.bool,

2
src/components/Form/src/components/ApiRadioGroup.vue

@ -38,7 +38,7 @@
const props = defineProps({
api: {
type: Function as PropType<(arg?: any) => Promise<OptionsItem[]>>,
type: Function as PropType<(arg?: any) => Promise<OptionsItem[] | Recordable<any>>>,
default: null,
},
params: {

6
src/components/Form/src/components/ApiSelect.vue

@ -39,7 +39,7 @@
value: { type: [Array, Object, String, Number] as PropType<SelectValue> },
numberToString: propTypes.bool,
api: {
type: Function as PropType<(arg?: any) => Promise<OptionsItem[]>>,
type: Function as PropType<(arg?: any) => Promise<OptionsItem[] | Recordable<any>>>,
default: null,
},
// api params
@ -121,10 +121,10 @@
emitChange();
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
// reset status
isFirstLoaded.value = false;
} finally {
loading.value = false;
}
}

2
src/components/Form/src/components/ApiTransfer.vue

@ -25,7 +25,7 @@
const props = defineProps({
value: { type: Array as PropType<Array<string>> },
api: {
type: Function as PropType<(arg) => Promise<TransferItem[]>>,
type: Function as PropType<(arg) => Promise<TransferItem[] | Recordable<any>>>,
default: null,
},
params: { type: Object },

17
src/components/Form/src/hooks/useFormEvents.ts

@ -87,7 +87,7 @@ export function useFormEvents({
const defaultValueObj = schema?.defaultValueObj;
const fieldKeys = Object.keys(defaultValueObj || {});
if (fieldKeys.length) {
fieldKeys.map((field) => {
fieldKeys.forEach((field) => {
formModel[field] = defaultValueObj![field];
});
}
@ -135,6 +135,9 @@ export function useFormEvents({
}
const constructValue = tryConstructArray(key, values) || tryConstructObject(key, values);
const setDateFieldValue = (v) => {
return v ? (_props?.valueFormat ? v : dateUtil(v)) : null;
};
// 0| '' is allow
if (hasKey || !!constructValue) {
@ -144,15 +147,11 @@ export function useFormEvents({
if (Array.isArray(fieldValue)) {
const arr: any[] = [];
for (const ele of fieldValue) {
arr.push(ele ? dateUtil(ele) : null);
arr.push(setDateFieldValue(ele));
}
unref(formModel)[key] = arr;
} else {
unref(formModel)[key] = fieldValue
? _props?.valueFormat
? fieldValue
: dateUtil(fieldValue)
: null;
unref(formModel)[key] = setDateFieldValue(fieldValue);
}
} else {
unref(formModel)[key] = fieldValue;
@ -195,7 +194,7 @@ export function useFormEvents({
fieldList = [fields];
}
for (const field of fieldList) {
_removeSchemaByFeild(field, schemaList);
_removeSchemaByField(field, schemaList);
}
schemaRef.value = schemaList;
}
@ -203,7 +202,7 @@ export function useFormEvents({
/**
* @description: Delete based on field name
*/
function _removeSchemaByFeild(field: string, schemaList: FormSchema[]): void {
function _removeSchemaByField(field: string, schemaList: FormSchema[]): void {
if (isString(field)) {
const index = schemaList.findIndex((schema) => schema.field === field);
if (index !== -1) {

6
src/components/Form/src/hooks/useFormValues.ts

@ -13,7 +13,7 @@ interface UseFormValuesContext {
}
/**
* @desription deconstruct array-link key. This method will mutate the target.
* @description deconstruct array-link key. This method will mutate the target.
*/
function tryDeconstructArray(key: string, value: any, target: Recordable) {
const pattern = /^\[(.+)\]$/;
@ -31,7 +31,7 @@ function tryDeconstructArray(key: string, value: any, target: Recordable) {
}
/**
* @desription deconstruct object-link key. This method will mutate the target.
* @description deconstruct object-link key. This method will mutate the target.
*/
function tryDeconstructObject(key: string, value: any, target: Recordable) {
const pattern = /^\{(.+)\}$/;
@ -138,7 +138,7 @@ export function useFormValues({
const { defaultValue, defaultValueObj } = item;
const fieldKeys = Object.keys(defaultValueObj || {});
if (fieldKeys.length) {
fieldKeys.map((field) => {
fieldKeys.forEach((field) => {
obj[field] = defaultValueObj![field];
if (formModel[field] === undefined) {
formModel[field] = defaultValueObj![field];

2
src/components/Menu/src/components/MenuItemContent.vue

@ -19,7 +19,7 @@
const { t } = useI18n();
const { prefixCls } = useDesign('basic-menu-item-content');
const getI18nName = computed(() => t(props.item?.name));
const getI18nName = computed(() => t(props.item?.meta?.title || props.item?.name));
const getIcon = computed(() => (props.item?.img ? undefined : props.item?.icon));
const getImg = computed(() => props.item?.img);
</script>

52
src/components/Scrollbar/src/bar.ts

@ -28,6 +28,35 @@ export default defineComponent({
});
const barStore = ref<Recordable>({});
const cursorDown = ref();
const mouseMoveDocumentHandler = (e: any) => {
if (cursorDown.value === false) {
return;
}
const prevPage = barStore.value[bar.value.axis];
if (!prevPage) {
return;
}
const offset =
(instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) *
-1;
const thumbClickPosition = thumb.value[bar.value.offset] - prevPage;
const thumbPositionPercentage =
((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset];
wrap.value[bar.value.scroll] =
(thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100;
};
const startDrag = (e: any) => {
e.stopImmediatePropagation();
cursorDown.value = true;
on(document, 'mousemove', mouseMoveDocumentHandler);
on(document, 'mouseup', mouseUpDocumentHandler);
document.onselectstart = () => false;
};
const clickThumbHandler = (e: any) => {
// prevent click event of right button
if (e.ctrlKey || e.button === 2) {
@ -51,29 +80,6 @@ export default defineComponent({
wrap.value[bar.value.scroll] =
(thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100;
};
const startDrag = (e: any) => {
e.stopImmediatePropagation();
cursorDown.value = true;
on(document, 'mousemove', mouseMoveDocumentHandler);
on(document, 'mouseup', mouseUpDocumentHandler);
document.onselectstart = () => false;
};
const mouseMoveDocumentHandler = (e: any) => {
if (cursorDown.value === false) return;
const prevPage = barStore.value[bar.value.axis];
if (!prevPage) return;
const offset =
(instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) *
-1;
const thumbClickPosition = thumb.value[bar.value.offset] - prevPage;
const thumbPositionPercentage =
((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset];
wrap.value[bar.value.scroll] =
(thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100;
};
function mouseUpDocumentHandler() {
cursorDown.value = false;

1
src/components/SimpleMenu/src/SimpleSubMenu.vue

@ -78,7 +78,6 @@
const getShowMenu = computed(() => !props.item?.meta?.hideMenu);
const getIcon = computed(() => (props.item?.img ? undefined : props.item?.icon));
const getImg = computed(() => props.item?.img);
// const getI18nName = computed(() => t(props.item?.name));
const getI18nName = computed(() => {
const locales = props.item.meta && props.item.meta.locales;
const title = props.item.meta && props.item.meta.title;

1
src/components/Table/src/components/HeaderCell.vue

@ -55,7 +55,6 @@
.@{prefix-cls} {
&__help {
margin-left: 8px;
color: rgb(0 0 0 / 65%) !important;
}
}
</style>

2
src/components/Table/src/components/TableImg.vue

@ -5,7 +5,7 @@
v-if="imgList && imgList.length"
:style="getWrapStyle"
>
<Badge :count="!showBadge || imgList.length == 1 ? 0 : imgList.length" v-if="simpleShow">
<Badge :count="!showBadge || imgList.length === 1 ? 0 : imgList.length" v-if="simpleShow">
<div class="img-div">
<Image.PreviewGroup>
<template v-for="(img, index) in imgList" :key="img">

40
src/components/Table/src/components/editable/EditableCell.vue

@ -65,6 +65,19 @@
return ['Checkbox', 'Switch'].includes(component);
});
const getDisable = computed(() => {
const { editDynamicDisabled } = props.column;
let disabled = false;
if (isBoolean(editDynamicDisabled)) {
disabled = editDynamicDisabled;
}
if (isFunction(editDynamicDisabled)) {
const { record } = props;
disabled = editDynamicDisabled({ record, currentValue: currentValueRef.value });
}
return disabled;
});
const getComponentProps = computed(() => {
const isCheckValue = unref(getIsCheckComp);
let compProps = props.column?.editComponentProps ?? ({} as any);
@ -117,18 +130,7 @@
const dataKey = (dataIndex || key) as string;
set(record, dataKey, value);
}
const getDisable = computed(() => {
const { editDynamicDisabled } = props.column;
let disabled = false;
if (isBoolean(editDynamicDisabled)) {
disabled = editDynamicDisabled;
}
if (isFunction(editDynamicDisabled)) {
const { record } = props;
disabled = editDynamicDisabled({ record, currentValue: currentValueRef.value });
}
return disabled;
});
const getValues = computed(() => {
const { editValueMap } = props.column;
@ -149,6 +151,11 @@
return option?.label ?? value;
});
const getRowEditable = computed(() => {
const { editable } = props.record || {};
return !!editable;
});
const getWrapperStyle = computed((): CSSProperties => {
if (unref(getIsCheckComp) || unref(getRowEditable)) {
return {};
@ -163,11 +170,6 @@
return `edit-cell-align-${align}`;
});
const getRowEditable = computed(() => {
const { editable } = props.record || {};
return !!editable;
});
watchEffect(() => {
// defaultValueRef.value = props.value;
currentValueRef.value = props.value;
@ -191,7 +193,7 @@
});
}
async function handleChange(e: any) {
async function handleChange(e: any, ...rest: any[]) {
const component = unref(getComponent);
if (!e) {
currentValueRef.value = e;
@ -205,7 +207,7 @@
currentValueRef.value = e;
}
const onChange = unref(getComponentProps)?.onChangeTemp;
if (onChange && isFunction(onChange)) onChange(...arguments);
if (onChange && isFunction(onChange)) onChange(e, ...rest);
table.emit?.('edit-change', {
column: props.column,

12
src/components/Table/src/components/settings/ColumnSetting.vue

@ -149,6 +149,12 @@
return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
};
//
let defaultIsIndexColumnShow: boolean = false;
let defaultIsRowSelectionShow: boolean = false;
let defaultRowSelection: TableRowSelection<Recordable<any>>;
let defaultColumnOptions: ColumnOptionsType[] = [];
//
let isRestored = false;
let isInnerChange = false;
@ -518,12 +524,6 @@
tableColumnsUpdate();
};
//
let defaultIsIndexColumnShow: boolean = false;
let defaultIsRowSelectionShow: boolean = false;
let defaultRowSelection: TableRowSelection<Recordable<any>>;
let defaultColumnOptions: ColumnOptionsType[] = [];
const init = async () => {
if (!isRestored) {
//

2
src/components/Table/src/hooks/useDataSource.ts

@ -211,7 +211,7 @@ export function useDataSource(
}
function findTableDataRecord(keyValue: Key) {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
if (!dataSourceRef.value || dataSourceRef.value.length === 0) return;
const { childrenColumnName = 'children' } = unref(propsRef);
const findRow = (array: any[]) => {

6
src/components/Table/src/hooks/usePagination.tsx → src/components/Table/src/hooks/usePagination.ts

@ -1,6 +1,6 @@
import type { PaginationProps } from '../types/pagination';
import type { BasicTableProps } from '../types/table';
import { computed, unref, ref, ComputedRef, watch } from 'vue';
import { computed, unref, ref, ComputedRef, watch, h } from 'vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import { isBoolean } from '@/utils/is';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
@ -14,9 +14,9 @@ interface ItemRender {
function itemRender({ page, type, originalElement }: ItemRender) {
if (type === 'prev') {
return page === 0 ? null : <LeftOutlined />;
return page === 0 ? null : h(LeftOutlined);
} else if (type === 'next') {
return page === 1 ? null : <RightOutlined />;
return page === 1 ? null : h(RightOutlined);
}
return originalElement;
}

2
src/components/Table/src/hooks/useTableExpand.ts

@ -105,7 +105,7 @@ export function useTableExpand(
}
// 监听展开事件,用于支持手风琴展开效果
function handleTableExpand(expanded, record) {
function handleTableExpand(expanded: boolean, record: Recordable) {
// 手风琴开关
// isTreeTable 或 expandRowByClick 时支持
// 展开操作

34
src/components/VirtualScroll/src/VirtualScroll.vue

@ -52,7 +52,7 @@
export default defineComponent({
name: 'VirtualScroll',
props,
setup(props, { slots }) {
setup(props, { slots, expose }) {
const wrapElRef = ref<HTMLDivElement | null>(null);
const state = reactive({
first: 0,
@ -128,6 +128,31 @@
state.last = getLast(state.first);
}
function scrollToTop() {
const wrapEl = unref(wrapElRef);
if (!wrapEl) {
return;
}
wrapEl.scrollTop = 0;
}
function scrollToBottom() {
const wrapEl = unref(wrapElRef);
if (!wrapEl) {
return;
}
wrapEl.scrollTop = wrapEl.scrollHeight;
}
function scrollToItem(index: number) {
const wrapEl = unref(wrapElRef);
if (!wrapEl) {
return;
}
const i = index - 1 > 0 ? index - 1 : 0;
wrapEl.scrollTop = i * unref(getItemHeightRef);
}
function renderChildren() {
const { items = [] } = props;
return items.slice(unref(getFirstToRenderRef), unref(getLastToRenderRef)).map(genChild);
@ -143,6 +168,13 @@
);
}
expose({
wrapElRef,
scrollToTop,
scrollToItem,
scrollToBottom,
});
onMounted(() => {
state.last = getLast(0);
nextTick(() => {

12
src/components/VxeTable/src/components/common.tsx

@ -7,7 +7,7 @@ import {
import XEUtils from 'xe-utils';
import { componentMap } from '../componentMap';
import { ComponentType } from '../componentType';
import { createPlaceholderMessage } from '../helper';
import { createPlaceholderMessage, sanitizeInputWhitespace } from '../helper';
/**
* @description:
@ -102,13 +102,13 @@ export function createEvents(
};
});
if (inputFunc) {
ons[getOnName(modelEvent)] = function (targetEvnt: any) {
inputFunc(targetEvnt);
ons[getOnName(modelEvent)] = function (targetEvent: any) {
inputFunc(targetEvent);
if (events && events[modelEvent]) {
events[modelEvent](params, targetEvnt);
events[modelEvent](params, targetEvent);
}
if (isSameEvent && changeFunc) {
changeFunc(targetEvnt);
changeFunc(targetEvent);
}
};
}
@ -323,7 +323,7 @@ export function createFormItemRender(
params,
(value: any) => {
// 处理 model 值双向绑定
XEUtils.set(data, property, value);
XEUtils.set(data, property, sanitizeInputWhitespace(name as ComponentType, value));
},
() => {
// 处理 change 事件相关逻辑

5
src/components/VxeTable/src/const.ts

@ -2,3 +2,8 @@
* @description: vxe-table prop
*/
export const ignorePropKeys = ['tableClass', 'tableStyle'];
/**
* @description:
*/
export const ignoreTrimInputComponents = ['AInput', 'ATextarea'];

13
src/components/VxeTable/src/helper.ts

@ -1,5 +1,7 @@
import { ComponentType } from './componentType';
import { useI18n } from '@/hooks/web/useI18n';
import XEUtils from 'xe-utils';
import { ignoreTrimInputComponents } from './const';
const { t } = useI18n();
@ -17,3 +19,14 @@ export function createPlaceholderMessage(component: ComponentType) {
return t('common.chooseText');
}
}
/**
*
* @description:
*/
export function sanitizeInputWhitespace(component: ComponentType, value: string) {
if (ignoreTrimInputComponents.includes(component)) {
return XEUtils.trim(value);
}
return value;
}

3
src/design/ant/input.less

@ -4,8 +4,7 @@
.ant-input {
&-number,
&-number-group-wrapper {
width: 100% !important;
min-width: 110px;
width: 100%;
max-width: 100%;
}
}

2
src/enums/appEnum.ts

@ -32,7 +32,7 @@ export enum PermissionModeEnum {
// role
// 角色权限
ROLE = 'ROLE',
// black
// back
// 后端
BACK = 'BACK',
// route mapping

1
src/hooks/web/usePermission.ts

@ -41,7 +41,6 @@ export function usePermission() {
/**
* Reset and regain authority resource information
*
* @param id
*/
async function resume() {
const tabStore = useMultipleTabStore();

4
src/layouts/default/header/components/Breadcrumb.vue

@ -4,10 +4,10 @@
<template #itemRender="{ route, routes: routesMatched, paths }">
<Icon :icon="getIcon(route)" v-if="getShowBreadCrumbIcon && getIcon(route)" />
<span v-if="!hasRedirect(routesMatched, route)">
{{ t(route.name || route.meta.title) }}
{{ t(route.meta.title || route.name) }}
</span>
<router-link v-else to="" @click="handleClick(route, paths, $event as Event)">
{{ t(route.name || route.meta.title) }}
{{ t(route.meta.title || route.name) }}
</router-link>
</template>
</a-breadcrumb>

2
src/layouts/default/sider/MixSider.vue

@ -43,7 +43,7 @@
:icon="item.icon || (item.meta && item.meta.icon)"
/>
<p :class="`${prefixCls}-module__name`">
{{ t(item.name) }}
{{ t(item?.meta?.title || item.name) }}
</p>
</li>
</ul>

4
src/router/helper/menuHelper.ts

@ -61,12 +61,12 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
// 提取树指定结构
const list = treeMap(routeList, {
conversion: (node: AppRouteRecordRaw) => {
const { meta: { title, hideMenu = false } = {} } = node;
const { meta: { hideMenu = false } = {}, name } = node;
return {
...(node.meta || {}),
meta: node.meta,
name: title,
name,
hideMenu,
path: node.path,
...(node.redirect ? { redirect: node.redirect } : {}),

2
src/utils/helper/tsxHelper.tsx → src/utils/helper/tsxHelper.ts

@ -27,7 +27,7 @@ export function getSlot(slots: Slots, slot = 'default', data?: any, opts?: Rende
export function extendSlots(slots: Slots, excludeKeys: string[] = []) {
const slotKeys = Object.keys(slots);
const ret: any = {};
slotKeys.map((key) => {
slotKeys.forEach((key) => {
if (excludeKeys.includes(key)) {
return null;
}

2
src/utils/index.ts

@ -97,7 +97,7 @@ export function openWindow(
export function getDynamicProps<T extends Record<string, unknown>, U>(props: T): Partial<U> {
const ret: Recordable = {};
Object.keys(props).map((key) => {
Object.keys(props).forEach((key) => {
ret[key] = unref((props as Recordable)[key]);
});

6
src/views/demo/comp/qrcode/index.vue

@ -1,7 +1,7 @@
<template>
<PageWrapper title="二维码组件使用示例">
<div class="flex flex-wrap">
<CollapseContainer title="基础示例" :canExpan="true" class="text-center mb-6 w-1/5 mr-6">
<CollapseContainer title="基础示例" :canExpand="true" class="text-center mb-6 w-1/5 mr-6">
<QrCode :value="qrCodeUrl" />
</CollapseContainer>
@ -79,13 +79,13 @@
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { QrCode, QrCodeActionType } from '@/components/Qrcode';
import LogoImg from '@/assets/images/logo.png';
import { CollapseContainer } from '@/components/Container';
import { PageWrapper } from '@/components/Page';
import { QrCode, QrCodeActionType } from '@/components/Qrcode';
import { type Nullable } from '@vben/types';
import { QRCode } from 'ant-design-vue';
import { ref, unref } from 'vue';
const qrCodeUrl = 'https://www.vvbin.cn';
const qrRef = ref<Nullable<QrCodeActionType>>(null);

28
src/views/demo/comp/scroll/VirtualScroll.vue

@ -1,8 +1,26 @@
<template>
<PageWrapper class="virtual-scroll-demo">
<Divider>基础滚动示例</Divider>
<div class="text-center mb-4">
<a-button @click="vScrollRef?.scrollToTop()">滚动到顶部</a-button>
<a-button @click="vScrollRef?.scrollToBottom()" class="mx-2">滚动到底部</a-button>
<a-button @click="vScrollRef?.scrollToItem(scrollToItemIndex)"
>滚动到第
<input-number
v-model:value="scrollToItemIndex"
class="!w-60px mx-1"
:min="1"
:max="data.length"
:precision="0"
size="small"
:controls="false"
@keydown.enter="vScrollRef?.scrollToItem(scrollToItemIndex)"
/>
</a-button>
</div>
<div class="virtual-scroll-demo-wrap">
<VScroll :itemHeight="41" :items="data" :height="300" :width="300">
<VScroll :itemHeight="41" :items="data" :height="300" :width="300" ref="vScrollRef">
<template #default="{ item }">
<div class="virtual-scroll-demo__item">
{{ item.title }}
@ -24,9 +42,13 @@
</PageWrapper>
</template>
<script lang="ts" setup>
import { VScroll } from '@/components/VirtualScroll';
import { Divider } from 'ant-design-vue';
import { PageWrapper } from '@/components/Page';
import { VScroll } from '@/components/VirtualScroll';
import { Divider, InputNumber } from 'ant-design-vue';
import { ref } from 'vue';
const vScrollRef = ref<typeof VScroll>();
const scrollToItemIndex = ref(1000);
const data = (() => {
const arr: any[] = [];

12
src/views/demo/page/account/center/index.vue

@ -23,7 +23,7 @@
</Row>
</Col>
<Col :span="7" :class="`${prefixCls}-col`">
<CollapseContainer title="标签" :canExpan="false">
<CollapseContainer title="标签" :canExpand="false">
<template v-for="tag in tags" :key="tag">
<Tag class="mb-2">
{{ tag }}
@ -32,7 +32,7 @@
</CollapseContainer>
</Col>
<Col :span="8" :class="`${prefixCls}-col`">
<CollapseContainer :class="`${prefixCls}-top__team`" title="团队" :canExpan="false">
<CollapseContainer :class="`${prefixCls}-top__team`" title="团队" :canExpand="false">
<div v-for="(team, index) in teams" :key="index" :class="`${prefixCls}-top__team-item`">
<Icon :icon="team.icon" :color="team.color" />
<span>{{ team.title }}</span>
@ -53,17 +53,17 @@
</template>
<script lang="ts" setup>
import { Tag, Tabs, Row, Col } from 'ant-design-vue';
import { computed } from 'vue';
import { CollapseContainer } from '@/components/Container';
import Icon from '@/components/Icon/Icon.vue';
import Article from './Article.vue';
import { Col, Row, Tabs, Tag } from 'ant-design-vue';
import { computed } from 'vue';
import Application from './Application.vue';
import Article from './Article.vue';
import Project from './Project.vue';
import headerImg from '@/assets/images/header.jpg';
import { tags, teams, details, achieveList } from './data';
import { useUserStore } from '@/store/modules/user';
import { achieveList, details, tags, teams } from './data';
const userStore = useUserStore();
const TabPane = Tabs.TabPane;

4
src/views/demo/page/account/setting/AccountBind.vue

@ -1,5 +1,5 @@
<template>
<CollapseContainer title="账号绑定" :canExpan="false">
<CollapseContainer title="账号绑定" :canExpand="false">
<List>
<template v-for="item in accountBindList" :key="item.key">
<ListItem>
@ -23,9 +23,9 @@
</CollapseContainer>
</template>
<script lang="ts" setup>
import { List } from 'ant-design-vue';
import { CollapseContainer } from '@/components/Container';
import Icon from '@/components/Icon/Icon.vue';
import { List } from 'ant-design-vue';
import { accountBindList } from './data';

14
src/views/demo/page/account/setting/BaseSetting.vue

@ -1,5 +1,5 @@
<template>
<CollapseContainer title="基本设置" :canExpan="false">
<CollapseContainer title="基本设置" :canExpand="false">
<Row :gutter="24">
<Col :span="14">
<BasicForm @register="register" />
@ -22,19 +22,19 @@
</CollapseContainer>
</template>
<script lang="ts" setup>
import { Row, Col } from 'ant-design-vue';
import { computed, onMounted } from 'vue';
import { BasicForm, useForm } from '@/components/Form';
import { CollapseContainer } from '@/components/Container';
import { CropperAvatar } from '@/components/Cropper';
import { BasicForm, useForm } from '@/components/Form';
import { Col, Row } from 'ant-design-vue';
import { computed, onMounted } from 'vue';
import { useMessage } from '@/hooks/web/useMessage';
import headerImg from '@/assets/images/header.jpg';
import { accountInfoApi } from '@/api/demo/account';
import { baseSetschemas } from './data';
import { useUserStore } from '@/store/modules/user';
import { uploadApi } from '@/api/sys/upload';
import headerImg from '@/assets/images/header.jpg';
import { useUserStore } from '@/store/modules/user';
import { baseSetschemas } from './data';
const { createMessage } = useMessage();
const userStore = useUserStore();

4
src/views/demo/page/account/setting/MsgNotify.vue

@ -1,5 +1,5 @@
<template>
<CollapseContainer title="新消息通知" :canExpan="false">
<CollapseContainer title="新消息通知" :canExpand="false">
<List>
<template v-for="item in msgNotifyList" :key="item.key">
<ListItem>
@ -23,8 +23,8 @@
</CollapseContainer>
</template>
<script lang="ts" setup>
import { List, Switch } from 'ant-design-vue';
import { CollapseContainer } from '@/components/Container';
import { List, Switch } from 'ant-design-vue';
import { msgNotifyList } from './data';
const ListItem = List.Item;

4
src/views/demo/page/account/setting/SecureSetting.vue

@ -1,5 +1,5 @@
<template>
<CollapseContainer title="安全设置" :canExpan="false">
<CollapseContainer title="安全设置" :canExpand="false">
<List>
<template v-for="item in secureSettingList" :key="item.key">
<ListItem>
@ -23,8 +23,8 @@
</CollapseContainer>
</template>
<script lang="ts" setup>
import { List } from 'ant-design-vue';
import { CollapseContainer } from '@/components/Container';
import { List } from 'ant-design-vue';
import { secureSettingList } from './data';
const ListItem = List.Item;

4
src/views/form-design/components/VFormDesign/components/ComponentProps.vue

@ -199,14 +199,14 @@
//
const controlOptions = computed(() => {
return allOptions.value.filter((item) => {
return item.category == 'control';
return item.category === 'control';
});
});
//
const inputOptions = computed(() => {
return allOptions.value.filter((item) => {
return item.category == 'input';
return item.category === 'input';
});
});

2
src/views/form-design/components/VFormDesign/components/FormProps.vue

@ -65,7 +65,7 @@
</div>
<FormItem label="表单属性">
<Col
><Checkbox v-model:checked="formConfig.colon" v-if="formConfig.layout == 'horizontal'"
><Checkbox v-model:checked="formConfig.colon" v-if="formConfig.layout === 'horizontal'"
>label后面显示冒号</Checkbox
></Col
>

2
src/views/form-design/components/VFormDesign/config/componentPropsConfig.ts

@ -1099,7 +1099,7 @@ const componentAttrs: IBaseComponentProps = {
function deleteProps(list: Omit<IBaseFormAttrs, 'tag'>[], key: string) {
list.forEach((element, index) => {
if (element.name == key) {
if (element.name === key) {
list.splice(index, 1);
}
});

Loading…
Cancel
Save