mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	refactor: PageTable组件重构
This commit is contained in:
		@@ -15,7 +15,7 @@ const config = {
 | 
			
		||||
    baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
 | 
			
		||||
 | 
			
		||||
    // 系统版本
 | 
			
		||||
    version: 'v1.6.0',
 | 
			
		||||
    version: 'v1.6.1',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default config;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										66
									
								
								mayfly_go_web/src/components/Grid/components/GridItem.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								mayfly_go_web/src/components/Grid/components/GridItem.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div v-show="isShow" :style="style">
 | 
			
		||||
        <slot></slot>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="ts" name="GridItem">
 | 
			
		||||
import { computed, inject, Ref, ref, useAttrs, watch } from 'vue';
 | 
			
		||||
import { BreakPoint, Responsive } from '../interface/index';
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    offset?: number;
 | 
			
		||||
    span?: number;
 | 
			
		||||
    suffix?: boolean;
 | 
			
		||||
    xs?: Responsive;
 | 
			
		||||
    sm?: Responsive;
 | 
			
		||||
    md?: Responsive;
 | 
			
		||||
    lg?: Responsive;
 | 
			
		||||
    xl?: Responsive;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
    offset: 0,
 | 
			
		||||
    span: 1,
 | 
			
		||||
    suffix: false,
 | 
			
		||||
    xs: undefined,
 | 
			
		||||
    sm: undefined,
 | 
			
		||||
    md: undefined,
 | 
			
		||||
    lg: undefined,
 | 
			
		||||
    xl: undefined,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const attrs = useAttrs() as { index: string };
 | 
			
		||||
const isShow = ref(true);
 | 
			
		||||
 | 
			
		||||
// 注入断点
 | 
			
		||||
const breakPoint = inject<Ref<BreakPoint>>('breakPoint', ref('xl'));
 | 
			
		||||
const shouldHiddenIndex = inject<Ref<number>>('shouldHiddenIndex', ref(-1));
 | 
			
		||||
watch(
 | 
			
		||||
    () => [shouldHiddenIndex.value, breakPoint.value],
 | 
			
		||||
    (n) => {
 | 
			
		||||
        if (attrs.index) {
 | 
			
		||||
            isShow.value = !(n[0] !== -1 && parseInt(attrs.index) >= Number(n[0]));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    { immediate: true }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const gap = inject('gap', 0);
 | 
			
		||||
const cols = inject('cols', ref(4));
 | 
			
		||||
const style = computed(() => {
 | 
			
		||||
    let span = props[breakPoint.value]?.span ?? props.span;
 | 
			
		||||
    let offset = props[breakPoint.value]?.offset ?? props.offset;
 | 
			
		||||
    if (props.suffix) {
 | 
			
		||||
        return {
 | 
			
		||||
            gridColumnStart: cols.value - span - offset + 1,
 | 
			
		||||
            gridColumnEnd: `span ${span + offset}`,
 | 
			
		||||
            marginLeft: offset !== 0 ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` : 'unset',
 | 
			
		||||
        };
 | 
			
		||||
    } else {
 | 
			
		||||
        return {
 | 
			
		||||
            gridColumn: `span ${span + offset > cols.value ? cols.value : span + offset}/span ${span + offset > cols.value ? cols.value : span + offset}`,
 | 
			
		||||
            marginLeft: offset !== 0 ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` : 'unset',
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										159
									
								
								mayfly_go_web/src/components/Grid/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								mayfly_go_web/src/components/Grid/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,159 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div :style="style">
 | 
			
		||||
        <slot></slot>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts" name="Grid">
 | 
			
		||||
import { ref, watch, useSlots, computed, provide, onBeforeMount, onMounted, onUnmounted, onDeactivated, onActivated, VNodeArrayChildren, VNode } from 'vue';
 | 
			
		||||
import type { BreakPoint } from './interface/index';
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    cols?: number | Record<BreakPoint, number>;
 | 
			
		||||
    collapsed?: boolean;
 | 
			
		||||
    collapsedRows?: number;
 | 
			
		||||
    gap?: [number, number] | number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
    cols: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
 | 
			
		||||
    collapsed: false,
 | 
			
		||||
    collapsedRows: 1,
 | 
			
		||||
    gap: 0,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onBeforeMount(() => props.collapsed && findIndex());
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
    resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
 | 
			
		||||
    window.addEventListener('resize', resize);
 | 
			
		||||
});
 | 
			
		||||
onActivated(() => {
 | 
			
		||||
    resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
 | 
			
		||||
    window.addEventListener('resize', resize);
 | 
			
		||||
});
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
    window.removeEventListener('resize', resize);
 | 
			
		||||
});
 | 
			
		||||
onDeactivated(() => {
 | 
			
		||||
    window.removeEventListener('resize', resize);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 监听屏幕变化
 | 
			
		||||
const resize = (e: UIEvent) => {
 | 
			
		||||
    let width = (e.target as Window).innerWidth;
 | 
			
		||||
    switch (!!width) {
 | 
			
		||||
        case width < 768:
 | 
			
		||||
            breakPoint.value = 'xs';
 | 
			
		||||
            break;
 | 
			
		||||
        case width >= 768 && width < 992:
 | 
			
		||||
            breakPoint.value = 'sm';
 | 
			
		||||
            break;
 | 
			
		||||
        case width >= 992 && width < 1200:
 | 
			
		||||
            breakPoint.value = 'md';
 | 
			
		||||
            break;
 | 
			
		||||
        case width >= 1200 && width < 1920:
 | 
			
		||||
            breakPoint.value = 'lg';
 | 
			
		||||
            break;
 | 
			
		||||
        case width >= 1920:
 | 
			
		||||
            breakPoint.value = 'xl';
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 注入 gap 间距
 | 
			
		||||
provide('gap', Array.isArray(props.gap) ? props.gap[0] : props.gap);
 | 
			
		||||
 | 
			
		||||
// 注入响应式断点
 | 
			
		||||
let breakPoint = ref<BreakPoint>('xl');
 | 
			
		||||
provide('breakPoint', breakPoint);
 | 
			
		||||
 | 
			
		||||
// 注入要开始折叠的 index
 | 
			
		||||
const hiddenIndex = ref(-1);
 | 
			
		||||
provide('shouldHiddenIndex', hiddenIndex);
 | 
			
		||||
 | 
			
		||||
// 注入 cols
 | 
			
		||||
const gridCols = computed(() => {
 | 
			
		||||
    if (typeof props.cols === 'object') return props.cols[breakPoint.value] ?? props.cols;
 | 
			
		||||
    return props.cols;
 | 
			
		||||
});
 | 
			
		||||
provide('cols', gridCols);
 | 
			
		||||
 | 
			
		||||
// 寻找需要开始折叠的字段 index
 | 
			
		||||
const slots = useSlots().default!();
 | 
			
		||||
 | 
			
		||||
const findIndex = () => {
 | 
			
		||||
    let fields: VNodeArrayChildren = [];
 | 
			
		||||
    let suffix: VNode | null = null;
 | 
			
		||||
    slots.forEach((slot: any) => {
 | 
			
		||||
        // suffix
 | 
			
		||||
        if (typeof slot.type === 'object' && slot.type.__name === 'GridItem' && slot.props?.suffix !== undefined) {
 | 
			
		||||
            suffix = slot;
 | 
			
		||||
        }
 | 
			
		||||
        // slot children
 | 
			
		||||
        if (typeof slot.type === 'symbol' && Array.isArray(slot.children)) {
 | 
			
		||||
            fields.push(...slot.children);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // 计算 suffix 所占用的列
 | 
			
		||||
    let suffixCols = 0;
 | 
			
		||||
    if (suffix) {
 | 
			
		||||
        suffixCols =
 | 
			
		||||
            ((suffix as VNode).props![breakPoint.value]?.span ?? (suffix as VNode).props?.span ?? 1) +
 | 
			
		||||
            ((suffix as VNode).props![breakPoint.value]?.offset ?? (suffix as VNode).props?.offset ?? 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        let find = false;
 | 
			
		||||
        fields.reduce((prev = 0, current, index) => {
 | 
			
		||||
            prev +=
 | 
			
		||||
                ((current as VNode)!.props![breakPoint.value]?.span ?? (current as VNode)!.props?.span ?? 1) +
 | 
			
		||||
                ((current as VNode)!.props![breakPoint.value]?.offset ?? (current as VNode)!.props?.offset ?? 0);
 | 
			
		||||
            if (Number(prev) > props.collapsedRows * gridCols.value - suffixCols) {
 | 
			
		||||
                hiddenIndex.value = index;
 | 
			
		||||
                find = true;
 | 
			
		||||
                throw 'find it';
 | 
			
		||||
            }
 | 
			
		||||
            return prev;
 | 
			
		||||
        }, 0);
 | 
			
		||||
        if (!find) hiddenIndex.value = -1;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        // console.warn(e);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 断点变化时执行 findIndex
 | 
			
		||||
watch(
 | 
			
		||||
    () => breakPoint.value,
 | 
			
		||||
    () => {
 | 
			
		||||
        if (props.collapsed) findIndex();
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// 监听 collapsed
 | 
			
		||||
watch(
 | 
			
		||||
    () => props.collapsed,
 | 
			
		||||
    (value) => {
 | 
			
		||||
        if (value) return findIndex();
 | 
			
		||||
        hiddenIndex.value = -1;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// 设置间距
 | 
			
		||||
const gridGap = computed(() => {
 | 
			
		||||
    if (typeof props.gap === 'number') return `${props.gap}px`;
 | 
			
		||||
    if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px`;
 | 
			
		||||
    return 'unset';
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 设置 style
 | 
			
		||||
const style = computed(() => {
 | 
			
		||||
    return {
 | 
			
		||||
        display: 'grid',
 | 
			
		||||
        gridGap: gridGap.value,
 | 
			
		||||
        gridTemplateColumns: `repeat(${gridCols.value}, minmax(0, 1fr))`,
 | 
			
		||||
    };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
defineExpose({ breakPoint });
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										6
									
								
								mayfly_go_web/src/components/Grid/interface/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mayfly_go_web/src/components/Grid/interface/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
export type BreakPoint = "xs" | "sm" | "md" | "lg" | "xl";
 | 
			
		||||
 | 
			
		||||
export type Responsive = {
 | 
			
		||||
  span?: number;
 | 
			
		||||
  offset?: number;
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,94 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <component
 | 
			
		||||
        :is="item?.render ?? `el-${item.type}`"
 | 
			
		||||
        v-bind="{ ...handleSearchProps, ...placeholder, clearable: true }"
 | 
			
		||||
        v-model.trim="itemValue"
 | 
			
		||||
        :data="item.type === 'tree-select' ? item.options : []"
 | 
			
		||||
        :options="['cascader', 'select-v2'].includes(item.type!) ? item.options : []"
 | 
			
		||||
    >
 | 
			
		||||
        <template v-if="item.type === 'cascader'" #default="{ data }">
 | 
			
		||||
            <span>{{ data[fieldNames.label] }}</span>
 | 
			
		||||
        </template>
 | 
			
		||||
 | 
			
		||||
        <template v-if="item.type === 'select'">
 | 
			
		||||
            <component
 | 
			
		||||
                :is="`el-option`"
 | 
			
		||||
                v-for="(col, index) in item.options"
 | 
			
		||||
                :key="index"
 | 
			
		||||
                :label="col[fieldNames.label]"
 | 
			
		||||
                :value="col[fieldNames.value]"
 | 
			
		||||
            ></component>
 | 
			
		||||
        </template>
 | 
			
		||||
 | 
			
		||||
        <slot v-else></slot>
 | 
			
		||||
    </component>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts" name="SearchFormItem">
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import { SearchItem } from '../index';
 | 
			
		||||
import { useVModel } from '@vueuse/core';
 | 
			
		||||
 | 
			
		||||
interface SearchFormItemProps {
 | 
			
		||||
    modelValue: any;
 | 
			
		||||
    item: SearchItem;
 | 
			
		||||
}
 | 
			
		||||
const props = defineProps<SearchFormItemProps>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['update:modelValue']);
 | 
			
		||||
 | 
			
		||||
const itemValue = useVModel(props, 'modelValue', emit);
 | 
			
		||||
 | 
			
		||||
// 判断 fieldNames 设置 label && value && children 的 key 值
 | 
			
		||||
const fieldNames = computed(() => {
 | 
			
		||||
    return {
 | 
			
		||||
        label: props.item?.fieldNames?.label ?? 'label',
 | 
			
		||||
        value: props.item?.fieldNames?.value ?? 'value',
 | 
			
		||||
        children: props.item.fieldNames?.children ?? 'children',
 | 
			
		||||
    };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 接收 enumMap (el 为 select-v2 需单独处理 enumData)
 | 
			
		||||
// const enumMap = inject('enumMap', ref(new Map()));
 | 
			
		||||
// const columnEnum = computed(() => {
 | 
			
		||||
//     let enumData = enumMap.value.get(props.item.prop);
 | 
			
		||||
//     if (!enumData) return [];
 | 
			
		||||
//     if (props.item?.type === 'select-v2' && props.item.fieldNames) {
 | 
			
		||||
//         enumData = enumData.map((item: { [key: string]: any }) => {
 | 
			
		||||
//             return { ...item, label: item[fieldNames.value.label], value: item[fieldNames.value.value] };
 | 
			
		||||
//         });
 | 
			
		||||
//     }
 | 
			
		||||
//     return enumData;
 | 
			
		||||
// });
 | 
			
		||||
 | 
			
		||||
// 处理透传的 searchProps (type 为 tree-select、cascader 的时候需要给下默认 label && value && children)
 | 
			
		||||
const handleSearchProps = computed(() => {
 | 
			
		||||
    const label = fieldNames.value.label;
 | 
			
		||||
    const value = fieldNames.value.value;
 | 
			
		||||
    const children = fieldNames.value.children;
 | 
			
		||||
    const searchEl = props.item?.type;
 | 
			
		||||
    let searchProps = props.item?.props ?? {};
 | 
			
		||||
    if (searchEl === 'tree-select') {
 | 
			
		||||
        searchProps = { ...searchProps, props: { ...searchProps.props, label, children }, nodeKey: value };
 | 
			
		||||
    }
 | 
			
		||||
    if (searchEl === 'cascader') {
 | 
			
		||||
        searchProps = { ...searchProps, props: { ...searchProps.props, label, value, children } };
 | 
			
		||||
    }
 | 
			
		||||
    return searchProps;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 处理默认 placeholder
 | 
			
		||||
const placeholder = computed(() => {
 | 
			
		||||
    const search = props.item;
 | 
			
		||||
    const label = search.label;
 | 
			
		||||
    if (['datetimerange', 'daterange', 'monthrange'].includes(search?.props?.type) || search?.props?.isRange) {
 | 
			
		||||
        return {
 | 
			
		||||
            rangeSeparator: search?.props?.rangeSeparator ?? '至',
 | 
			
		||||
            startPlaceholder: search?.props?.startPlaceholder ?? '开始时间',
 | 
			
		||||
            endPlaceholder: search?.props?.endPlaceholder ?? '结束时间',
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    const placeholder = search?.props?.placeholder ?? (search?.type?.includes('input') ? `请输入${label}` : `请选择${label}`);
 | 
			
		||||
    return { placeholder };
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										115
									
								
								mayfly_go_web/src/components/SearchForm/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								mayfly_go_web/src/components/SearchForm/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
import { VNode } from 'vue';
 | 
			
		||||
 | 
			
		||||
export type FieldNamesProps = {
 | 
			
		||||
    label: string;
 | 
			
		||||
    value: string;
 | 
			
		||||
    children?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type SearchItemType =
 | 
			
		||||
    | 'input'
 | 
			
		||||
    | 'input-number'
 | 
			
		||||
    | 'select'
 | 
			
		||||
    | 'select-v2'
 | 
			
		||||
    | 'tree-select'
 | 
			
		||||
    | 'cascader'
 | 
			
		||||
    | 'date-picker'
 | 
			
		||||
    | 'time-picker'
 | 
			
		||||
    | 'time-select'
 | 
			
		||||
    | 'switch'
 | 
			
		||||
    | 'slider';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 搜索项
 | 
			
		||||
 */
 | 
			
		||||
export class SearchItem {
 | 
			
		||||
    /**
 | 
			
		||||
     * 属性字段
 | 
			
		||||
     */
 | 
			
		||||
    prop: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 当前项搜索框的 label
 | 
			
		||||
     */
 | 
			
		||||
    label: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 表单项类型,input、select、date等
 | 
			
		||||
     */
 | 
			
		||||
    type: SearchItemType;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * select等组件的可选值
 | 
			
		||||
     */
 | 
			
		||||
    options: any;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 插槽名
 | 
			
		||||
     */
 | 
			
		||||
    slot: string;
 | 
			
		||||
 | 
			
		||||
    props?: any; // 搜索项参数,根据 element plus 官方文档来传递,该属性所有值会透传到组件
 | 
			
		||||
 | 
			
		||||
    tooltip?: string; // 搜索提示
 | 
			
		||||
 | 
			
		||||
    span?: number; // 搜索项所占用的列数,默认为 1 列
 | 
			
		||||
 | 
			
		||||
    offset?: number; // 搜索字段左侧偏移列数
 | 
			
		||||
 | 
			
		||||
    fieldNames: FieldNamesProps; // 指定 label && value && children 的 key 值,用于select等类型组件
 | 
			
		||||
 | 
			
		||||
    render?: (scope: any) => VNode; // 自定义搜索内容渲染(tsx语法)
 | 
			
		||||
 | 
			
		||||
    constructor(prop: string, label: string) {
 | 
			
		||||
        this.prop = prop;
 | 
			
		||||
        this.label = label;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static new(prop: string, label: string): SearchItem {
 | 
			
		||||
        return new SearchItem(prop, label);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static text(prop: string, label: string): SearchItem {
 | 
			
		||||
        const tq = new SearchItem(prop, label);
 | 
			
		||||
        tq.type = 'input';
 | 
			
		||||
        return tq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static select(prop: string, label: string): SearchItem {
 | 
			
		||||
        const tq = new SearchItem(prop, label);
 | 
			
		||||
        tq.type = 'select';
 | 
			
		||||
        return tq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static date(prop: string, label: string): SearchItem {
 | 
			
		||||
        const tq = new SearchItem(prop, label);
 | 
			
		||||
        tq.type = 'date-picker';
 | 
			
		||||
        return tq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static slot(prop: string, label: string, slotName: string): SearchItem {
 | 
			
		||||
        const tq = new SearchItem(prop, label);
 | 
			
		||||
        tq.slot = slotName;
 | 
			
		||||
        return tq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    withSpan(span: number): SearchItem {
 | 
			
		||||
        this.span = span;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置枚举值用于选择等
 | 
			
		||||
     * @param enumValues 枚举值对象
 | 
			
		||||
     * @returns
 | 
			
		||||
     */
 | 
			
		||||
    withEnum(enumValues: any): SearchItem {
 | 
			
		||||
        this.options = Object.values(enumValues);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setOptions(options: any): SearchItem {
 | 
			
		||||
        this.options = options;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										136
									
								
								mayfly_go_web/src/components/SearchForm/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								mayfly_go_web/src/components/SearchForm/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div v-if="items.length" class="search-form">
 | 
			
		||||
        <el-form ref="formRef" :model="searchParam" label-width="auto">
 | 
			
		||||
            <Grid ref="gridRef" :collapsed="collapsed" :gap="[20, 0]" :cols="searchCol">
 | 
			
		||||
                <GridItem v-for="(item, index) in items" :key="item.prop" v-bind="getResponsive(item)" :index="index">
 | 
			
		||||
                    <el-form-item>
 | 
			
		||||
                        <template #label>
 | 
			
		||||
                            <el-space :size="4">
 | 
			
		||||
                                <span>{{ `${item?.label}` }}</span>
 | 
			
		||||
                                <el-tooltip v-if="item.tooltip" :content="item?.tooltip" placement="top">
 | 
			
		||||
                                    <SvgIcon name="QuestionFilled" />
 | 
			
		||||
                                </el-tooltip>
 | 
			
		||||
                            </el-space>
 | 
			
		||||
                            <span>:</span>
 | 
			
		||||
                        </template>
 | 
			
		||||
 | 
			
		||||
                        <SearchFormItem v-if="!item.slot" :item="item" v-model="searchParam[item.prop]" />
 | 
			
		||||
 | 
			
		||||
                        <slot v-else :name="item.slot"></slot>
 | 
			
		||||
                    </el-form-item>
 | 
			
		||||
                </GridItem>
 | 
			
		||||
                <GridItem suffix>
 | 
			
		||||
                    <div class="operation">
 | 
			
		||||
                        <el-button type="primary" :icon="Search" @click="search" plain> 搜索 </el-button>
 | 
			
		||||
                        <el-button :icon="Delete" @click="reset"> 重置 </el-button>
 | 
			
		||||
                        <el-button v-if="showCollapse" type="primary" link class="search-isOpen" @click="collapsed = !collapsed">
 | 
			
		||||
                            {{ collapsed ? '展开' : '合并' }}
 | 
			
		||||
                            <el-icon class="el-icon--right">
 | 
			
		||||
                                <component :is="collapsed ? ArrowDown : ArrowUp"></component>
 | 
			
		||||
                            </el-icon>
 | 
			
		||||
                        </el-button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </GridItem>
 | 
			
		||||
            </Grid>
 | 
			
		||||
        </el-form>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="ts" name="SearchForm">
 | 
			
		||||
import { computed, ref } from 'vue';
 | 
			
		||||
import { BreakPoint } from '@/components/Grid/interface/index';
 | 
			
		||||
import { Delete, Search, ArrowDown, ArrowUp } from '@element-plus/icons-vue';
 | 
			
		||||
import SearchFormItem from './components/SearchFormItem.vue';
 | 
			
		||||
import Grid from '@/components/Grid/index.vue';
 | 
			
		||||
import GridItem from '@/components/Grid/components/GridItem.vue';
 | 
			
		||||
import SvgIcon from '@/components/svgIcon/index.vue';
 | 
			
		||||
import { SearchItem } from './index';
 | 
			
		||||
import { useVModel } from '@vueuse/core';
 | 
			
		||||
 | 
			
		||||
interface ProTableProps {
 | 
			
		||||
    items: SearchItem[]; // 搜索配置项
 | 
			
		||||
    modelValue?: { [key: string]: any }; // 搜索参数
 | 
			
		||||
    searchCol: number | Record<BreakPoint, number>;
 | 
			
		||||
    search: (params: any) => void; // 搜索方法
 | 
			
		||||
    reset: (params: any) => void; // 重置方法
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 默认值
 | 
			
		||||
const props = withDefaults(defineProps<ProTableProps>(), {
 | 
			
		||||
    items: () => [],
 | 
			
		||||
    modelValue: () => ({}),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['update:modelValue']);
 | 
			
		||||
 | 
			
		||||
const searchParam = useVModel(props, 'modelValue', emit);
 | 
			
		||||
 | 
			
		||||
// 获取响应式设置
 | 
			
		||||
const getResponsive = (item: SearchItem) => {
 | 
			
		||||
    return {
 | 
			
		||||
        span: item?.span,
 | 
			
		||||
        offset: item.offset ?? 0,
 | 
			
		||||
        // xs: item.search?.xs,
 | 
			
		||||
        // sm: item.search?.sm,
 | 
			
		||||
        // md: item.search?.md,
 | 
			
		||||
        // lg: item.search?.lg,
 | 
			
		||||
        // xl: item.search?.xl,
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 是否默认折叠搜索项
 | 
			
		||||
const collapsed = ref(true);
 | 
			
		||||
 | 
			
		||||
// 获取响应式断点
 | 
			
		||||
const gridRef = ref();
 | 
			
		||||
const breakPoint = computed<BreakPoint>(() => gridRef.value?.breakPoint);
 | 
			
		||||
 | 
			
		||||
// 判断是否显示 展开/合并 按钮
 | 
			
		||||
const showCollapse = computed(() => {
 | 
			
		||||
    let show = false;
 | 
			
		||||
    props.items.reduce((prev, current) => {
 | 
			
		||||
        prev += (current![breakPoint.value]?.span ?? current?.span ?? 1) + (current![breakPoint.value]?.offset ?? current?.offset ?? 0);
 | 
			
		||||
        if (typeof props.searchCol !== 'number') {
 | 
			
		||||
            if (prev >= props.searchCol[breakPoint.value]) show = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (prev >= props.searchCol) show = true;
 | 
			
		||||
        }
 | 
			
		||||
        return prev;
 | 
			
		||||
    }, 0);
 | 
			
		||||
    return show;
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
.search-form {
 | 
			
		||||
    padding: 18px 18px 0;
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
    background-color: var(--el-bg-color);
 | 
			
		||||
    border: 1px solid var(--el-border-color-light);
 | 
			
		||||
    border-radius: 6px;
 | 
			
		||||
    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
 | 
			
		||||
 | 
			
		||||
    .el-form {
 | 
			
		||||
        .el-form-item__content > * {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 去除时间选择器上下 padding
 | 
			
		||||
        .el-range-editor.el-input__wrapper {
 | 
			
		||||
            padding: 0 10px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .el-form-item {
 | 
			
		||||
            margin-bottom: 18px !important;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .operation {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        justify-content: flex-end;
 | 
			
		||||
        margin-bottom: 18px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,170 +1,112 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div class="page-table">
 | 
			
		||||
        <!-- 
 | 
			
		||||
            实现:通过我们配置好的 查询条件
 | 
			
		||||
                首先去创建form表单,根据我们配置的查询条件去做一个循环判断,展示出不用类型所对应不同的输入框
 | 
			
		||||
                比如:text对应普通的输入框,select对应下拉选择,dateTime对应日期时间选择器
 | 
			
		||||
                在使用时,父组件会传来一个queryForm空的对象,
 | 
			
		||||
                循环出来的输入框会绑定表格配置中的prop字段绑定在queryForm对象中
 | 
			
		||||
         -->
 | 
			
		||||
    <div>
 | 
			
		||||
        <!-- 查询表单 -->
 | 
			
		||||
        <SearchForm v-show="isShowSearch" :items="searchItems" v-model="queryForm_" :search="queryData" :reset="reset" :search-col="searchCol">
 | 
			
		||||
            <!-- 遍历父组件传入的 solts 透传给子组件 -->
 | 
			
		||||
            <template v-for="(_, key) in useSlots()" v-slot:[key]>
 | 
			
		||||
                <slot :name="key"></slot>
 | 
			
		||||
            </template>
 | 
			
		||||
        </SearchForm>
 | 
			
		||||
 | 
			
		||||
        <el-card>
 | 
			
		||||
            <div class="query" ref="queryRef">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div v-if="props.query.length > 0">
 | 
			
		||||
                        <el-form :model="queryForm_" label-width="auto" :size="props.size">
 | 
			
		||||
                            <el-row
 | 
			
		||||
                                v-for="i in Math.ceil((props.query.length + 1) / (defaultQueryCount + 1))"
 | 
			
		||||
                                :key="i"
 | 
			
		||||
                                v-show="i == 1 || isOpenMoreQuery"
 | 
			
		||||
                                :class="i > 1 && isOpenMoreQuery ? 'is-open' : ''"
 | 
			
		||||
            <div class="table-main">
 | 
			
		||||
                <!-- 表格头部 操作按钮 -->
 | 
			
		||||
                <div class="table-header">
 | 
			
		||||
                    <div class="header-button-lf">
 | 
			
		||||
                        <slot name="tableHeader" />
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <div v-if="toolButton" class="header-button-ri">
 | 
			
		||||
                        <slot name="toolButton">
 | 
			
		||||
                            <el-button v-if="showToolButton('refresh')" icon="Refresh" circle @click="execQuery()" />
 | 
			
		||||
 | 
			
		||||
                            <el-button v-if="showToolButton('search') && searchItems?.length" icon="Search" circle @click="isShowSearch = !isShowSearch" />
 | 
			
		||||
 | 
			
		||||
                            <el-popover
 | 
			
		||||
                                placement="bottom"
 | 
			
		||||
                                title="表格配置"
 | 
			
		||||
                                popper-style="max-height: 550px; overflow: auto; max-width: 450px"
 | 
			
		||||
                                width="auto"
 | 
			
		||||
                                trigger="click"
 | 
			
		||||
                            >
 | 
			
		||||
                                <el-form-item
 | 
			
		||||
                                    :label="item.label"
 | 
			
		||||
                                    style="margin-right: 12px; margin-bottom: 0px"
 | 
			
		||||
                                    v-for="item in getRowQueryItem(i)"
 | 
			
		||||
                                    :key="item.prop"
 | 
			
		||||
                                >
 | 
			
		||||
                                    <!-- 这里只获取指定个数的筛选条件 -->
 | 
			
		||||
                                    <el-input
 | 
			
		||||
                                        v-model="queryForm_[item.prop]"
 | 
			
		||||
                                        :placeholder="'输入' + item.label + '关键字'"
 | 
			
		||||
                                        clearable
 | 
			
		||||
                                        v-if="item.type == 'text'"
 | 
			
		||||
                                    ></el-input>
 | 
			
		||||
 | 
			
		||||
                                    <el-select-v2
 | 
			
		||||
                                        v-model="queryForm_[item.prop]"
 | 
			
		||||
                                        :options="item.options"
 | 
			
		||||
                                        clearable
 | 
			
		||||
                                        :placeholder="'选择' + item.label + '关键字'"
 | 
			
		||||
                                        v-else-if="item.type == 'select'"
 | 
			
		||||
                                    />
 | 
			
		||||
 | 
			
		||||
                                    <el-date-picker
 | 
			
		||||
                                        v-model="queryForm_[item.prop]"
 | 
			
		||||
                                        clearable
 | 
			
		||||
                                        type="datetimerange"
 | 
			
		||||
                                        format="YYYY-MM-DD hh:mm:ss"
 | 
			
		||||
                                        value-format="x"
 | 
			
		||||
                                        range-separator="至"
 | 
			
		||||
                                        start-placeholder="开始时间"
 | 
			
		||||
                                        end-placeholder="结束时间"
 | 
			
		||||
                                        v-else-if="item.type == 'date'"
 | 
			
		||||
                                    />
 | 
			
		||||
 | 
			
		||||
                                    <template v-else-if="item.slot == 'queryBtns'">
 | 
			
		||||
                                        <template v-if="props.query?.length > defaultQueryCount">
 | 
			
		||||
                                            <el-button
 | 
			
		||||
                                                @click="isOpenMoreQuery = !isOpenMoreQuery"
 | 
			
		||||
                                                v-if="!isOpenMoreQuery"
 | 
			
		||||
                                                icon="ArrowDownBold"
 | 
			
		||||
                                                circle
 | 
			
		||||
                                            ></el-button>
 | 
			
		||||
                                            <el-button @click="isOpenMoreQuery = !isOpenMoreQuery" v-else icon="ArrowUpBold" circle></el-button>
 | 
			
		||||
                                        </template>
 | 
			
		||||
 | 
			
		||||
                                        <el-button @click="queryData()" type="primary" icon="search" plain>查询</el-button>
 | 
			
		||||
                                        <el-button @click="reset()" icon="RefreshRight">重置</el-button>
 | 
			
		||||
                                    </template>
 | 
			
		||||
 | 
			
		||||
                                    <slot :name="item.slot"></slot>
 | 
			
		||||
                                </el-form-item>
 | 
			
		||||
                            </el-row>
 | 
			
		||||
                        </el-form>
 | 
			
		||||
                                <div v-for="(item, index) in tableColumns" :key="index">
 | 
			
		||||
                                    <el-checkbox v-model="item.show" :label="item.label" :true-label="true" :false-label="false" />
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <template #reference>
 | 
			
		||||
                                    <el-button icon="Operation" circle :size="props.size"></el-button>
 | 
			
		||||
                                </template>
 | 
			
		||||
                            </el-popover>
 | 
			
		||||
                        </slot>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="slot">
 | 
			
		||||
                    <!-- 查询栏右侧slot插槽(用来添加表格其他操作,比如,新增数据,删除数据等其他操作) -->
 | 
			
		||||
                    <slot name="queryRight"></slot>
 | 
			
		||||
                <el-table
 | 
			
		||||
                    v-bind="$attrs"
 | 
			
		||||
                    :max-height="tableMaxHeight"
 | 
			
		||||
                    @selection-change="handleSelectionChange"
 | 
			
		||||
                    :data="state.data"
 | 
			
		||||
                    highlight-current-row
 | 
			
		||||
                    v-loading="state.loading"
 | 
			
		||||
                    :size="props.size"
 | 
			
		||||
                    :border="border"
 | 
			
		||||
                >
 | 
			
		||||
                    <el-table-column v-if="props.showSelection" type="selection" width="40" />
 | 
			
		||||
 | 
			
		||||
                    <!-- 
 | 
			
		||||
                    动态表头显示,根据表格每条配置项中的show字段来决定改列是否显示或者隐藏 
 | 
			
		||||
                    columns 就是我们表格配置的数组对象
 | 
			
		||||
                    -->
 | 
			
		||||
                    <el-popover
 | 
			
		||||
                        placement="bottom"
 | 
			
		||||
                        title="表格配置"
 | 
			
		||||
                        popper-style="max-height: 550px; overflow: auto; max-width: 450px"
 | 
			
		||||
                        width="auto"
 | 
			
		||||
                        trigger="click"
 | 
			
		||||
                    >
 | 
			
		||||
                        <div v-for="(item, index) in props.columns" :key="index">
 | 
			
		||||
                            <el-checkbox v-model="item.show" :label="item.label" :true-label="true" :false-label="false" />
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <template #reference>
 | 
			
		||||
                            <!-- 一个Element Plus中的图标 -->
 | 
			
		||||
                            <el-button icon="Operation" :size="props.size"></el-button>
 | 
			
		||||
                        </template>
 | 
			
		||||
                    </el-popover>
 | 
			
		||||
                </div>
 | 
			
		||||
                    <template v-for="(item, index) in tableColumns">
 | 
			
		||||
                        <el-table-column
 | 
			
		||||
                            :key="index"
 | 
			
		||||
                            v-if="item.show"
 | 
			
		||||
                            :prop="item.prop"
 | 
			
		||||
                            :label="item.label"
 | 
			
		||||
                            :fixed="item.fixed"
 | 
			
		||||
                            :align="item.align"
 | 
			
		||||
                            :show-overflow-tooltip="item.showOverflowTooltip"
 | 
			
		||||
                            :min-width="item.minWidth"
 | 
			
		||||
                            :sortable="item.sortable || false"
 | 
			
		||||
                            :type="item.type"
 | 
			
		||||
                            :width="item.width"
 | 
			
		||||
                        >
 | 
			
		||||
                            <!-- 插槽:预留功能 -->
 | 
			
		||||
                            <template #default="scope" v-if="item.slot">
 | 
			
		||||
                                <slot :name="item.prop" :data="scope.row"></slot>
 | 
			
		||||
                            </template>
 | 
			
		||||
 | 
			
		||||
                            <!-- 枚举类型使用tab展示 -->
 | 
			
		||||
                            <template #default="scope" v-else-if="item.type == 'tag'">
 | 
			
		||||
                                <enum-tag :size="props.size" :enums="item.typeParam" :value="scope.row[item.prop]"></enum-tag>
 | 
			
		||||
                            </template>
 | 
			
		||||
 | 
			
		||||
                            <template #default="scope" v-else>
 | 
			
		||||
                                <!-- 配置了美化文本按钮以及文本内容大于指定长度,则显示美化按钮 -->
 | 
			
		||||
                                <el-popover
 | 
			
		||||
                                    v-if="item.isBeautify && scope.row[item.prop]?.length > 35"
 | 
			
		||||
                                    effect="light"
 | 
			
		||||
                                    trigger="click"
 | 
			
		||||
                                    placement="top"
 | 
			
		||||
                                    width="600px"
 | 
			
		||||
                                >
 | 
			
		||||
                                    <template #default>
 | 
			
		||||
                                        <el-input :autosize="{ minRows: 3, maxRows: 15 }" disabled v-model="formatVal" type="textarea" />
 | 
			
		||||
                                    </template>
 | 
			
		||||
                                    <template #reference>
 | 
			
		||||
                                        <el-link
 | 
			
		||||
                                            @click="formatText(scope.row[item.prop])"
 | 
			
		||||
                                            :underline="false"
 | 
			
		||||
                                            type="success"
 | 
			
		||||
                                            icon="MagicStick"
 | 
			
		||||
                                            class="mr5"
 | 
			
		||||
                                        ></el-link>
 | 
			
		||||
                                    </template>
 | 
			
		||||
                                </el-popover>
 | 
			
		||||
 | 
			
		||||
                                <span>{{ item.getValueByData(scope.row) }}</span>
 | 
			
		||||
                            </template>
 | 
			
		||||
                        </el-table-column>
 | 
			
		||||
                    </template>
 | 
			
		||||
                </el-table>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <el-table
 | 
			
		||||
                v-bind="$attrs"
 | 
			
		||||
                :max-height="tableMaxHeight"
 | 
			
		||||
                @selection-change="handleSelectionChange"
 | 
			
		||||
                :data="state.data"
 | 
			
		||||
                highlight-current-row
 | 
			
		||||
                v-loading="state.loading"
 | 
			
		||||
                :size="props.size"
 | 
			
		||||
            >
 | 
			
		||||
                <el-table-column v-if="props.showSelection" type="selection" width="40" />
 | 
			
		||||
 | 
			
		||||
                <template v-for="(item, index) in columns">
 | 
			
		||||
                    <el-table-column
 | 
			
		||||
                        :key="index"
 | 
			
		||||
                        v-if="item.show"
 | 
			
		||||
                        :prop="item.prop"
 | 
			
		||||
                        :label="item.label"
 | 
			
		||||
                        :fixed="item.fixed"
 | 
			
		||||
                        :align="item.align"
 | 
			
		||||
                        :show-overflow-tooltip="item.showOverflowTooltip"
 | 
			
		||||
                        :min-width="item.minWidth"
 | 
			
		||||
                        :sortable="item.sortable || false"
 | 
			
		||||
                        :type="item.type"
 | 
			
		||||
                        :width="item.width"
 | 
			
		||||
                    >
 | 
			
		||||
                        <!-- 插槽:预留功能 -->
 | 
			
		||||
                        <template #default="scope" v-if="item.slot">
 | 
			
		||||
                            <slot :name="item.prop" :data="scope.row"></slot>
 | 
			
		||||
                        </template>
 | 
			
		||||
 | 
			
		||||
                        <!-- 枚举类型使用tab展示 -->
 | 
			
		||||
                        <template #default="scope" v-else-if="item.type == 'tag'">
 | 
			
		||||
                            <enum-tag :size="props.size" :enums="item.typeParam" :value="scope.row[item.prop]"></enum-tag>
 | 
			
		||||
                        </template>
 | 
			
		||||
 | 
			
		||||
                        <template #default="scope" v-else>
 | 
			
		||||
                            <!-- 配置了美化文本按钮以及文本内容大于指定长度,则显示美化按钮 -->
 | 
			
		||||
                            <el-popover
 | 
			
		||||
                                v-if="item.isBeautify && scope.row[item.prop]?.length > 35"
 | 
			
		||||
                                effect="light"
 | 
			
		||||
                                trigger="click"
 | 
			
		||||
                                placement="top"
 | 
			
		||||
                                width="600px"
 | 
			
		||||
                            >
 | 
			
		||||
                                <template #default>
 | 
			
		||||
                                    <el-input :autosize="{ minRows: 3, maxRows: 15 }" disabled v-model="formatVal" type="textarea" />
 | 
			
		||||
                                </template>
 | 
			
		||||
                                <template #reference>
 | 
			
		||||
                                    <el-link
 | 
			
		||||
                                        @click="formatText(scope.row[item.prop])"
 | 
			
		||||
                                        :underline="false"
 | 
			
		||||
                                        type="success"
 | 
			
		||||
                                        icon="MagicStick"
 | 
			
		||||
                                        class="mr5"
 | 
			
		||||
                                    ></el-link>
 | 
			
		||||
                                </template>
 | 
			
		||||
                            </el-popover>
 | 
			
		||||
 | 
			
		||||
                            <span>{{ item.getValueByData(scope.row) }}</span>
 | 
			
		||||
                        </template>
 | 
			
		||||
                    </el-table-column>
 | 
			
		||||
                </template>
 | 
			
		||||
            </el-table>
 | 
			
		||||
 | 
			
		||||
            <el-row style="margin-top: 20px" type="flex" justify="end">
 | 
			
		||||
            <el-row class="mt20" type="flex" justify="end">
 | 
			
		||||
                <el-pagination
 | 
			
		||||
                    :small="props.size == 'small'"
 | 
			
		||||
                    @current-change="handlePageChange"
 | 
			
		||||
@@ -182,79 +124,55 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { toRefs, watch, reactive, onMounted, Ref } from 'vue';
 | 
			
		||||
import { TableColumn, TableQuery } from './index';
 | 
			
		||||
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots } from 'vue';
 | 
			
		||||
import { TableColumn } from './index';
 | 
			
		||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
			
		||||
import { useThemeConfig } from '@/store/themeConfig';
 | 
			
		||||
import { storeToRefs } from 'pinia';
 | 
			
		||||
import { useVModel } from '@vueuse/core';
 | 
			
		||||
import { useVModel, useEventListener } from '@vueuse/core';
 | 
			
		||||
import Api from '@/common/Api';
 | 
			
		||||
import SearchForm from '@/components/SearchForm/index.vue';
 | 
			
		||||
import { SearchItem } from '../SearchForm/index';
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['update:queryForm', 'update:pageNum', 'update:pageSize', 'update:selectionData', 'pageChange']);
 | 
			
		||||
const emit = defineEmits(['update:queryForm', 'update:selectionData', 'pageChange']);
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    size: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        default: '',
 | 
			
		||||
    },
 | 
			
		||||
    inputWidth: {
 | 
			
		||||
        type: [Number, String],
 | 
			
		||||
        default: '200px',
 | 
			
		||||
    },
 | 
			
		||||
    // 是否显示选择列
 | 
			
		||||
    showSelection: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        default: false,
 | 
			
		||||
    },
 | 
			
		||||
    // 当前选择的数据
 | 
			
		||||
    selectionData: {
 | 
			
		||||
        type: Array<any>,
 | 
			
		||||
    },
 | 
			
		||||
    // 列信息
 | 
			
		||||
    columns: {
 | 
			
		||||
        type: Array<TableColumn>,
 | 
			
		||||
        default: function () {
 | 
			
		||||
            return [];
 | 
			
		||||
        },
 | 
			
		||||
        required: true,
 | 
			
		||||
    },
 | 
			
		||||
    // 调用分页数据的api
 | 
			
		||||
    pageApi: {
 | 
			
		||||
        type: Api,
 | 
			
		||||
        required: true,
 | 
			
		||||
    },
 | 
			
		||||
    // 懒加载,即需要手动调用search方法才可调接口获取数据,不会在mounted的时候调用。
 | 
			
		||||
    lazy: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        default: false,
 | 
			
		||||
    },
 | 
			
		||||
    // 执行查询时对查询参数进行处理,调整等
 | 
			
		||||
    beforeQueryFn: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
    },
 | 
			
		||||
    // 数据处理回调函数,用于将请求回来的数据二次加工处理等
 | 
			
		||||
    dataHandlerFn: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
    },
 | 
			
		||||
    // 查询条件配置
 | 
			
		||||
    query: {
 | 
			
		||||
        type: Array<TableQuery>,
 | 
			
		||||
        default: function () {
 | 
			
		||||
            return [];
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
    // 绑定的查询表单
 | 
			
		||||
export interface PageTableProps {
 | 
			
		||||
    size?: string;
 | 
			
		||||
    showSelection?: boolean;
 | 
			
		||||
    showSearch?: boolean; // 是否显示搜索表单
 | 
			
		||||
    columns: TableColumn[]; // 列配置项  ==> 必传
 | 
			
		||||
    data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
 | 
			
		||||
    pageApi: Api; // 请求表格数据的 api
 | 
			
		||||
    lazy?: boolean; // 是否自动执行请求 api ==> 非必传(默认为false)
 | 
			
		||||
    beforeQueryFn?: (params: any) => any; // 执行查询时对查询参数进行处理,调整等
 | 
			
		||||
    dataHandlerFn?: (data: any) => any; // 数据处理回调函数,用于将请求回来的数据二次加工处理等
 | 
			
		||||
    searchItems?: SearchItem[];
 | 
			
		||||
    queryForm?: any; // 查询表单参数 ==> 非必传(默认为{pageNum:1, pageSize: 10})
 | 
			
		||||
    border?: boolean; // 是否带有纵向边框 ==> 非必传(默认为false)
 | 
			
		||||
    toolButton?: ('refresh' | 'setting' | 'search')[] | boolean; // 是否显示表格功能按钮 ==> 非必传(默认为true)
 | 
			
		||||
    searchCol?: any; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 接受父组件参数,配置默认值
 | 
			
		||||
const props = withDefaults(defineProps<PageTableProps>(), {
 | 
			
		||||
    columns: () => [],
 | 
			
		||||
    showSelection: false,
 | 
			
		||||
    lazy: false,
 | 
			
		||||
    initParam: {},
 | 
			
		||||
    queryForm: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        default: function () {
 | 
			
		||||
            return {
 | 
			
		||||
                pageNum: 1,
 | 
			
		||||
                pageSize: 10,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        pageNum: 1,
 | 
			
		||||
        pageSize: 0,
 | 
			
		||||
    },
 | 
			
		||||
    border: false,
 | 
			
		||||
    toolButton: true,
 | 
			
		||||
    showSearch: false,
 | 
			
		||||
    searchItems: () => [],
 | 
			
		||||
    searchCol: () => ({ xs: 1, sm: 3, md: 3, lg: 4, xl: 4 }),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 接收 columns 并设置为响应式
 | 
			
		||||
const tableColumns = reactive<TableColumn[]>(props.columns);
 | 
			
		||||
 | 
			
		||||
const { themeConfig } = storeToRefs(useThemeConfig());
 | 
			
		||||
 | 
			
		||||
const state = reactive({
 | 
			
		||||
@@ -267,10 +185,18 @@ const state = reactive({
 | 
			
		||||
    // 输入框宽度
 | 
			
		||||
    inputWidth_: '200px' as any,
 | 
			
		||||
    formatVal: '', // 格式化后的值
 | 
			
		||||
    tableMaxHeight: window.innerHeight - 240 + 'px',
 | 
			
		||||
    tableMaxHeight: '500px',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const { pageSizes, isOpenMoreQuery, defaultQueryCount, inputWidth_, formatVal, tableMaxHeight } = toRefs(state);
 | 
			
		||||
// 是否显示搜索模块
 | 
			
		||||
const isShowSearch = ref(props.showSearch);
 | 
			
		||||
 | 
			
		||||
// 控制 ToolButton 显示
 | 
			
		||||
const showToolButton = (key: 'refresh' | 'setting' | 'search') => {
 | 
			
		||||
    return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const { pageSizes, formatVal, tableMaxHeight } = toRefs(state);
 | 
			
		||||
 | 
			
		||||
const queryForm_: Ref<any> = useVModel(props, 'queryForm', emit);
 | 
			
		||||
 | 
			
		||||
@@ -287,9 +213,19 @@ watch(
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
    let pageSize = queryForm_.value.pageSize;
 | 
			
		||||
watch(
 | 
			
		||||
    () => isShowSearch.value,
 | 
			
		||||
    () => {
 | 
			
		||||
        console.log('watch show sa');
 | 
			
		||||
        calcuTableHeight();
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
    calcuTableHeight();
 | 
			
		||||
    useEventListener(window, 'resize', calcuTableHeight);
 | 
			
		||||
 | 
			
		||||
    let pageSize = queryForm_.value.pageSize;
 | 
			
		||||
    // 如果pageSize设为0,则使用系统全局配置的pageSize
 | 
			
		||||
    if (!pageSize) {
 | 
			
		||||
        pageSize = themeConfig.value.defaultListPageSize;
 | 
			
		||||
@@ -303,24 +239,14 @@ onMounted(async () => {
 | 
			
		||||
    queryForm_.value.pageSize = pageSize;
 | 
			
		||||
    state.pageSizes = [pageSize, pageSize * 2, pageSize * 3, pageSize * 4, pageSize * 5];
 | 
			
		||||
 | 
			
		||||
    // 如果没传输入框宽度,则根据组件size设置默认宽度
 | 
			
		||||
    if (!props.inputWidth) {
 | 
			
		||||
        state.inputWidth_ = props.size == 'small' ? '150px' : '200px';
 | 
			
		||||
    } else {
 | 
			
		||||
        state.inputWidth_ = props.inputWidth;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    window.addEventListener('resize', () => {
 | 
			
		||||
        calcuTableHeight();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!props.lazy) {
 | 
			
		||||
        await reqPageApi();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const calcuTableHeight = () => {
 | 
			
		||||
    state.tableMaxHeight = window.innerHeight - 240 + 'px';
 | 
			
		||||
    const headerHeight = isShowSearch.value ? 320 : 240;
 | 
			
		||||
    state.tableMaxHeight = window.innerHeight - headerHeight + 'px';
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const formatText = (data: any) => {
 | 
			
		||||
@@ -332,18 +258,6 @@ const formatText = (data: any) => {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getRowQueryItem = (row: number) => {
 | 
			
		||||
    // 第一行需要加个查询等按钮列
 | 
			
		||||
    if (row === 1) {
 | 
			
		||||
        const res = props.query.slice(row - 1, defaultQueryCount.value);
 | 
			
		||||
        // 查询等按钮列
 | 
			
		||||
        res.push(TableQuery.slot('', '', 'queryBtns'));
 | 
			
		||||
        return res;
 | 
			
		||||
    }
 | 
			
		||||
    const columnCount = defaultQueryCount.value + 1;
 | 
			
		||||
    return props.query.slice((row - 1) * columnCount - 1, row * columnCount - 1);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const handleSelectionChange = (val: any) => {
 | 
			
		||||
    emit('update:selectionData', val);
 | 
			
		||||
};
 | 
			
		||||
@@ -390,7 +304,7 @@ const queryData = () => {
 | 
			
		||||
 | 
			
		||||
const reset = () => {
 | 
			
		||||
    // 将查询参数绑定的值置空,并重新粗发查询接口
 | 
			
		||||
    for (let qi of props.query) {
 | 
			
		||||
    for (let qi of props.searchItems) {
 | 
			
		||||
        queryForm_.value[qi.prop] = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -411,49 +325,88 @@ defineExpose({
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped lang="scss">
 | 
			
		||||
.page-table {
 | 
			
		||||
    .query {
 | 
			
		||||
        margin-bottom: 10px;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
.table-box,
 | 
			
		||||
.table-main {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex: 1;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
 | 
			
		||||
        .is-open {
 | 
			
		||||
            // padding: 10px 0;
 | 
			
		||||
            max-height: 200px;
 | 
			
		||||
            margin-top: 10px;
 | 
			
		||||
    // 表格 header 样式
 | 
			
		||||
    .table-header {
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        .header-button-lf {
 | 
			
		||||
            float: left;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: flex-start;
 | 
			
		||||
        justify-content: space-between;
 | 
			
		||||
        .header-button-ri {
 | 
			
		||||
            float: right;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .slot {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            justify-content: flex-end;
 | 
			
		||||
        .el-button {
 | 
			
		||||
            margin-bottom: 10px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .page {
 | 
			
		||||
        margin-top: 10px;
 | 
			
		||||
    // el-table 表格样式
 | 
			
		||||
    .el-table {
 | 
			
		||||
        flex: 1;
 | 
			
		||||
 | 
			
		||||
        // 修复 safari 浏览器表格错位 https://github.com/HalseySpicy/Geeker-Admin/issues/83
 | 
			
		||||
        table {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // .el-table__header th {
 | 
			
		||||
        //     height: 45px;
 | 
			
		||||
        //     font-size: 15px;
 | 
			
		||||
        //     font-weight: bold;
 | 
			
		||||
        //     color: var(--el-text-color-primary);
 | 
			
		||||
        //     background: var(--el-fill-color-light);
 | 
			
		||||
        // }
 | 
			
		||||
 | 
			
		||||
        // .el-table__row {
 | 
			
		||||
        //     height: 45px;
 | 
			
		||||
        //     font-size: 14px;
 | 
			
		||||
 | 
			
		||||
        //     .move {
 | 
			
		||||
        //         cursor: move;
 | 
			
		||||
 | 
			
		||||
        //         .el-icon {
 | 
			
		||||
        //             cursor: move;
 | 
			
		||||
        //         }
 | 
			
		||||
        //     }
 | 
			
		||||
        // }
 | 
			
		||||
 | 
			
		||||
        // 设置 el-table 中 header 文字不换行,并省略
 | 
			
		||||
        .el-table__header .el-table__cell > .cell {
 | 
			
		||||
            // white-space: nowrap;
 | 
			
		||||
            white-space: wrap;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 解决表格数据为空时样式不居中问题(仅在element-plus中)
 | 
			
		||||
        // .el-table__empty-block {
 | 
			
		||||
        //     position: absolute;
 | 
			
		||||
        //     top: 50%;
 | 
			
		||||
        //     left: 50%;
 | 
			
		||||
        //     transform: translate(-50%, -50%);
 | 
			
		||||
 | 
			
		||||
        //     .table-empty {
 | 
			
		||||
        //         line-height: 30px;
 | 
			
		||||
        //     }
 | 
			
		||||
        // }
 | 
			
		||||
 | 
			
		||||
        // table 中 image 图片样式
 | 
			
		||||
        .table-image {
 | 
			
		||||
            width: 50px;
 | 
			
		||||
            height: 50px;
 | 
			
		||||
            border-radius: 50%;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
::v-deep(.el-form-item__label) {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-select-v2 {
 | 
			
		||||
    width: v-bind(inputWidth_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-input {
 | 
			
		||||
    width: v-bind(inputWidth_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-select {
 | 
			
		||||
    width: v-bind(inputWidth_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-date-editor {
 | 
			
		||||
    width: 380px !important;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -242,68 +242,3 @@ export class TableColumn {
 | 
			
		||||
        this.minWidth = (flexWidth > 400 ? 400 : flexWidth) + this.addWidth;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class TableQuery {
 | 
			
		||||
    /**
 | 
			
		||||
     * 属性字段
 | 
			
		||||
     */
 | 
			
		||||
    prop: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 显示表头
 | 
			
		||||
     */
 | 
			
		||||
    label: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询类型,text、select、date
 | 
			
		||||
     */
 | 
			
		||||
    type: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * select可选值
 | 
			
		||||
     */
 | 
			
		||||
    options: any;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 插槽名
 | 
			
		||||
     */
 | 
			
		||||
    slot: string;
 | 
			
		||||
 | 
			
		||||
    constructor(prop: string, label: string) {
 | 
			
		||||
        this.prop = prop;
 | 
			
		||||
        this.label = label;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static new(prop: string, label: string): TableQuery {
 | 
			
		||||
        return new TableQuery(prop, label);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static text(prop: string, label: string): TableQuery {
 | 
			
		||||
        const tq = new TableQuery(prop, label);
 | 
			
		||||
        tq.type = 'text';
 | 
			
		||||
        return tq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static select(prop: string, label: string): TableQuery {
 | 
			
		||||
        const tq = new TableQuery(prop, label);
 | 
			
		||||
        tq.type = 'select';
 | 
			
		||||
        return tq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static date(prop: string, label: string): TableQuery {
 | 
			
		||||
        const tq = new TableQuery(prop, label);
 | 
			
		||||
        tq.type = 'date';
 | 
			
		||||
        return tq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static slot(prop: string, label: string, slotName: string): TableQuery {
 | 
			
		||||
        const tq = new TableQuery(prop, label);
 | 
			
		||||
        tq.slot = slotName;
 | 
			
		||||
        return tq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setOptions(options: any): TableQuery {
 | 
			
		||||
        this.options = options;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -138,7 +138,7 @@ export const useThemeConfig = defineStore('themeConfig', {
 | 
			
		||||
 | 
			
		||||
            /** 全局设置 */
 | 
			
		||||
            // 默认列表页的分页大小
 | 
			
		||||
            defaultListPageSize: 15,
 | 
			
		||||
            defaultListPageSize: 10,
 | 
			
		||||
        },
 | 
			
		||||
    }),
 | 
			
		||||
    actions: {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,28 +4,20 @@
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="dbApi.dbs"
 | 
			
		||||
            :before-query-fn="checkRouteTagPath"
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :search-items="searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="state.selectionData"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #tagPathSelect>
 | 
			
		||||
                <el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable style="width: 200px">
 | 
			
		||||
                <el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
 | 
			
		||||
                    <el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
 | 
			
		||||
                </el-select>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template #instanceSelect>
 | 
			
		||||
                <el-select
 | 
			
		||||
                    remote
 | 
			
		||||
                    :remote-method="getInstances"
 | 
			
		||||
                    v-model="query.instanceId"
 | 
			
		||||
                    placeholder="输入并选择实例"
 | 
			
		||||
                    filterable
 | 
			
		||||
                    clearable
 | 
			
		||||
                    style="width: 200px"
 | 
			
		||||
                >
 | 
			
		||||
                <el-select remote :remote-method="getInstances" v-model="query.instanceId" placeholder="输入并选择实例" filterable clearable>
 | 
			
		||||
                    <el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item.id">
 | 
			
		||||
                        {{ item.name }}
 | 
			
		||||
                        <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
@@ -37,7 +29,7 @@
 | 
			
		||||
                </el-select>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="perms.saveDb" type="primary" icon="plus" @click="editDb(false)">添加</el-button>
 | 
			
		||||
                <el-button v-auth="perms.delDb" :disabled="selectionData.length < 1" @click="deleteDb()" type="danger" icon="delete">删除</el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -170,7 +162,7 @@ import { isTrue } from '@/common/assert';
 | 
			
		||||
import { dateFormat } from '@/common/utils/date';
 | 
			
		||||
import ResourceTag from '../component/ResourceTag.vue';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { hasPerms } from '@/components/auth/auth';
 | 
			
		||||
import DbSqlExecLog from './DbSqlExecLog.vue';
 | 
			
		||||
import { DbType } from './dialect';
 | 
			
		||||
@@ -178,6 +170,7 @@ import { tagApi } from '../tag/api';
 | 
			
		||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
			
		||||
import { useRoute } from 'vue-router';
 | 
			
		||||
import { getDbDialect } from './dialect/index';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue'));
 | 
			
		||||
 | 
			
		||||
@@ -187,7 +180,7 @@ const perms = {
 | 
			
		||||
    delDb: 'db:del',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect'), TableQuery.slot('instanceId', '实例', 'instanceSelect')];
 | 
			
		||||
const searchItems = [SearchItem.slot('tagPath', '标签', 'tagPathSelect'), SearchItem.slot('instanceId', '实例', 'instanceSelect')];
 | 
			
		||||
 | 
			
		||||
const columns = ref([
 | 
			
		||||
    TableColumn.new('instanceName', '实例名'),
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,12 @@
 | 
			
		||||
            :page-api="dbApi.getSqlExecs"
 | 
			
		||||
            :lazy="true"
 | 
			
		||||
            height="100%"
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :search-items="searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #dbSelect>
 | 
			
		||||
                <el-select v-model="query.db" placeholder="请选择数据库" style="width: 200px" filterable clearable>
 | 
			
		||||
                <el-select v-model="query.db" placeholder="请选择数据库" filterable clearable>
 | 
			
		||||
                    <el-option v-for="item in dbs" :key="item" :label="`${item}`" :value="item"> </el-option>
 | 
			
		||||
                </el-select>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -40,7 +40,8 @@ import { toRefs, watch, reactive, onMounted, Ref, ref } from 'vue';
 | 
			
		||||
import { dbApi } from './api';
 | 
			
		||||
import { DbSqlExecTypeEnum } from './enums';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    dbId: {
 | 
			
		||||
@@ -53,13 +54,13 @@ const props = defineProps({
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const queryConfig = [
 | 
			
		||||
    TableQuery.slot('db', '数据库', 'dbSelect'),
 | 
			
		||||
    TableQuery.text('table', '表名'),
 | 
			
		||||
    TableQuery.select('type', '操作类型').setOptions(Object.values(DbSqlExecTypeEnum)),
 | 
			
		||||
const searchItems = [
 | 
			
		||||
    SearchItem.slot('db', '数据库', 'dbSelect'),
 | 
			
		||||
    SearchItem.text('table', '表名'),
 | 
			
		||||
    SearchItem.select('type', '操作类型').withEnum(DbSqlExecTypeEnum),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const columns = [
 | 
			
		||||
const columns = ref([
 | 
			
		||||
    TableColumn.new('db', '数据库'),
 | 
			
		||||
    TableColumn.new('table', '表'),
 | 
			
		||||
    TableColumn.new('type', '类型').typeTag(DbSqlExecTypeEnum).setAddWidth(10),
 | 
			
		||||
@@ -69,7 +70,7 @@ const columns = [
 | 
			
		||||
    TableColumn.new('createTime', '执行时间').isTime(),
 | 
			
		||||
    TableColumn.new('remark', '备注'),
 | 
			
		||||
    TableColumn.new('action', '操作').isSlot().setMinWidth(90).fixedRight().alignCenter(),
 | 
			
		||||
];
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
const pageTableRef: Ref<any> = ref(null);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,13 @@
 | 
			
		||||
        <page-table
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="dbApi.instances"
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :searchItems="searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="state.selectionData"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="perms.saveInstance" type="primary" icon="plus" @click="editInstance(false)">添加</el-button>
 | 
			
		||||
                <el-button v-auth="perms.delInstance" :disabled="selectionData.length < 1" @click="deleteInstance()" type="danger" icon="delete"
 | 
			
		||||
                    >删除</el-button
 | 
			
		||||
@@ -66,10 +66,11 @@ import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import { dbApi } from './api';
 | 
			
		||||
import { dateFormat } from '@/common/utils/date';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { hasPerms } from '@/components/auth/auth';
 | 
			
		||||
import SvgIcon from '@/components/svgIcon/index.vue';
 | 
			
		||||
import { getDbDialect } from './dialect';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const InstanceEdit = defineAsyncComponent(() => import('./InstanceEdit.vue'));
 | 
			
		||||
 | 
			
		||||
@@ -78,7 +79,7 @@ const perms = {
 | 
			
		||||
    delInstance: 'db:instance:del',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const queryConfig = [TableQuery.text('name', '名称')];
 | 
			
		||||
const searchItems = [SearchItem.text('name', '名称')];
 | 
			
		||||
 | 
			
		||||
const columns = ref([
 | 
			
		||||
    TableColumn.new('name', '名称'),
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,19 @@
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="machineApi.list"
 | 
			
		||||
            :before-query-fn="checkRouteTagPath"
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :search-items="searchItems"
 | 
			
		||||
            v-model:query-form="params"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="state.selectionData"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #tagPathSelect>
 | 
			
		||||
                <el-select @focus="getTags" v-model="params.tagPath" placeholder="请选择标签" @clear="search" filterable clearable style="width: 200px">
 | 
			
		||||
                <el-select @focus="getTags" v-model="params.tagPath" placeholder="请选择标签" @clear="search" filterable clearable>
 | 
			
		||||
                    <el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
 | 
			
		||||
                </el-select>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="perms.addMachine" type="primary" icon="plus" @click="openFormDialog(false)" plain>添加 </el-button>
 | 
			
		||||
                <el-button v-auth="perms.delMachine" :disabled="selectionData.length < 1" @click="deleteMachine()" type="danger" icon="delete">删除</el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -190,11 +190,12 @@ import { machineApi, getMachineTerminalSocketUrl } from './api';
 | 
			
		||||
import { dateFormat } from '@/common/utils/date';
 | 
			
		||||
import ResourceTag from '../component/ResourceTag.vue';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { hasPerms } from '@/components/auth/auth';
 | 
			
		||||
import { formatByteSize } from '@/common/utils/format';
 | 
			
		||||
import { tagApi } from '../tag/api';
 | 
			
		||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
// 组件
 | 
			
		||||
const TerminalDialog = defineAsyncComponent(() => import('@/components/terminal/TerminalDialog.vue'));
 | 
			
		||||
@@ -218,9 +219,9 @@ const perms = {
 | 
			
		||||
    closeCli: 'machine:close-cli',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect'), TableQuery.text('ip', 'IP'), TableQuery.text('name', '名称')];
 | 
			
		||||
const searchItems = [SearchItem.slot('tagPath', '标签', 'tagPathSelect'), SearchItem.text('ip', 'IP'), SearchItem.text('name', '名称')];
 | 
			
		||||
 | 
			
		||||
const columns = ref([
 | 
			
		||||
const columns = [
 | 
			
		||||
    TableColumn.new('name', '名称'),
 | 
			
		||||
    TableColumn.new('ipPort', 'ip:port').isSlot().setAddWidth(50),
 | 
			
		||||
    TableColumn.new('stat', '运行状态').isSlot().setAddWidth(50),
 | 
			
		||||
@@ -230,7 +231,7 @@ const columns = ref([
 | 
			
		||||
    TableColumn.new('tagPath', '关联标签').isSlot().setAddWidth(10).alignCenter(),
 | 
			
		||||
    TableColumn.new('remark', '备注'),
 | 
			
		||||
    TableColumn.new('action', '操作').isSlot().setMinWidth(238).fixedRight().alignCenter(),
 | 
			
		||||
]);
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// 该用户拥有的的操作列按钮权限,使用v-if进行判断,v-auth对el-dropdown-item无效
 | 
			
		||||
const actionBtns = hasPerms([perms.updateMachine, perms.closeCli]);
 | 
			
		||||
 
 | 
			
		||||
@@ -14,13 +14,13 @@
 | 
			
		||||
                :page-api="machineApi.scripts"
 | 
			
		||||
                :before-query-fn="checkScriptType"
 | 
			
		||||
                :lazy="true"
 | 
			
		||||
                :query="queryConfig"
 | 
			
		||||
                :search-items="state.searchItems"
 | 
			
		||||
                v-model:query-form="query"
 | 
			
		||||
                :columns="columns"
 | 
			
		||||
                :show-selection="true"
 | 
			
		||||
                v-model:selection-data="selectionData"
 | 
			
		||||
            >
 | 
			
		||||
                <template #queryRight>
 | 
			
		||||
                <template #tableHeader>
 | 
			
		||||
                    <el-button v-auth="'machine:script:save'" type="primary" @click="editScript(null)" icon="plus" plain>添加</el-button>
 | 
			
		||||
                    <el-button
 | 
			
		||||
                        v-auth="'machine:script:del'"
 | 
			
		||||
@@ -93,8 +93,9 @@ import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
			
		||||
import { ScriptResultEnum, ScriptTypeEnum } from './enums';
 | 
			
		||||
import ScriptEdit from './ScriptEdit.vue';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { DynamicFormDialog } from '@/components/dynamic-form';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    visible: { type: Boolean },
 | 
			
		||||
@@ -110,7 +111,7 @@ const pageTableRef: Ref<any> = ref(null);
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    dialogVisible: false,
 | 
			
		||||
    selectionData: [],
 | 
			
		||||
    queryConfig: [TableQuery.select('type', '类型').setOptions(Object.values(ScriptTypeEnum))],
 | 
			
		||||
    searchItems: [SearchItem.select('type', '类型').withEnum(ScriptTypeEnum)],
 | 
			
		||||
    columns: [
 | 
			
		||||
        TableColumn.new('name', '名称'),
 | 
			
		||||
        TableColumn.new('description', '描述'),
 | 
			
		||||
@@ -129,8 +130,6 @@ const state = reactive({
 | 
			
		||||
        title: '',
 | 
			
		||||
        machineId: 9999999,
 | 
			
		||||
    },
 | 
			
		||||
    total: 0,
 | 
			
		||||
    scriptTable: [],
 | 
			
		||||
    scriptParamsDialog: {
 | 
			
		||||
        script: null,
 | 
			
		||||
        visible: false,
 | 
			
		||||
@@ -148,7 +147,7 @@ const state = reactive({
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const { dialogVisible, queryConfig, columns, selectionData, query, editDialog, scriptParamsDialog, resultDialog, terminalDialog } = toRefs(state);
 | 
			
		||||
const { dialogVisible, columns, selectionData, query, editDialog, scriptParamsDialog, resultDialog, terminalDialog } = toRefs(state);
 | 
			
		||||
 | 
			
		||||
watch(props, async (newValue) => {
 | 
			
		||||
    state.dialogVisible = newValue.visible;
 | 
			
		||||
@@ -281,7 +280,6 @@ const handleClose = () => {
 | 
			
		||||
    emit('update:machineId', null);
 | 
			
		||||
    emit('cancel');
 | 
			
		||||
    state.query.type = ScriptTypeEnum.Private.value;
 | 
			
		||||
    state.scriptTable = [];
 | 
			
		||||
    state.scriptParamsDialog.paramsFormItem = [];
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,13 @@
 | 
			
		||||
        <page-table
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="authCertApi.list"
 | 
			
		||||
            :query="state.queryConfig"
 | 
			
		||||
            :search-items="state.searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="selectionData"
 | 
			
		||||
            :columns="state.columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button type="primary" icon="plus" @click="edit(false)">添加</el-button>
 | 
			
		||||
                <el-button :disabled="selectionData.length < 1" @click="deleteAc(selectionData)" type="danger" icon="delete">删除 </el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -29,8 +29,9 @@ import AuthCertEdit from './AuthCertEdit.vue';
 | 
			
		||||
import { authCertApi } from '../api';
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { AuthMethodEnum } from '../enums';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const pageTableRef: Ref<any> = ref(null);
 | 
			
		||||
const state = reactive({
 | 
			
		||||
@@ -39,7 +40,7 @@ const state = reactive({
 | 
			
		||||
        pageSize: 0,
 | 
			
		||||
        name: null,
 | 
			
		||||
    },
 | 
			
		||||
    queryConfig: [TableQuery.text('name', '凭证名称')],
 | 
			
		||||
    searchItems: [SearchItem.text('name', '凭证名称')],
 | 
			
		||||
    columns: [
 | 
			
		||||
        TableColumn.new('name', '名称'),
 | 
			
		||||
        TableColumn.new('authMethod', '认证方式').typeTag(AuthMethodEnum),
 | 
			
		||||
@@ -50,8 +51,6 @@ const state = reactive({
 | 
			
		||||
        TableColumn.new('createTime', '修改时间').isTime(),
 | 
			
		||||
        TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(65).alignCenter(),
 | 
			
		||||
    ],
 | 
			
		||||
    total: 0,
 | 
			
		||||
    authcerts: [],
 | 
			
		||||
    selectionData: [],
 | 
			
		||||
    paramsDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
 
 | 
			
		||||
@@ -15,13 +15,13 @@
 | 
			
		||||
                :page-api="cronJobApi.execList"
 | 
			
		||||
                :lazy="true"
 | 
			
		||||
                :data-handler-fn="parseData"
 | 
			
		||||
                :query="queryConfig"
 | 
			
		||||
                :search-items="searchItems"
 | 
			
		||||
                v-model:query-form="params"
 | 
			
		||||
                :data="state.data.list"
 | 
			
		||||
                :columns="columns"
 | 
			
		||||
            >
 | 
			
		||||
                <template #machineSelect>
 | 
			
		||||
                    <el-select v-model="params.machineId" filterable placeholder="选择机器查询" style="width: 200px" clearable>
 | 
			
		||||
                    <el-select v-model="params.machineId" filterable placeholder="选择机器查询" clearable>
 | 
			
		||||
                        <el-option v-for="ac in machineMap.values()" :key="ac.id" :value="ac.id" :label="ac.ip">
 | 
			
		||||
                            {{ ac.ip }}
 | 
			
		||||
                            <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
@@ -38,8 +38,9 @@
 | 
			
		||||
import { watch, ref, toRefs, reactive, Ref } from 'vue';
 | 
			
		||||
import { cronJobApi, machineApi } from '../api';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { CronJobExecStatusEnum } from '../enums';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    visible: {
 | 
			
		||||
@@ -55,9 +56,9 @@ const props = defineProps({
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['update:visible', 'update:data', 'cancel']);
 | 
			
		||||
 | 
			
		||||
const queryConfig = [
 | 
			
		||||
    TableQuery.slot('machineId', '机器', 'machineSelect'),
 | 
			
		||||
    TableQuery.select('status', '状态').setOptions(Object.values(CronJobExecStatusEnum)),
 | 
			
		||||
const searchItems = [
 | 
			
		||||
    SearchItem.slot('machineId', '机器', 'machineSelect'),
 | 
			
		||||
    SearchItem.select('status', '状态').setOptions(Object.values(CronJobExecStatusEnum)),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const columns = ref([
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,13 @@
 | 
			
		||||
        <page-table
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="cronJobApi.list"
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :query="searchItems"
 | 
			
		||||
            v-model:query-form="params"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="state.selectionData"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="perms.saveCronJob" type="primary" icon="plus" @click="openFormDialog(false)" plain>添加 </el-button>
 | 
			
		||||
                <el-button v-auth="perms.delCronJob" :disabled="selectionData.length < 1" @click="deleteCronJob()" type="danger" icon="delete">删除</el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -38,8 +38,9 @@ import { ref, toRefs, reactive, onMounted, defineAsyncComponent, Ref } from 'vue
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import { cronJobApi } from '../api';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { CronJobStatusEnum, CronJobSaveExecResTypeEnum } from '../enums';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const CronJobEdit = defineAsyncComponent(() => import('./CronJobEdit.vue'));
 | 
			
		||||
const CronJobExecList = defineAsyncComponent(() => import('./CronJobExecList.vue'));
 | 
			
		||||
@@ -49,7 +50,7 @@ const perms = {
 | 
			
		||||
    delCronJob: 'machine:cronjob:del',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const queryConfig = [TableQuery.text('name', '名称'), TableQuery.select('status', '状态').setOptions(Object.values(CronJobStatusEnum))];
 | 
			
		||||
const searchItems = [SearchItem.text('name', '名称'), SearchItem.select('status', '状态').withEnum(CronJobStatusEnum)];
 | 
			
		||||
 | 
			
		||||
const columns = ref([
 | 
			
		||||
    TableColumn.new('key', 'key'),
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,19 @@
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="mongoApi.mongoList"
 | 
			
		||||
            :before-query-fn="checkRouteTagPath"
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :search-items="searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="selectionData"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #tagPathSelect>
 | 
			
		||||
                <el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" @clear="search" filterable clearable style="width: 200px">
 | 
			
		||||
                <el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" @clear="search" filterable clearable>
 | 
			
		||||
                    <el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
 | 
			
		||||
                </el-select>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button type="primary" icon="plus" @click="editMongo(true)" plain>添加</el-button>
 | 
			
		||||
                <el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteMongo" plain>删除 </el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -53,10 +53,11 @@ import { defineAsyncComponent, ref, toRefs, reactive, onMounted, Ref } from 'vue
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import ResourceTag from '../component/ResourceTag.vue';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
			
		||||
import { tagApi } from '../tag/api';
 | 
			
		||||
import { useRoute } from 'vue-router';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const MongoEdit = defineAsyncComponent(() => import('./MongoEdit.vue'));
 | 
			
		||||
const MongoDbs = defineAsyncComponent(() => import('./MongoDbs.vue'));
 | 
			
		||||
@@ -65,15 +66,15 @@ const MongoRunCommand = defineAsyncComponent(() => import('./MongoRunCommand.vue
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const pageTableRef: Ref<any> = ref(null);
 | 
			
		||||
 | 
			
		||||
const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect')];
 | 
			
		||||
const columns = ref([
 | 
			
		||||
const searchItems = [SearchItem.slot('tagPath', '标签', 'tagPathSelect')];
 | 
			
		||||
const columns = [
 | 
			
		||||
    TableColumn.new('name', '名称'),
 | 
			
		||||
    TableColumn.new('uri', '连接uri'),
 | 
			
		||||
    TableColumn.new('tagPath', '关联标签').isSlot().setAddWidth(20).alignCenter(),
 | 
			
		||||
    TableColumn.new('createTime', '创建时间').isTime(),
 | 
			
		||||
    TableColumn.new('creator', '创建人'),
 | 
			
		||||
    TableColumn.new('action', '操作').isSlot().setMinWidth(170).fixedRight().alignCenter(),
 | 
			
		||||
]);
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    tags: [],
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,19 @@
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="redisApi.redisList"
 | 
			
		||||
            :before-query-fn="checkRouteTagPath"
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :searchItems="searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="selectionData"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #tagPathSelect>
 | 
			
		||||
                <el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" @clear="search" filterable clearable style="width: 200px">
 | 
			
		||||
                <el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" @clear="search" filterable clearable>
 | 
			
		||||
                    <el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
 | 
			
		||||
                </el-select>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button type="primary" icon="plus" @click="editRedis(false)" plain>添加</el-button>
 | 
			
		||||
                <el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteRedis" plain>删除 </el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -163,15 +163,16 @@ import RedisEdit from './RedisEdit.vue';
 | 
			
		||||
import { dateFormat } from '@/common/utils/date';
 | 
			
		||||
import ResourceTag from '../component/ResourceTag.vue';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { tagApi } from '../tag/api';
 | 
			
		||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
			
		||||
import { useRoute } from 'vue-router';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const pageTableRef: Ref<any> = ref(null);
 | 
			
		||||
 | 
			
		||||
const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect')];
 | 
			
		||||
const searchItems = [SearchItem.slot('tagPath', '标签', 'tagPathSelect')];
 | 
			
		||||
const columns = ref([
 | 
			
		||||
    TableColumn.new('name', '名称'),
 | 
			
		||||
    TableColumn.new('host', 'host:port'),
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,13 @@
 | 
			
		||||
        <page-table
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="tagApi.getTeams"
 | 
			
		||||
            :query="state.queryConfig"
 | 
			
		||||
            :search-items="searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="selectionData"
 | 
			
		||||
            :columns="state.columns"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="'team:save'" type="primary" icon="plus" @click="showSaveTeamDialog(false)">添加</el-button>
 | 
			
		||||
                <el-button v-auth="'team:del'" :disabled="selectionData.length < 1" @click="deleteTeam()" type="danger" icon="delete">删除</el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -91,11 +91,11 @@
 | 
			
		||||
                ref="showMemPageTableRef"
 | 
			
		||||
                :page-api="tagApi.getTeamMem"
 | 
			
		||||
                :lazy="true"
 | 
			
		||||
                :query="showMemDialog.queryConfig"
 | 
			
		||||
                :search-items="showMemDialog.searchItems"
 | 
			
		||||
                v-model:query-form="showMemDialog.query"
 | 
			
		||||
                :columns="showMemDialog.columns"
 | 
			
		||||
            >
 | 
			
		||||
                <template #queryRight>
 | 
			
		||||
                <template #tableHeader>
 | 
			
		||||
                    <el-button v-auth="'team:member:save'" @click="showAddMemberDialog()" type="primary" icon="plus">添加</el-button>
 | 
			
		||||
                </template>
 | 
			
		||||
 | 
			
		||||
@@ -139,13 +139,23 @@ import { accountApi } from '../../system/api';
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import { notBlank } from '@/common/assert';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const teamForm: any = ref(null);
 | 
			
		||||
const tagTreeRef: any = ref(null);
 | 
			
		||||
const pageTableRef: Ref<any> = ref(null);
 | 
			
		||||
const showMemPageTableRef: Ref<any> = ref(null);
 | 
			
		||||
 | 
			
		||||
const searchItems = [SearchItem.text('name', '团队名称')];
 | 
			
		||||
const columns = [
 | 
			
		||||
    TableColumn.new('name', '团队名称'),
 | 
			
		||||
    TableColumn.new('remark', '备注'),
 | 
			
		||||
    TableColumn.new('createTime', '创建时间').isTime(),
 | 
			
		||||
    TableColumn.new('creator', '创建人'),
 | 
			
		||||
    TableColumn.new('action', '操作').isSlot().setMinWidth(120).fixedRight().alignCenter(),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const state = reactive({
 | 
			
		||||
    currentEditPermissions: false,
 | 
			
		||||
    addTeamDialog: {
 | 
			
		||||
@@ -158,17 +168,9 @@ const state = reactive({
 | 
			
		||||
        pageSize: 0,
 | 
			
		||||
        name: null,
 | 
			
		||||
    },
 | 
			
		||||
    queryConfig: [TableQuery.text('name', '团队名称')],
 | 
			
		||||
    columns: [
 | 
			
		||||
        TableColumn.new('name', '团队名称'),
 | 
			
		||||
        TableColumn.new('remark', '备注'),
 | 
			
		||||
        TableColumn.new('createTime', '创建时间').isTime(),
 | 
			
		||||
        TableColumn.new('creator', '创建人'),
 | 
			
		||||
        TableColumn.new('action', '操作').isSlot().setMinWidth(120).fixedRight().alignCenter(),
 | 
			
		||||
    ],
 | 
			
		||||
    selectionData: [],
 | 
			
		||||
    showMemDialog: {
 | 
			
		||||
        queryConfig: [TableQuery.text('username', '用户名')],
 | 
			
		||||
        searchItems: [SearchItem.text('username', '用户名').withSpan(2)],
 | 
			
		||||
        columns: [
 | 
			
		||||
            TableColumn.new('name', '姓名'),
 | 
			
		||||
            TableColumn.new('username', '账号'),
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,13 @@
 | 
			
		||||
        <page-table
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
            :page-api="accountApi.list"
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :search-items="searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="selectionData"
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
        >
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="perms.addAccount" type="primary" icon="plus" @click="editAccount(false)">添加</el-button>
 | 
			
		||||
                <el-button v-auth="perms.delAccount" :disabled="state.selectionData.length < 1" @click="deleteAccount()" type="danger" icon="delete"
 | 
			
		||||
                    >删除</el-button
 | 
			
		||||
@@ -85,8 +85,9 @@ import { accountApi } from '../api';
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import { dateFormat } from '@/common/utils/date';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { hasPerms } from '@/components/auth/auth';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const perms = {
 | 
			
		||||
    addAccount: 'account:add',
 | 
			
		||||
@@ -95,8 +96,8 @@ const perms = {
 | 
			
		||||
    changeAccountStatus: 'account:changeStatus',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const queryConfig = [TableQuery.text('username', '用户名')];
 | 
			
		||||
const columns = ref([
 | 
			
		||||
const searchItems = [SearchItem.text('username', '用户名')];
 | 
			
		||||
const columns = [
 | 
			
		||||
    TableColumn.new('name', '姓名'),
 | 
			
		||||
    TableColumn.new('username', '用户名'),
 | 
			
		||||
    TableColumn.new('status', '状态').typeTag(AccountStatusEnum),
 | 
			
		||||
@@ -106,7 +107,7 @@ const columns = ref([
 | 
			
		||||
    TableColumn.new('createTime', '创建时间').isTime(),
 | 
			
		||||
    TableColumn.new('modifier', '更新账号'),
 | 
			
		||||
    TableColumn.new('updateTime', '更新时间').isTime(),
 | 
			
		||||
]);
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// 该用户拥有的的操作列按钮权限
 | 
			
		||||
const actionBtns = hasPerms([perms.addAccount, perms.saveAccountRole, perms.changeAccountStatus]);
 | 
			
		||||
@@ -155,7 +156,7 @@ const { selectionData, query, showRoleDialog, showResourceDialog, roleDialog, ac
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
    if (Object.keys(actionBtns).length > 0) {
 | 
			
		||||
        columns.value.push(actionColumn);
 | 
			
		||||
        columns.push(actionColumn);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <page-table ref="pageTableRef" :page-api="configApi.list" v-model:selection-data="selectionData" :columns="columns">
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="perms.saveConfig" type="primary" icon="plus" @click="editConfig(false)">添加</el-button>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <page-table
 | 
			
		||||
            :query="queryConfig"
 | 
			
		||||
            :search-items="searchItems"
 | 
			
		||||
            v-model:query-form="query"
 | 
			
		||||
            :show-selection="true"
 | 
			
		||||
            v-model:selection-data="selectionData"
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
            :page-api="roleApi.list"
 | 
			
		||||
            ref="pageTableRef"
 | 
			
		||||
        >
 | 
			
		||||
            <template #queryRight>
 | 
			
		||||
            <template #tableHeader>
 | 
			
		||||
                <el-button v-auth="perms.addRole" type="primary" icon="plus" @click="editRole(false)">添加</el-button>
 | 
			
		||||
                <el-button v-auth="perms.delRole" :disabled="selectionData.length < 1" @click="deleteRole(selectionData)" type="danger" icon="delete"
 | 
			
		||||
                    >删除</el-button
 | 
			
		||||
@@ -43,9 +43,10 @@ import ShowResource from './ShowResource.vue';
 | 
			
		||||
import { roleApi, resourceApi } from '../api';
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { hasPerms } from '@/components/auth/auth';
 | 
			
		||||
import { RoleStatusEnum } from '../enums';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const perms = {
 | 
			
		||||
    addRole: 'role:add',
 | 
			
		||||
@@ -54,7 +55,7 @@ const perms = {
 | 
			
		||||
    saveRoleResource: 'role:saveResources',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const queryConfig = [TableQuery.text('name', '角色名')];
 | 
			
		||||
const searchItems = [SearchItem.text('name', '角色名')];
 | 
			
		||||
const columns = ref([
 | 
			
		||||
    TableColumn.new('name', '角色名称'),
 | 
			
		||||
    TableColumn.new('code', '角色code'),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,8 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <page-table :query="queryConfig" v-model:query-form="query" :columns="columns" :page-api="logApi.list">
 | 
			
		||||
        <page-table :search-items="searchItems" v-model:query-form="query" :columns="columns" :page-api="logApi.list">
 | 
			
		||||
            <template #selectAccount>
 | 
			
		||||
                <el-select
 | 
			
		||||
                    style="width: 200px"
 | 
			
		||||
                    remote
 | 
			
		||||
                    :remote-method="getAccount"
 | 
			
		||||
                    v-model="query.creatorId"
 | 
			
		||||
                    filterable
 | 
			
		||||
                    placeholder="请输入并选择账号"
 | 
			
		||||
                    clearable
 | 
			
		||||
                >
 | 
			
		||||
                <el-select remote :remote-method="getAccount" v-model="query.creatorId" filterable placeholder="请输入并选择账号" clearable>
 | 
			
		||||
                    <el-option v-for="item in accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
 | 
			
		||||
                </el-select>
 | 
			
		||||
            </template>
 | 
			
		||||
@@ -22,13 +14,14 @@
 | 
			
		||||
import { toRefs, reactive } from 'vue';
 | 
			
		||||
import { logApi, accountApi } from '../api';
 | 
			
		||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
			
		||||
import { TableColumn, TableQuery } from '@/components/pagetable';
 | 
			
		||||
import { TableColumn } from '@/components/pagetable';
 | 
			
		||||
import { LogTypeEnum } from '../enums';
 | 
			
		||||
import { SearchItem } from '@/components/SearchForm';
 | 
			
		||||
 | 
			
		||||
const queryConfig = [
 | 
			
		||||
    TableQuery.slot('creatorId', '操作人', 'selectAccount'),
 | 
			
		||||
    TableQuery.select('type', '操作结果').setOptions(Object.values(LogTypeEnum)),
 | 
			
		||||
    TableQuery.text('description', '描述'),
 | 
			
		||||
const searchItems = [
 | 
			
		||||
    SearchItem.slot('creatorId', '操作人', 'selectAccount'),
 | 
			
		||||
    SearchItem.select('type', '操作结果').withEnum(LogTypeEnum),
 | 
			
		||||
    SearchItem.text('description', '描述'),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const columns = [
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import "fmt"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	AppName = "mayfly-go"
 | 
			
		||||
	Version = "v1.6.0"
 | 
			
		||||
	Version = "v1.6.1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func GetAppInfo() string {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user