Browse Source

synchronize vben admin changes

pull/744/head
cKey 3 years ago
parent
commit
b52d4f71e2
  1. 1
      apps/vue/build/vite/plugin/styleImport.ts
  2. 2
      apps/vue/package.json
  3. 9
      apps/vue/src/components/Button/src/props.ts
  4. 4
      apps/vue/src/components/Cropper/src/CropperAvatar.vue
  5. 24
      apps/vue/src/components/Excel/src/Export2Excel.ts
  6. 4
      apps/vue/src/components/Excel/src/ImportExcel.vue
  7. 12
      apps/vue/src/components/Form/src/BasicForm.vue
  8. 4
      apps/vue/src/components/Form/src/TabForm.vue
  9. 9
      apps/vue/src/components/Form/src/components/ApiSelect.vue
  10. 9
      apps/vue/src/components/Form/src/components/ApiTransfer.vue
  11. 7
      apps/vue/src/components/Form/src/components/FormItem.vue
  12. 9
      apps/vue/src/components/Form/src/hooks/useAdvanced.ts
  13. 4
      apps/vue/src/components/Form/src/hooks/useForm.ts
  14. 24
      apps/vue/src/components/Form/src/hooks/useFormEvents.ts
  15. 13
      apps/vue/src/components/Form/src/hooks/useFormValues.ts
  16. 4
      apps/vue/src/components/Form/src/props.ts
  17. 7
      apps/vue/src/components/Form/src/types/form.ts
  18. 2
      apps/vue/src/components/Menu/src/props.ts
  19. 10
      apps/vue/src/components/Preview/src/Functional.vue
  20. 3
      apps/vue/src/components/Table/src/BasicTable.vue
  21. 6
      apps/vue/src/components/Table/src/componentMap.ts
  22. 12
      apps/vue/src/components/Table/src/components/TableImg.vue
  23. 4
      apps/vue/src/components/Table/src/components/editable/EditableCell.vue
  24. 485
      apps/vue/src/components/Table/src/components/settings/ColumnSetting.vue
  25. 2
      apps/vue/src/components/Table/src/hooks/useCustomRow.ts
  26. 4
      apps/vue/src/components/Table/src/hooks/useRowSelection.ts
  27. 3
      apps/vue/src/components/Table/src/hooks/useTable.ts
  28. 5
      apps/vue/src/components/Table/src/types/componentType.ts
  29. 3
      apps/vue/src/components/Table/src/types/table.ts
  30. 2
      apps/vue/src/components/Tinymce/src/Editor.vue
  31. 4
      apps/vue/src/components/Tree/index.ts
  32. 78
      apps/vue/src/components/Tree/src/BasicTree.vue
  33. 16
      apps/vue/src/components/Tree/src/components/TreeHeader.vue
  34. 2
      apps/vue/src/components/Tree/src/hooks/useTree.ts
  35. 108
      apps/vue/src/components/Tree/src/props.ts
  36. 3
      apps/vue/src/components/Tree/src/types/tree.ts
  37. 54
      apps/vue/src/components/Tree/src/typing.ts
  38. 2
      apps/vue/src/components/Upload/src/props.ts
  39. 10
      apps/vue/src/components/Verify/src/props.ts
  40. 8
      apps/vue/src/design/ant/index.less
  41. 5
      apps/vue/src/design/ant/input.less
  42. 8
      apps/vue/src/hooks/web/useECharts.ts
  43. 7
      apps/vue/src/layouts/default/header/components/FullScreen.vue
  44. 2
      apps/vue/src/layouts/default/setting/components/ThemeColorPicker.vue
  45. 2
      apps/vue/src/layouts/default/setting/components/TypePicker.vue
  46. 2
      apps/vue/src/locales/lang/en/layout.ts
  47. 2
      apps/vue/src/router/constant.ts
  48. 1
      apps/vue/src/store/modules/multipleTab.ts
  49. 11
      apps/vue/src/utils/dateUtil.ts
  50. 6
      apps/vue/src/utils/http/axios/index.ts
  51. 6
      apps/vue/src/utils/index.ts
  52. 3
      apps/vue/src/utils/is.ts
  53. 4
      apps/vue/src/views/account/setting/BaseSetting.vue
  54. 3
      apps/vue/src/views/oss-management/objects/components/FileList.vue
  55. 2
      apps/vue/src/views/platform/menu/hooks/useMenuFormContext.ts

1
apps/vue/build/vite/plugin/styleImport.ts

@ -49,6 +49,7 @@ export function configStyleImportPlugin(_isBuild: boolean) {
// 这里是需要额外引入样式的子组件列表
// 单独引入子组件时需引入组件样式,否则会在打包后导致子组件样式丢失
const replaceList = {
textarea: 'input',
'typography-text': 'typography',
'typography-title': 'typography',
'typography-paragraph': 'typography',

2
apps/vue/package.json

@ -149,7 +149,7 @@
"vite-plugin-vue-setup-extend": "^0.4.0",
"vite-plugin-windicss": "^1.8.4",
"vue-eslint-parser": "^8.3.0",
"vue-tsc": "^0.33.9"
"vue-tsc": "^1.0.9"
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china",

9
apps/vue/src/components/Button/src/props.ts

@ -1,5 +1,12 @@
const validColors = ['error', 'warning', 'success', ''] as const;
type ButtonColorType = typeof validColors[number];
export const buttonProps = {
color: { type: String, validator: (v) => ['error', 'warning', 'success', ''].includes(v) },
color: {
type: String as PropType<ButtonColorType>,
validator: (v) => validColors.includes(v),
default: '',
},
loading: { type: Boolean },
disabled: { type: Boolean },
/**

4
apps/vue/src/components/Cropper/src/CropperAvatar.vue

@ -92,9 +92,9 @@
},
);
function handleUploadSuccess({ source }) {
function handleUploadSuccess({ source, data }) {
sourceValue.value = source;
emits('change', source);
emits('change', { source, data });
createMessage.success(t('component.cropper.uploadSuccess'));
}

24
apps/vue/src/components/Excel/src/Export2Excel.ts

@ -6,6 +6,28 @@ const { utils, writeFile } = xlsx;
const DEF_FILE_NAME = 'excel-list.xlsx';
/**
* @param data source data
* @param worksheet worksheet object
* @param min min width
*/
function setColumnWidth(data, worksheet, min = 3) {
const obj = {};
worksheet['!cols'] = [];
data.forEach((item) => {
Object.keys(item).forEach((key) => {
const cur = item[key];
const length = cur?.length ?? min;
obj[key] = Math.max(length, obj[key] ?? min);
});
});
Object.keys(obj).forEach((key) => {
worksheet['!cols'].push({
wch: obj[key],
});
});
}
export function jsonToSheetXlsx<T = any>({
data,
header,
@ -20,7 +42,7 @@ export function jsonToSheetXlsx<T = any>({
}
const worksheet = utils.json_to_sheet(arrData, json2sheetOpts);
setColumnWidth(arrData, worksheet);
/* add worksheet to workbook */
const workbook: WorkBook = {
SheetNames: [filename],

4
apps/vue/src/components/Excel/src/ImportExcel.vue

@ -137,8 +137,10 @@
* @description: 触发选择文件管理器
*/
function handleInputClick(e: Event) {
const files = e && (e.target as HTMLInputElement).files;
const target = e && (e.target as HTMLInputElement);
const files = target?.files;
const rawFile = files && files[0]; // only setting files[0]
target.value = '';
if (!rawFile) return;
upload(rawFile);
}

12
apps/vue/src/components/Form/src/BasicForm.vue

@ -10,6 +10,7 @@
<slot name="formHeader"></slot>
<template v-for="schema in getSchema" :key="schema.field">
<FormItem
:isAdvanced="fieldsIsAdvancedMap[schema.field]"
:tableAction="tableAction"
:formActionType="formActionType"
:schema="schema"
@ -117,9 +118,9 @@
const getSchema = computed((): FormSchema[] => {
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
for (const schema of schemas) {
const { defaultValue, component } = schema;
const { defaultValue, component, isHandleDateDefaultValue = true } = schema;
// handle date type
if (defaultValue && dateItemType.includes(component)) {
if (isHandleDateDefaultValue && defaultValue && dateItemType.includes(component)) {
if (!Array.isArray(defaultValue)) {
schema.defaultValue = dateUtil(defaultValue);
} else {
@ -138,7 +139,7 @@
}
});
const { handleToggleAdvanced } = useAdvanced({
const { handleToggleAdvanced, fieldsIsAdvancedMap } = useAdvanced({
advanceState,
emit,
getProps,
@ -171,7 +172,7 @@
updateSchema,
resetSchema,
appendSchemaByField,
removeSchemaByFiled,
removeSchemaByField,
resetFields,
scrollToField,
} = useFormEvents({
@ -265,7 +266,7 @@
updateSchema,
resetSchema,
setProps,
removeSchemaByFiled,
removeSchemaByField,
appendSchemaByField,
clearValidate,
validateFields,
@ -296,6 +297,7 @@
getFormActionBindProps: computed(
(): Recordable => ({ ...getProps.value, ...advanceState }),
),
fieldsIsAdvancedMap,
...formActionType,
};
},

4
apps/vue/src/components/Form/src/TabForm.vue

@ -214,7 +214,7 @@
updateSchema,
resetSchema,
appendSchemaByField,
removeSchemaByFiled,
removeSchemaByField,
resetFields,
scrollToField,
} = useFormEvents({
@ -319,7 +319,7 @@
updateSchema,
resetSchema,
setProps,
removeSchemaByFiled,
removeSchemaByField,
appendSchemaByField,
clearValidate,
validateFields,

9
apps/vue/src/components/Form/src/components/ApiSelect.vue

@ -59,7 +59,7 @@
immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false),
},
emits: ['options-change', 'change'],
emits: ['options-change', 'change', 'update:value'],
setup(props, { emit }) {
const options = ref<OptionsItem[]>([]);
const loading = ref(false);
@ -91,6 +91,13 @@
props.immediate && !props.alwaysLoad && fetch();
});
watch(
() => state.value,
(v) => {
emit('update:value', v);
},
);
watch(
() => props.params,
() => {

9
apps/vue/src/components/Form/src/components/ApiTransfer.vue

@ -1,7 +1,6 @@
<template>
<Transfer
:data-source="getdataSource"
show-search
:filter-option="filterOption"
:render="(item) => item.title"
:showSelectAll="showSelectAll"
@ -24,13 +23,13 @@
name: 'ApiTransfer',
components: { Transfer },
props: {
value: { type: Array<string> },
value: { type: Array as PropType<Array<string>> },
api: {
type: Function as PropType<(arg?: Recordable) => Promise<TransferItem[]>>,
default: null,
},
params: { type: Object },
dataSource: { type: Array<TransferItem> },
dataSource: { type: Array as PropType<Array<TransferItem>> },
immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false),
afterFetch: { type: Function as PropType<Fn> },
@ -42,9 +41,9 @@
filterOption: {
type: Function as PropType<(inputValue: string, item: TransferItem) => boolean>,
},
selectedKeys: { type: Array<string> },
selectedKeys: { type: Array as PropType<Array<string>> },
showSelectAll: { type: Boolean, default: false },
targetKeys: { type: Array<string> },
targetKeys: { type: Array as PropType<Array<string>> },
},
emits: ['options-change', 'change'],
setup(props, { attrs, emit }) {

7
apps/vue/src/components/Form/src/components/FormItem.vue

@ -43,6 +43,9 @@
formActionType: {
type: Object as PropType<FormActionType>,
},
isAdvanced: {
type: Boolean,
},
},
setup(props, { slots }) {
const { t } = useI18n();
@ -102,8 +105,8 @@
const { show, ifShow } = props.schema;
const { showAdvancedButton } = props.formProps;
const itemIsAdvanced = showAdvancedButton
? isBoolean(props.schema.isAdvanced)
? props.schema.isAdvanced
? isBoolean(props.isAdvanced)
? props.isAdvanced
: true
: true;

9
apps/vue/src/components/Form/src/hooks/useAdvanced.ts

@ -2,7 +2,7 @@ import type { ColEx } from '../types';
import type { AdvanceState } from '../types/hooks';
import type { ComputedRef, Ref } from 'vue';
import type { FormProps, FormSchema } from '../types/form';
import { computed, unref, watch } from 'vue';
import { computed, unref, watch, shallowReactive } from 'vue';
import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
import { useDebounceFn } from '@vueuse/core';
@ -111,6 +111,8 @@ export default function ({
}
}
const fieldsIsAdvancedMap = shallowReactive({});
function updateAdvanced() {
let itemColSum = 0;
let realItemColSum = 0;
@ -146,7 +148,8 @@ export default function ({
if (isAdvanced) {
realItemColSum = itemColSum;
}
schema.isAdvanced = isAdvanced;
fieldsIsAdvancedMap[schema.field] = isAdvanced;
}
}
@ -161,5 +164,5 @@ export default function ({
advanceState.isAdvanced = !advanceState.isAdvanced;
}
return { handleToggleAdvanced };
return { handleToggleAdvanced, fieldsIsAdvancedMap };
}

4
apps/vue/src/components/Form/src/hooks/useForm.ts

@ -79,8 +79,8 @@ export function useForm(props?: Props): UseFormReturnType {
});
},
removeSchemaByFiled: async (field: string | string[]) => {
unref(formRef)?.removeSchemaByFiled(field);
removeSchemaByField: async (field: string | string[]) => {
unref(formRef)?.removeSchemaByField(field);
},
// TODO promisify

24
apps/vue/src/components/Form/src/hooks/useFormEvents.ts

@ -2,7 +2,15 @@ import type { ComputedRef, Ref } from 'vue';
import type { FormProps, FormSchema, FormActionType } from '../types/form';
import type { NamePath } from 'ant-design-vue/lib/form/interface';
import { unref, toRaw, nextTick } from 'vue';
import { isArray, isFunction, isObject, isString, isDef, isNullOrUnDef } from '/@/utils/is';
import {
isArray,
isFunction,
isObject,
isString,
isDef,
isNullOrUnDef,
isEmpty,
} from '/@/utils/is';
import { deepMerge } from '/@/utils';
import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper';
import { dateUtil } from '/@/utils/dateUtil';
@ -94,7 +102,7 @@ export function useFormEvents({
} else {
nestKeyArray.forEach((nestKey: string) => {
try {
const value = eval('values' + delimiter + nestKey);
const value = nestKey.split('.').reduce((out, item) => out[item], values);
if (isDef(value)) {
formModel[nestKey] = value;
validKeys.push(nestKey);
@ -114,7 +122,7 @@ export function useFormEvents({
/**
* @description: Delete based on field name
*/
async function removeSchemaByFiled(fields: string | string[]): Promise<void> {
async function removeSchemaByField(fields: string | string[]): Promise<void> {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
if (!fields) {
return;
@ -125,7 +133,7 @@ export function useFormEvents({
fieldList = [fields];
}
for (const field of fieldList) {
_removeSchemaByFiled(field, schemaList);
_removeSchemaByField(field, schemaList);
}
schemaRef.value = schemaList;
}
@ -133,7 +141,7 @@ export function useFormEvents({
/**
* @description: Delete based on field name
*/
function _removeSchemaByFiled(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) {
@ -239,7 +247,9 @@ export function useFormEvents({
Reflect.has(item, 'field') &&
item.field &&
!isNullOrUnDef(item.defaultValue) &&
!(item.field in currentFieldsValue)
(!(item.field in currentFieldsValue) ||
isNullOrUnDef(currentFieldsValue[item.field]) ||
isEmpty(currentFieldsValue[item.field]))
) {
obj[item.field] = item.defaultValue;
}
@ -308,7 +318,7 @@ export function useFormEvents({
updateSchema,
resetSchema,
appendSchemaByField,
removeSchemaByFiled,
removeSchemaByField,
resetFields,
setFieldsValue,
scrollToField,

13
apps/vue/src/components/Form/src/hooks/useFormValues.ts

@ -97,14 +97,21 @@ export function useFormValues({
}
for (const [field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD'] of fieldMapToTime) {
if (!field || !startTimeKey || !endTimeKey || !values[field]) {
if (!field || !startTimeKey || !endTimeKey) {
continue;
}
// If the value to be converted is empty, remove the field
if (!values[field]) {
Reflect.deleteProperty(values, field);
continue;
}
const [startTime, endTime]: string[] = values[field];
values[startTimeKey] = dateUtil(startTime).format(format);
values[endTimeKey] = dateUtil(endTime).format(format);
const [startTimeFormat, endTimeFormat] = Array.isArray(format) ? format : [format, format];
values[startTimeKey] = dateUtil(startTime).format(startTimeFormat);
values[endTimeKey] = dateUtil(endTime).format(endTimeFormat);
Reflect.deleteProperty(values, field);
}

4
apps/vue/src/components/Form/src/props.ts

@ -9,7 +9,7 @@ import { propTypes } from '/@/utils/propTypes';
export const basicProps = {
model: {
type: Object as PropType<Recordable>,
default: {},
default: () => ({}),
},
// 标签宽度 固定宽度
labelWidth: {
@ -45,7 +45,7 @@ export const basicProps = {
// 禁用表单
disabled: propTypes.bool,
emptySpan: {
type: [Number, Object] as PropType<number>,
type: [Number, Object] as PropType<number | Recordable>,
default: 0,
},
// 是否显示收起展开按钮

7
apps/vue/src/components/Form/src/types/form.ts

@ -7,7 +7,7 @@ import type { TableActionType } from '/@/components/Table/src/types/table';
import type { CSSProperties } from 'vue';
import type { RowProps } from 'ant-design-vue/lib/grid/Row';
export type FieldMapToTime = [string, [string, string], string?][];
export type FieldMapToTime = [string, [string, string], (string | [string, string])?][];
export type Rule = RuleObject & {
trigger?: 'blur' | 'change' | ['change', 'blur'];
@ -33,7 +33,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>;
removeSchemaByFiled: (field: string | string[]) => Promise<void>;
removeSchemaByField: (field: string | string[]) => Promise<void>;
appendSchemaByField: (
schema: FormSchema,
prefixField: string | undefined,
@ -187,6 +187,9 @@ export interface FormSchema {
// 默认值
defaultValue?: any;
// 是否自动处理与时间相关组件的默认值
isHandleDateDefaultValue?: boolean;
isAdvanced?: boolean;
// Matching details components

2
apps/vue/src/components/Menu/src/props.ts

@ -41,7 +41,7 @@ export const basicProps = {
export const itemProps = {
item: {
type: Object as PropType<Menu>,
default: {},
default: () => ({}),
},
level: propTypes.number,
theme: propTypes.oneOf(['dark', 'light']),

10
apps/vue/src/components/Preview/src/Functional.vue

@ -141,8 +141,16 @@
}
//
function scaleFunc(num: number) {
//
const MIN_SCALE = 0.02;
//
const GRA = 0.1;
if (imgState.imgScale <= 0.2 && num < 0) return;
imgState.imgScale += num;
imgState.imgScale += num * GRA;
// scale < 0
if (imgState.imgScale < 0) {
imgState.imgScale = MIN_SCALE;
}
}
//

3
apps/vue/src/components/Table/src/BasicTable.vue

@ -163,6 +163,7 @@
getRowSelection,
getRowSelectionRef,
getSelectRows,
setSelectedRows,
clearSelectedRowKeys,
getSelectRowKeys,
deleteSelectRowByKey,
@ -328,6 +329,7 @@
const tableAction: TableActionType = {
reload,
getSelectRows,
setSelectedRows,
clearSelectedRowKeys,
getSelectRowKeys,
deleteSelectRowByKey,
@ -439,6 +441,7 @@
padding: 16px;
.ant-form {
width: 100%;
padding: 12px 10px 6px;
margin-bottom: 16px;
background-color: @component-background;

6
apps/vue/src/components/Table/src/componentMap.ts

@ -8,9 +8,10 @@ import {
DatePicker,
TimePicker,
AutoComplete,
Radio,
} from 'ant-design-vue';
import type { ComponentType } from './types/componentType';
import { ApiSelect, ApiTreeSelect } from '/@/components/Form';
import { ApiSelect, ApiTreeSelect, RadioButtonGroup, ApiRadioGroup } from '/@/components/Form';
const componentMap = new Map<ComponentType, Component>();
@ -24,6 +25,9 @@ componentMap.set('Switch', Switch);
componentMap.set('Checkbox', Checkbox);
componentMap.set('DatePicker', DatePicker);
componentMap.set('TimePicker', TimePicker);
componentMap.set('RadioGroup', Radio.Group);
componentMap.set('RadioButtonGroup', RadioButtonGroup);
componentMap.set('ApiRadioGroup', ApiRadioGroup);
export function add(compName: ComponentType, component: Component) {
componentMap.set(compName, component);

12
apps/vue/src/components/Table/src/components/TableImg.vue

@ -9,7 +9,7 @@
<div class="img-div">
<PreviewGroup>
<template v-for="(img, index) in imgList" :key="img">
<Image
<AImage
:width="size"
:style="{
display: index === 0 ? '' : 'none !important',
@ -22,9 +22,9 @@
</Badge>
<PreviewGroup v-else>
<template v-for="(img, index) in imgList" :key="img">
<Image
<AImage
:width="size"
:style="{ marginLeft: index === 0 ? 0 : margin }"
:style="{ marginLeft: index === 0 ? 0 : margin + 'px' }"
:src="srcPrefix + img"
/>
</template>
@ -40,7 +40,7 @@
export default defineComponent({
name: 'TableImage',
components: { Image, PreviewGroup: Image.PreviewGroup, Badge },
components: { AImage: Image, PreviewGroup: Image.PreviewGroup, Badge },
props: {
imgList: propTypes.arrayOf(propTypes.string),
size: propTypes.number.def(40),
@ -52,6 +52,10 @@
margin: propTypes.number.def(4),
// srcimgList
srcPrefix: propTypes.string.def(''),
// fallback,
fallback: propTypes.string.def(
'data:image/png;base64,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',
),
},
setup(props) {
const getWrapStyle = computed((): CSSProperties => {

4
apps/vue/src/components/Table/src/components/editable/EditableCell.vue

@ -121,7 +121,7 @@
}
const component = unref(getComponent);
if (!component.includes('Select')) {
if (!component.includes('Select') && !component.includes('Radio')) {
return value;
}
@ -152,7 +152,7 @@
});
watchEffect(() => {
defaultValueRef.value = props.value;
//defaultValueRef.value = props.value;
currentValueRef.value = props.value;
});

485
apps/vue/src/components/Table/src/components/settings/ColumnSetting.vue

@ -14,7 +14,7 @@
<div :class="`${prefixCls}__popover-title`">
<Checkbox
:indeterminate="indeterminate"
v-model:checked="checkAll"
v-model:checked="state.checkAll"
@change="onCheckAllChange"
>
{{ t('component.table.settingColumnShow') }}
@ -44,7 +44,7 @@
<template #content>
<ScrollContainer>
<CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef">
<CheckboxGroup v-model:value="state.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-column-drag-icon" />
@ -66,7 +66,7 @@
`${prefixCls}__fixed-left`,
{
active: item.fixed === 'left',
disabled: !checkedList.includes(item.value),
disabled: !state.checkedList.includes(item.value),
},
]"
@click="handleColumnFixed(item, 'left')"
@ -87,7 +87,7 @@
`${prefixCls}__fixed-right`,
{
active: item.fixed === 'right',
disabled: !checkedList.includes(item.value),
disabled: !state.checkedList.includes(item.value),
},
]"
@click="handleColumnFixed(item, 'right')"
@ -102,13 +102,12 @@
</Popover>
</Tooltip>
</template>
<script lang="ts">
<script lang="ts" setup>
import type { BasicColumn, ColumnChangeParam } from '../../types/table';
import {
defineComponent,
useAttrs,
ref,
reactive,
toRefs,
watchEffect,
nextTick,
unref,
@ -142,285 +141,249 @@
fixed?: boolean | 'left' | 'right';
}
export default defineComponent({
name: 'ColumnSetting',
components: {
SettingOutlined,
Popover,
Tooltip,
Checkbox,
CheckboxGroup: Checkbox.Group,
DragOutlined,
ScrollContainer,
Divider,
Icon,
},
emits: ['columns-change'],
setup(_, { emit, attrs }) {
const { t } = useI18n();
const table = useTableContext();
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
let inited = false;
const cachePlainOptions = ref<Options[]>([]);
const plainOptions = ref<Options[] | any>([]);
const plainSortOptions = ref<Options[]>([]);
const columnListRef = ref<ComponentRef>(null);
const state = reactive<State>({
checkAll: true,
checkedList: [],
defaultCheckList: [],
});
const CheckboxGroup = Checkbox.Group;
const emits = defineEmits(['columns-change']);
const checkIndex = ref(false);
const checkSelect = ref(false);
const checkDrag = ref(false);
const { t } = useI18n();
const table = useTableContext();
const { prefixCls } = useDesign('basic-column-setting');
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
let inited = false;
const getValues = computed(() => {
return unref(table?.getBindValues) || {};
});
const cachePlainOptions = ref<Options[]>([]);
const plainOptions = ref<Options[] | any>([]);
watchEffect(() => {
setTimeout(() => {
const columns = table.getColumns();
if (columns.length && !state.isInit) {
init();
}
}, 10);
});
const plainSortOptions = ref<Options[]>([]);
watchEffect(() => {
const values = unref(getValues);
checkIndex.value = !!values.showIndexColumn;
checkSelect.value = !!values.rowSelection;
});
const columnListRef = ref<ComponentRef>(null);
function getColumns() {
const ret: Options[] = [];
table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
ret.push({
label: (item.title as string) || (item.customTitle as string),
value: (item.dataIndex || item.title) as string,
...item,
});
});
return ret;
}
const state = reactive<State>({
checkAll: true,
checkedList: [],
defaultCheckList: [],
});
function init() {
const columns = getColumns();
const checkList = table
.getColumns({ ignoreAction: true })
.map((item) => {
if (item.defaultHidden) {
return '';
}
return item.dataIndex || item.title;
})
.filter(Boolean) as string[];
if (!plainOptions.value.length) {
plainOptions.value = columns;
plainSortOptions.value = columns;
cachePlainOptions.value = columns;
state.defaultCheckList = checkList;
} else {
// const fixedColumns = columns.filter((item) =>
// Reflect.has(item, 'fixed')
// ) as BasicColumn[];
unref(plainOptions).forEach((item: BasicColumn) => {
const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex);
if (findItem) {
item.fixed = findItem.fixed;
}
});
}
state.isInit = true;
state.checkedList = checkList;
}
const checkIndex = ref(false);
const checkSelect = ref(false);
const checkDrag = ref(false);
// checkAll change
function onCheckAllChange(e: CheckboxChangeEvent) {
const checkList = plainOptions.value.map((item) => item.value);
if (e.target.checked) {
state.checkedList = checkList;
setColumns(checkList);
} else {
state.checkedList = [];
setColumns([]);
}
const { prefixCls } = useDesign('basic-column-setting');
const getValues = computed(() => {
return unref(table?.getBindValues) || {};
});
watchEffect(() => {
setTimeout(() => {
const columns = table.getColumns();
if (columns.length && !state.isInit) {
init();
}
}, 10);
});
watchEffect(() => {
const values = unref(getValues);
checkIndex.value = !!values.showIndexColumn;
checkSelect.value = !!values.rowSelection;
});
const indeterminate = computed(() => {
const len = plainOptions.value.length;
let checkedLen = state.checkedList.length;
unref(checkIndex) && checkedLen--;
return checkedLen > 0 && checkedLen < len;
function getColumns() {
const ret: Options[] = [];
table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
ret.push({
label: (item.title as string) || (item.customTitle as string),
value: (item.dataIndex || item.title) as string,
...item,
});
});
return ret;
}
// Trigger when check/uncheck a column
function onChange(checkedList: string[]) {
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);
});
setColumns(checkedList);
}
function init() {
const columns = getColumns();
let sortable: Sortable;
let sortableOrder: string[] = [];
// reset columns
function reset() {
state.checkedList = [...state.defaultCheckList];
state.checkAll = true;
plainOptions.value = unref(cachePlainOptions);
plainSortOptions.value = unref(cachePlainOptions);
setColumns(table.getCacheColumns());
sortable.sort(sortableOrder);
}
const checkList = table
.getColumns({ ignoreAction: true, ignoreIndex: true })
.map((item) => {
if (item.defaultHidden) {
return '';
}
return item.dataIndex || item.title;
})
.filter(Boolean) as string[];
if (!plainOptions.value.length) {
plainOptions.value = columns;
plainSortOptions.value = columns;
cachePlainOptions.value = columns;
state.defaultCheckList = checkList;
} else {
// const fixedColumns = columns.filter((item) =>
// Reflect.has(item, 'fixed')
// ) as BasicColumn[];
unref(plainOptions).forEach((item: BasicColumn) => {
const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex);
if (findItem) {
item.fixed = findItem.fixed;
}
});
}
state.isInit = true;
state.checkedList = checkList;
}
// Open the pop-up window for drag and drop initialization
function handleVisibleChange() {
if (inited) return;
nextTick(() => {
const columnListEl = unref(columnListRef);
if (!columnListEl) return;
const el = columnListEl.$el as any;
if (!el) return;
// Drag and drop sort
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 = cloneDeep(plainSortOptions.value);
if (oldIndex > newIndex) {
columns.splice(newIndex, 0, columns[oldIndex]);
columns.splice(oldIndex + 1, 1);
} else {
columns.splice(newIndex + 1, 0, columns[oldIndex]);
columns.splice(oldIndex, 1);
}
plainSortOptions.value = columns;
// fix: ColumnSettingbug (#1931)
// https://github.com/vbenjs/vue-vben-admin/commit/50468e9581c93e95df21447bec30b6148541c46b
setColumns(
columns
.map((col: Options) => col.value)
.filter((value: string) => state.checkedList.includes(value)),
);
},
});
// order
sortableOrder = sortable.toArray();
inited = true;
});
}
// checkAll change
function onCheckAllChange(e: CheckboxChangeEvent) {
const checkList = plainOptions.value.map((item) => item.value);
if (e.target.checked) {
state.checkedList = checkList;
setColumns(checkList);
} else {
state.checkedList = [];
setColumns([]);
}
}
// Control whether the serial number column is displayed
function handleIndexCheckChange(e: CheckboxChangeEvent) {
table.setProps({
showIndexColumn: e.target.checked,
});
}
const indeterminate = computed(() => {
const len = plainOptions.value.length;
let checkedLen = state.checkedList.length;
unref(checkIndex) && checkedLen--;
return checkedLen > 0 && checkedLen < len;
});
// Control whether the check box is displayed
function handleSelectCheckChange(e: CheckboxChangeEvent) {
table.setProps({
rowSelection: e.target.checked ? defaultRowSelection : undefined,
});
}
// Trigger when check/uncheck a column
function onChange(checkedList: string[]) {
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);
});
setColumns(checkedList);
}
function handleDragChange(e: CheckboxChangeEvent) {
const columns = getColumns() as BasicColumn[];
columns.forEach((col) => {
if (isNumber(col.width)) {
col.resizable = e.target.checked;
let sortable: Sortable;
let sortableOrder: string[] = [];
// reset columns
function reset() {
state.checkedList = [...state.defaultCheckList];
state.checkAll = true;
plainOptions.value = unref(cachePlainOptions);
plainSortOptions.value = unref(cachePlainOptions);
setColumns(table.getCacheColumns());
sortable.sort(sortableOrder);
}
// Open the pop-up window for drag and drop initialization
function handleVisibleChange() {
if (inited) return;
nextTick(() => {
const columnListEl = unref(columnListRef);
if (!columnListEl) return;
const el = columnListEl.$el as any;
if (!el) return;
// Drag and drop sort
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 = cloneDeep(plainSortOptions.value);
if (oldIndex > newIndex) {
columns.splice(newIndex, 0, columns[oldIndex]);
columns.splice(oldIndex + 1, 1);
} else {
columns.splice(newIndex + 1, 0, columns[oldIndex]);
columns.splice(oldIndex, 1);
}
});
setColumns(columns);
}
function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
if (!state.checkedList.includes(item.dataIndex as string)) return;
plainSortOptions.value = columns;
// fix: ColumnSettingbug (#1931)
// https://github.com/vbenjs/vue-vben-admin/commit/50468e9581c93e95df21447bec30b6148541c46b
setColumns(
columns
.map((col: Options) => col.value)
.filter((value: string) => state.checkedList.includes(value)),
);
},
});
// order
sortableOrder = sortable.toArray();
inited = true;
});
}
const columns = getColumns() as BasicColumn[];
const isFixed = item.fixed === fixed ? false : fixed;
const index = columns.findIndex((col) => col.dataIndex === item.dataIndex);
if (index !== -1) {
columns[index].fixed = isFixed;
}
item.fixed = isFixed;
// Control whether the serial number column is displayed
function handleIndexCheckChange(e: CheckboxChangeEvent) {
table.setProps({
showIndexColumn: e.target.checked,
});
}
if (isFixed && !item.width) {
item.width = 100;
}
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
setColumns(columns);
}
// Control whether the check box is displayed
function handleSelectCheckChange(e: CheckboxChangeEvent) {
table.setProps({
rowSelection: e.target.checked ? defaultRowSelection : undefined,
});
}
function setColumns(columns: BasicColumn[] | string[]) {
table.setColumns(columns);
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 };
});
emit('columns-change', data);
function handleDragChange(e: CheckboxChangeEvent) {
const columns = getColumns() as BasicColumn[];
columns.forEach((col) => {
if (isNumber(col.width)) {
col.resizable = e.target.checked;
}
});
setColumns(columns);
}
function getPopupContainer() {
return isFunction(attrs.getPopupContainer)
? attrs.getPopupContainer()
: getParentContainer();
}
function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
if (!state.checkedList.includes(item.dataIndex as string)) return;
return {
t,
...toRefs(state),
indeterminate,
onCheckAllChange,
onChange,
plainOptions,
reset,
prefixCls,
columnListRef,
handleVisibleChange,
checkIndex,
checkSelect,
checkDrag,
handleIndexCheckChange,
handleSelectCheckChange,
handleDragChange,
defaultRowSelection,
handleColumnFixed,
getPopupContainer,
};
},
});
const columns = getColumns() as BasicColumn[];
const isFixed = item.fixed === fixed ? false : fixed;
const index = columns.findIndex((col) => col.dataIndex === item.dataIndex);
if (index !== -1) {
columns[index].fixed = isFixed;
}
item.fixed = isFixed;
if (isFixed && !item.width) {
item.width = 100;
}
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
setColumns(columns);
}
function setColumns(columns: BasicColumn[] | string[]) {
table.setColumns(columns);
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 };
});
emits('columns-change', data);
}
function getPopupContainer() {
const attrs = useAttrs();
return isFunction(attrs.getPopupContainer)
? attrs.getPopupContainer()
: getParentContainer();
}
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-column-setting';

2
apps/vue/src/components/Table/src/hooks/useCustomRow.ts

@ -40,7 +40,7 @@ export function useCustomRow(
function handleClick() {
const { rowSelection, rowKey, clickToRowSelect } = unref(propsRef);
if (!rowSelection || !clickToRowSelect) return;
const keys = getSelectRowKeys();
const keys = getSelectRowKeys() || [];
const key = getKey(record, rowKey, unref(getAutoCreateKey));
if (!key) return;

4
apps/vue/src/components/Table/src/hooks/useRowSelection.ts

@ -66,13 +66,13 @@ export function useRowSelection(
selectedRowKeysRef.value = rowKeys;
const allSelectedRows = findNodeAll(
toRaw(unref(tableData)).concat(toRaw(unref(selectedRowRef))),
(item) => rowKeys.includes(item[unref(getRowKey) as string]),
(item) => rowKeys?.includes(item[unref(getRowKey) as string]),
{
children: propsRef.value.childrenColumnName ?? 'children',
},
);
const trueSelectedRows: any[] = [];
rowKeys.forEach((key: string) => {
rowKeys?.forEach((key: string) => {
const found = allSelectedRows.find((item) => item[unref(getRowKey) as string] === key);
found && trueSelectedRows.push(found);
});

3
apps/vue/src/components/Table/src/hooks/useTable.ts

@ -76,6 +76,9 @@ export function useTable(tableProps?: Props): [
redoHeight: () => {
getTableInstance().redoHeight();
},
setSelectedRows: (rows: Recordable[]) => {
return toRaw(getTableInstance().setSelectedRows(rows));
},
setLoading: (loading: boolean) => {
getTableInstance().setLoading(loading);
},

5
apps/vue/src/components/Table/src/types/componentType.ts

@ -8,4 +8,7 @@ export type ComponentType =
| 'Checkbox'
| 'Switch'
| 'DatePicker'
| 'TimePicker';
| 'TimePicker'
| 'RadioGroup'
| 'RadioButtonGroup'
| 'ApiRadioGroup';

3
apps/vue/src/components/Table/src/types/table.ts

@ -85,10 +85,11 @@ export type SizeType = 'default' | 'middle' | 'small' | 'large';
export interface TableActionType {
reload: (opt?: FetchParams) => Promise<void>;
setSelectedRows: (rows: Recordable[]) => void;
getSelectRows: <T = Recordable>() => T[];
clearSelectedRowKeys: () => void;
expandAll: () => void;
expandRows: (keys: string[]) => void;
expandRows: (keys: string[] | number[]) => void;
collapseAll: () => void;
scrollTo: (pos: string) => void; // pos: id | "top" | "bottom"
getSelectRowKeys: () => string[];

2
apps/vue/src/components/Tinymce/src/Editor.vue

@ -76,7 +76,7 @@
const tinymceProps = {
options: {
type: Object as PropType<Partial<RawEditorSettings>>,
default: {},
default: () => ({}),
},
value: {
type: String,

4
apps/vue/src/components/Tree/index.ts

@ -1,6 +1,6 @@
import BasicTree from './src/Tree.vue';
import BasicTree from './src/BasicTree.vue';
import './style';
export { BasicTree };
export type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
export * from './src/tree';
export * from './src/types/tree';

78
apps/vue/src/components/Tree/src/Tree.vue → apps/vue/src/components/Tree/src/BasicTree.vue

@ -1,7 +1,13 @@
<script lang="tsx">
import type { CSSProperties } from 'vue';
import type { FieldNames, TreeState, TreeItem, KeyType, CheckKeys, TreeActionType } from './tree';
import type {
FieldNames,
TreeState,
TreeItem,
KeyType,
CheckKeys,
TreeActionType,
} from './types/tree';
import {
defineComponent,
reactive,
@ -13,7 +19,7 @@
watch,
onMounted,
} from 'vue';
import TreeHeader from './TreeHeader.vue';
import TreeHeader from './components/TreeHeader.vue';
import { Tree, Spin, Empty } from 'ant-design-vue';
import { TreeIcon } from './TreeIcon';
import { ScrollContainer } from '/@/components/Container';
@ -21,12 +27,11 @@
import { isArray, isBoolean, isEmpty, isFunction } from '/@/utils/is';
import { extendSlots, getSlot } from '/@/utils/helper/tsxHelper';
import { filter, treeToList, eachTree } from '/@/utils/helper/treeHelper';
import { useTree } from './useTree';
import { useTree } from './hooks/useTree';
import { useContextMenu } from '/@/hooks/web/useContextMenu';
import { CreateContextOptions } from '/@/components/ContextMenu';
import { treeEmits, treeProps } from './tree';
import { treeEmits, treeProps } from './types/tree';
import { createBEM } from '/@/utils/bem';
export default defineComponent({
name: 'BasicTree',
inheritAttrs: false,
@ -34,24 +39,19 @@
emits: treeEmits,
setup(props, { attrs, slots, emit, expose }) {
const [bem] = createBEM('tree');
const state = reactive<TreeState>({
checkStrictly: props.checkStrictly,
expandedKeys: props.expandedKeys || [],
selectedKeys: props.selectedKeys || [],
checkedKeys: props.checkedKeys || [],
});
const searchState = reactive({
startSearch: false,
searchText: '',
searchData: [] as TreeItem[],
});
const treeDataRef = ref<TreeItem[]>([]);
const [createContextMenu] = useContextMenu();
const getFieldNames = computed((): Required<FieldNames> => {
const { fieldNames } = props;
return {
@ -61,7 +61,6 @@
...fieldNames,
};
});
const getBindValues = computed(() => {
let propsData = {
blockNode: true,
@ -83,16 +82,15 @@
onCheck: (v: CheckKeys, e) => {
let currentValue = toRaw(state.checkedKeys) as KeyType[];
if (isArray(currentValue) && searchState.startSearch) {
const { key } = unref(getFieldNames);
currentValue = difference(currentValue, getChildrenKeys(e.node.$attrs.node[key]));
const value = e.node.eventKey;
currentValue = difference(currentValue, getChildrenKeys(value));
if (e.checked) {
currentValue.push(e.node.$attrs.node[key]);
currentValue.push(value);
}
state.checkedKeys = currentValue;
} else {
state.checkedKeys = v;
}
const rawVal = toRaw(state.checkedKeys);
emit('update:value', rawVal);
emit('check', rawVal, e);
@ -101,15 +99,12 @@
};
return omit(propsData, 'treeData', 'class');
});
const getTreeData = computed((): TreeItem[] =>
searchState.startSearch ? searchState.searchData : unref(treeDataRef),
);
const getNotFound = computed((): boolean => {
return !getTreeData.value || getTreeData.value.length === 0;
});
const {
deleteNodeByKey,
insertNodeByKey,
@ -121,7 +116,6 @@
getEnabledKeys,
getSelectedNode,
} = useTree(treeDataRef, getFieldNames);
function getIcon(params: Recordable, icon?: string) {
if (!icon) {
if (props.renderIcon && isFunction(props.renderIcon)) {
@ -130,11 +124,9 @@
}
return icon;
}
async function handleRightClick({ event, node }: Recordable) {
const { rightMenuList: menuList = [], beforeRightClick } = props;
let contextMenuOptions: CreateContextOptions = { event, items: [] };
if (beforeRightClick && isFunction(beforeRightClick)) {
let result = await beforeRightClick(node, event);
if (Array.isArray(result)) {
@ -149,42 +141,33 @@
contextMenuOptions.items = contextMenuOptions.items.filter((item) => !item.hidden);
createContextMenu(contextMenuOptions);
}
function setExpandedKeys(keys: KeyType[]) {
state.expandedKeys = keys;
}
function getExpandedKeys() {
return state.expandedKeys;
}
function setSelectedKeys(keys: KeyType[]) {
state.selectedKeys = keys;
}
function getSelectedKeys() {
return state.selectedKeys;
}
function setCheckedKeys(keys: CheckKeys) {
state.checkedKeys = keys;
}
function getCheckedKeys() {
return state.checkedKeys;
}
function checkAll(checkAll: boolean) {
state.checkedKeys = checkAll ? getEnabledKeys() : ([] as KeyType[]);
}
function expandAll(expandAll: boolean) {
state.expandedKeys = expandAll ? getAllKeys() : ([] as KeyType[]);
}
function onStrictlyChange(strictly: boolean) {
state.checkStrictly = strictly;
}
watch(
() => props.searchValue,
(val) => {
@ -196,7 +179,6 @@
immediate: true,
},
);
watch(
() => props.treeData,
(val) => {
@ -205,7 +187,6 @@
}
},
);
function handleSearch(searchValue: string) {
if (searchValue !== searchState.searchText) searchState.searchText = searchValue;
emit('update:searchValue', searchValue);
@ -217,7 +198,6 @@
unref(props);
searchState.startSearch = true;
const { title: titleField, key: keyField } = unref(getFieldNames);
const matchedKeys: string[] = [];
searchState.searchData = filter(
unref(treeDataRef),
@ -232,7 +212,6 @@
},
unref(getFieldNames),
);
if (expandOnSearch) {
const expandKeys = treeToList(searchState.searchData).map((val) => {
return val[keyField];
@ -241,16 +220,13 @@
setExpandedKeys(expandKeys);
}
}
if (checkOnSearch && checkable && matchedKeys.length) {
setCheckedKeys(matchedKeys);
}
if (selectedOnSearch && matchedKeys.length) {
setSelectedKeys(matchedKeys);
}
}
function handleClickNode(key: string, children: TreeItem[]) {
if (!props.clickRowToExpand || !children || children.length === 0) return;
if (!state.expandedKeys.includes(key)) {
@ -264,11 +240,9 @@
setExpandedKeys(keys);
}
}
watchEffect(() => {
treeDataRef.value = props.treeData as TreeItem[];
});
onMounted(() => {
const level = parseInt(props.defaultExpandLevel);
if (level > 0) {
@ -277,19 +251,15 @@
expandAll(true);
}
});
watchEffect(() => {
state.expandedKeys = props.expandedKeys;
});
watchEffect(() => {
state.selectedKeys = props.selectedKeys;
});
watchEffect(() => {
state.checkedKeys = props.checkedKeys;
});
watch(
() => props.value,
() => {
@ -297,7 +267,6 @@
},
{ immediate: true },
);
watch(
() => state.checkedKeys,
() => {
@ -306,11 +275,9 @@
emit('change', v);
},
);
watchEffect(() => {
state.checkStrictly = props.checkStrictly;
});
const instance: TreeActionType = {
setExpandedKeys,
getExpandedKeys,
@ -335,7 +302,6 @@
return searchState.searchText;
},
};
function renderAction(node: TreeItem) {
const { actionList } = props;
if (!actionList || actionList.length === 0) return;
@ -346,9 +312,7 @@
} else if (isBoolean(item.show)) {
nodeShow = item.show;
}
if (!nodeShow) return null;
return (
<span key={index} class={bem('action')}>
{item.render(node)}
@ -356,7 +320,6 @@
);
});
}
const treeData = computed(() => {
const data = cloneDeep(getTreeData.value);
eachTree(data, (item, _parent) => {
@ -367,15 +330,12 @@
key: keyField,
children: childrenField,
} = unref(getFieldNames);
const icon = getIcon(item, item.icon);
const title = get(item, titleField);
const searchIdx = searchText ? title.indexOf(searchText) : -1;
const isHighlight =
searchState.startSearch && !isEmpty(searchText) && highlight && searchIdx !== -1;
const highlightStyle = `color: ${isBoolean(highlight) ? '#f50' : highlight}`;
const titleDom = isHighlight ? (
<span class={unref(getBindValues)?.blockNode ? `${bem('content')}` : ''}>
<span>{title.substr(0, searchIdx)}</span>
@ -405,9 +365,7 @@
});
return data;
});
expose(instance);
return () => {
const { title, helpMessage, toolbar, search, checkable } = props;
const showTitle = title || toolbar || search || slots.headerTitle;
@ -430,7 +388,11 @@
{extendSlots(slots)}
</TreeHeader>
)}
<Spin spinning={unref(props.loading)} tip="加载中...">
<Spin
wrapperClassName={unref(props.treeWrapperClassName)}
spinning={unref(props.loading)}
tip="加载中..."
>
<ScrollContainer style={scrollStyle} v-show={!unref(getNotFound)}>
<Tree {...unref(getBindValues)} showIcon={false} treeData={treeData.value} />
</ScrollContainer>
@ -445,4 +407,4 @@
};
},
});
</script>
</script>

16
apps/vue/src/components/Tree/src/TreeHeader.vue → apps/vue/src/components/Tree/src/components/TreeHeader.vue

@ -40,12 +40,9 @@
import { useI18n } from '/@/hooks/web/useI18n';
import { useDebounceFn } from '@vueuse/core';
import { createBEM } from '/@/utils/bem';
import { ToolbarEnum } from './tree';
import { ToolbarEnum } from '../types/tree';
const searchValue = ref('');
const [bem] = createBEM('tree-header');
const props = defineProps({
helpMessage: {
type: [String, Array] as PropType<string | string[]>,
@ -81,10 +78,8 @@
},
} as const);
const emit = defineEmits(['strictly-change', 'search']);
const slots = useSlots();
const { t } = useI18n();
const getInputSearchCls = computed(() => {
const titleExists = slots.headerTitle || props.title;
return [
@ -95,7 +90,6 @@
},
];
});
const toolbarList = computed(() => {
const { checkable } = props;
const defaultToolbarList = [
@ -106,7 +100,6 @@
divider: checkable,
},
];
return checkable
? [
{ label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL },
@ -121,7 +114,6 @@
]
: defaultToolbarList;
});
function handleMenuClick(e: { key: ToolbarEnum }) {
const { key } = e;
switch (key) {
@ -145,20 +137,16 @@
break;
}
}
function emitChange(value?: string): void {
emit('search', value);
}
const debounceEmitChange = useDebounceFn(emitChange, 200);
watch(
() => searchValue.value,
(v) => {
debounceEmitChange(v);
},
);
watch(
() => props.searchText,
(v) => {
@ -167,4 +155,4 @@
}
},
);
</script>
</script>

2
apps/vue/src/components/Tree/src/useTree.ts → apps/vue/src/components/Tree/src/hooks/useTree.ts

@ -1,4 +1,4 @@
import type { InsertNodeParams, KeyType, FieldNames, TreeItem } from './tree';
import type { InsertNodeParams, KeyType, FieldNames, TreeItem } from '../types/tree';
import type { Ref, ComputedRef } from 'vue';
import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';

108
apps/vue/src/components/Tree/src/props.ts

@ -1,108 +0,0 @@
import type { PropType } from 'vue';
import type {
ReplaceFields,
ActionItem,
Keys,
CheckKeys,
ContextMenuOptions,
TreeItem,
} from './typing';
import type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
import { propTypes } from '/@/utils/propTypes';
export const basicProps = {
value: {
type: [Object, Array] as PropType<Keys | CheckKeys>,
},
renderIcon: {
type: Function as PropType<(params: Recordable) => string>,
},
helpMessage: {
type: [String, Array] as PropType<string | string[]>,
default: '',
},
title: propTypes.string,
toolbar: propTypes.bool,
search: propTypes.bool,
searchValue: propTypes.string,
checkStrictly: propTypes.bool,
clickRowToExpand: propTypes.bool.def(true),
checkable: propTypes.bool.def(false),
defaultExpandLevel: {
type: [String, Number] as PropType<string | number>,
default: '',
},
defaultExpandAll: propTypes.bool.def(false),
replaceFields: {
type: Object as PropType<ReplaceFields>,
},
treeData: {
type: Array as PropType<TreeDataItem[]>,
},
actionList: {
type: Array as PropType<ActionItem[]>,
default: () => [],
},
expandedKeys: {
type: Array as PropType<Keys>,
default: () => [],
},
selectedKeys: {
type: Array as PropType<Keys>,
default: () => [],
},
checkedKeys: {
type: Array as PropType<CheckKeys>,
default: () => [],
},
beforeRightClick: {
type: Function as PropType<(...arg: any) => ContextMenuItem[] | ContextMenuOptions>,
default: null,
},
rightMenuList: {
type: Array as PropType<ContextMenuItem[]>,
},
// 自定义数据过滤判断方法(注: 不是整个过滤方法,而是内置过滤的判断方法,用于增强原本仅能通过title进行过滤的方式)
filterFn: {
type: Function as PropType<
(searchValue: any, node: TreeItem, replaceFields: ReplaceFields) => boolean
>,
default: null,
},
// 高亮搜索值,仅高亮具体匹配值(通过title)值为true时使用默认色值,值为#xxx时使用此值替代且高亮开启
highlight: {
type: [Boolean, String] as PropType<Boolean | String>,
default: false,
},
// 搜索完成时自动展开结果
expandOnSearch: propTypes.bool.def(false),
// 搜索完成自动选中所有结果,当且仅当 checkable===true 时生效
checkOnSearch: propTypes.bool.def(false),
// 搜索完成自动select所有结果
selectedOnSearch: propTypes.bool.def(false),
};
export const treeNodeProps = {
actionList: {
type: Array as PropType<ActionItem[]>,
default: () => [],
},
replaceFields: {
type: Object as PropType<ReplaceFields>,
},
treeData: {
type: Array as PropType<TreeDataItem[]>,
default: () => [],
},
};

3
apps/vue/src/components/Tree/src/tree.ts → apps/vue/src/components/Tree/src/types/tree.ts

@ -134,6 +134,7 @@ export const treeProps = buildProps({
type: Boolean,
default: false,
},
treeWrapperClassName: String
});
export type TreeProps = ExtractPropTypes<typeof treeProps>;
@ -191,4 +192,4 @@ export interface TreeActionType {
treeList?: TreeItem[],
selectNode?: TreeItem | null,
) => TreeItem | null;
}
}

54
apps/vue/src/components/Tree/src/typing.ts

@ -1,54 +0,0 @@
import type { TreeDataItem, CheckEvent as CheckEventOrigin } from 'ant-design-vue/es/tree/Tree';
import { ContextMenuItem } from '/@/hooks/web/useContextMenu';
export interface ActionItem {
render: (record: Recordable) => any;
show?: boolean | ((record: Recordable) => boolean);
}
export interface TreeItem extends TreeDataItem {
icon?: any;
}
export interface ReplaceFields {
children?: string;
title?: string;
key?: string;
}
export type Keys = (string | number)[];
export type CheckKeys =
| (string | number)[]
| { checked: (string | number)[]; halfChecked: (string | number)[] };
export interface TreeActionType {
checkAll: (checkAll: boolean) => void;
expandAll: (expandAll: boolean) => void;
setExpandedKeys: (keys: Keys) => void;
getExpandedKeys: () => Keys;
setSelectedKeys: (keys: Keys) => void;
getSelectedKeys: () => Keys;
setCheckedKeys: (keys: CheckKeys) => void;
getCheckedKeys: () => CheckKeys;
filterByLevel: (level: number) => void;
insertNodeByKey: (opt: InsertNodeParams) => void;
insertNodesByKey: (opt: InsertNodeParams) => void;
deleteNodeByKey: (key: string) => void;
updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void;
setSearchValue: (value: string) => void;
getSearchValue: () => string;
}
export interface InsertNodeParams {
parentKey: string | null;
node: TreeDataItem;
list?: TreeDataItem[];
push?: 'push' | 'unshift';
}
export interface ContextMenuOptions {
icon?: string;
styles?: any;
items?: ContextMenuItem[];
}
export type CheckEvent = CheckEventOrigin;

2
apps/vue/src/components/Upload/src/props.ts

@ -27,7 +27,7 @@ export const basicProps = {
},
uploadParams: {
type: Object as PropType<any>,
default: {},
default: () => ({}),
},
api: {
type: Function as PropType<PromiseFn>,

10
apps/vue/src/components/Verify/src/props.ts

@ -38,19 +38,19 @@ export const basicProps = {
wrapStyle: {
type: Object as PropType<any>,
default: {},
default: () => ({}),
},
contentStyle: {
type: Object as PropType<any>,
default: {},
default: () => ({}),
},
barStyle: {
type: Object as PropType<any>,
default: {},
default: () => ({}),
},
actionStyle: {
type: Object as PropType<any>,
default: {},
default: () => ({}),
},
};
@ -67,7 +67,7 @@ export const rotateProps = {
imgWrapStyle: {
type: Object as PropType<any>,
default: {},
default: () => ({}),
},
minDegree: {

8
apps/vue/src/design/ant/index.less

@ -57,3 +57,11 @@ span.anticon:not(.app-iconify) {
border-top: 0 !important;
border-left: 0 !important;
}
.ant-form-item-control-input-content {
> div {
> div {
max-width: 100%;
}
}
}

5
apps/vue/src/design/ant/input.less

@ -2,9 +2,10 @@
// input
.ant-input {
&-number {
width: 100% !important;
&-number,
&-number-group-wrapper {
min-width: 110px;
width: 100% !important;
max-width: 100%;
}
}

8
apps/vue/src/hooks/web/useECharts.ts

@ -8,6 +8,7 @@ import { useEventListener } from '/@/hooks/event/useEventListener';
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
import echarts from '/@/utils/lib/echarts';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
export function useECharts(
elRef: Ref<HTMLDivElement>,
@ -18,6 +19,7 @@ export function useECharts(
const getDarkMode = computed(() => {
return theme === 'default' ? getSysDarkMode.value : theme;
});
const { getCollapsed } = useMenuSetting();
let chartInstance: echarts.ECharts | null = null;
let resizeFn: Fn = resize;
const cacheOptions = ref({}) as Ref<EChartsOption>;
@ -99,6 +101,12 @@ export function useECharts(
},
);
watch(getCollapsed, (_) => {
useTimeoutFn(() => {
resizeFn();
}, 300);
});
tryOnUnmounted(() => {
if (!chartInstance) return;
removeResizeFn();

7
apps/vue/src/layouts/default/header/components/FullScreen.vue

@ -20,6 +20,13 @@
setup() {
const { t } = useI18n();
const { toggle, isFullscreen } = useFullscreen();
//
isFullscreen.value = !!(
document.fullscreenElement ||
document.webkitFullscreenElement ||
document.mozFullScreenElement ||
document.msFullscreenElement
);
const getTitle = computed(() => {
return unref(isFullscreen)

2
apps/vue/src/layouts/default/setting/components/ThemeColorPicker.vue

@ -31,7 +31,7 @@
props: {
colorList: {
type: Array as PropType<string[]>,
defualt: [],
default: () => [],
},
event: {
type: Number as PropType<HandlerEnum>,

2
apps/vue/src/layouts/default/setting/components/TypePicker.vue

@ -31,7 +31,7 @@
props: {
menuTypeList: {
type: Array as PropType<typeof menuTypeList>,
defualt: () => [],
default: () => [],
},
handler: {
type: Function as PropType<Fn>,

2
apps/vue/src/locales/lang/en/layout.ts

@ -3,7 +3,7 @@ export default {
header: {
// user dropdown
dropdownItemDoc: 'Document',
dropdownItemLoginOut: 'Login Out',
dropdownItemLoginOut: 'Log Out',
tooltipErrorLog: 'Error log',
tooltipLock: 'Lock screen',

2
apps/vue/src/router/constant.ts

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

1
apps/vue/src/store/modules/multipleTab.ts

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

11
apps/vue/src/utils/dateUtil.ts

@ -6,17 +6,12 @@ import dayjs from 'dayjs';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
const DATE_FORMAT = 'YYYY-MM-DD';
export function formatToDateTime(
date: string | Date | dayjs.Dayjs | undefined = undefined,
format = DATE_TIME_FORMAT,
): string {
export function formatToDateTime(date?: dayjs.ConfigType, format = DATE_TIME_FORMAT): string{
return dayjs(date).format(format);
}
export function formatToDate(
date: string | Date | dayjs.Dayjs | undefined = undefined,
format = DATE_FORMAT,
): string {
export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): string {
if (typeof date === 'number') date *= 1000;
return dayjs(date).format(format);
}

6
apps/vue/src/utils/http/axios/index.ts

@ -16,6 +16,7 @@ import { setObjToUrlParams, deepMerge } from '/@/utils';
import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
import { joinTimestamp, formatRequestDate } from './helper';
import { useLocaleStoreWithOut } from '/@/store/modules/locale';
import axios from 'axios';
const globSetting = useGlobSetting();
const urlPrefix = globSetting.urlPrefix;
@ -153,6 +154,11 @@ const transform: AxiosTransform = {
// const { t } = useI18n();
const errorLogStore = useErrorLogStoreWithOut();
errorLogStore.addAjaxErrorInfo(error);
if (axios.isCancel(error)) {
return Promise.reject(error);
}
const resMessage = checkResponse(error.response);
return Promise.reject(resMessage ?? error);
},

6
apps/vue/src/utils/index.ts

@ -3,6 +3,7 @@ import type { App, Plugin } from 'vue';
import { unref } from 'vue';
import { isObject } from '/@/utils/is';
import { cloneDeep } from 'lodash-es';
export const noop = () => {};
@ -34,10 +35,11 @@ export function setObjToUrlParams(baseUrl: string, obj: any): string {
export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
let key: string;
const res: any = cloneDeep(src)
for (key in target) {
src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]);
res[key] = isObject(res[key]) ? deepMerge(res[key], target[key]) : (res[key] = target[key]);
}
return src;
return res;
}
export function openWindow(

3
apps/vue/src/utils/is.ts

@ -107,8 +107,7 @@ export function isPhone(val: string) {
}
export function isUrl(path: string): boolean {
const reg =
/^(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
const reg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
return isMatch(reg, path);
}

4
apps/vue/src/views/account/setting/BaseSetting.vue

@ -97,9 +97,9 @@
});
}
function updateAvatar(src: string) {
function updateAvatar({ source }) {
const userinfo = userStore.getUserInfo;
userinfo.avatar = src;
userinfo.avatar = source;
userStore.setUserInfo(userinfo);
}

3
apps/vue/src/views/oss-management/objects/components/FileList.vue

@ -99,9 +99,6 @@
// ,
clickToRowSelect: false,
clearSelectOnPageChange: true,
tableSetting: {
redo: false,
},
bordered: true,
showIndexColumn: false,
canResize: false,

2
apps/vue/src/views/platform/menu/hooks/useMenuFormContext.ts

@ -223,7 +223,7 @@ export function useMenuFormContext({ menuModel, formElRef, framework }: UseMenuF
const schemas = unref(getFormSchemas);
const metaSchemas= schemas.filter((x) => x.tab === tabKey);
metaSchemas.forEach((x) => {
formEl?.removeSchemaByFiled(x.field);
formEl?.removeSchemaByField(x.field);
const index = schemas.findIndex((s) => s.field === x.field);
if (index) {
schemas.splice(index, 1);

Loading…
Cancel
Save