refactor: PageTable组件重构

This commit is contained in:
meilin.huang
2023-12-12 23:31:53 +08:00
parent d86ef0a9ab
commit 8faf1831d9
26 changed files with 920 additions and 462 deletions

View File

@@ -15,7 +15,7 @@ const config = {
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`, baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
// 系统版本 // 系统版本
version: 'v1.6.0', version: 'v1.6.1',
}; };
export default config; export default config;

View 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>

View 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>

View File

@@ -0,0 +1,6 @@
export type BreakPoint = "xs" | "sm" | "md" | "lg" | "xl";
export type Responsive = {
span?: number;
offset?: number;
};

View File

@@ -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>

View 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;
}
}

View 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>

View File

@@ -1,170 +1,112 @@
<template> <template>
<div class="page-table"> <div>
<!-- <!-- 查询表单 -->
实现通过我们配置好的 查询条件 <SearchForm v-show="isShowSearch" :items="searchItems" v-model="queryForm_" :search="queryData" :reset="reset" :search-col="searchCol">
首先去创建form表单根据我们配置的查询条件去做一个循环判断展示出不用类型所对应不同的输入框 <!-- 遍历父组件传入的 solts 透传给子组件 -->
比如text对应普通的输入框select对应下拉选择dateTime对应日期时间选择器 <template v-for="(_, key) in useSlots()" v-slot:[key]>
在使用时父组件会传来一个queryForm空的对象 <slot :name="key"></slot>
循环出来的输入框会绑定表格配置中的prop字段绑定在queryForm对象中 </template>
--> </SearchForm>
<el-card> <el-card>
<div class="query" ref="queryRef"> <div class="table-main">
<div> <!-- 表格头部 操作按钮 -->
<div v-if="props.query.length > 0"> <div class="table-header">
<el-form :model="queryForm_" label-width="auto" :size="props.size"> <div class="header-button-lf">
<el-row <slot name="tableHeader" />
v-for="i in Math.ceil((props.query.length + 1) / (defaultQueryCount + 1))" </div>
:key="i"
v-show="i == 1 || isOpenMoreQuery" <div v-if="toolButton" class="header-button-ri">
:class="i > 1 && isOpenMoreQuery ? 'is-open' : ''" <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 <div v-for="(item, index) in tableColumns" :key="index">
:label="item.label" <el-checkbox v-model="item.show" :label="item.label" :true-label="true" :false-label="false" />
style="margin-right: 12px; margin-bottom: 0px" </div>
v-for="item in getRowQueryItem(i)" <template #reference>
:key="item.prop" <el-button icon="Operation" circle :size="props.size"></el-button>
> </template>
<!-- 这里只获取指定个数的筛选条件 --> </el-popover>
<el-input </slot>
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> </div>
</div> </div>
<div class="slot"> <el-table
<!-- 查询栏右侧slot插槽用来添加表格其他操作比如新增数据删除数据等其他操作 --> v-bind="$attrs"
<slot name="queryRight"></slot> :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" />
<!-- <template v-for="(item, index) in tableColumns">
动态表头显示根据表格每条配置项中的show字段来决定改列是否显示或者隐藏 <el-table-column
columns 就是我们表格配置的数组对象 :key="index"
--> v-if="item.show"
<el-popover :prop="item.prop"
placement="bottom" :label="item.label"
title="表格配置" :fixed="item.fixed"
popper-style="max-height: 550px; overflow: auto; max-width: 450px" :align="item.align"
width="auto" :show-overflow-tooltip="item.showOverflowTooltip"
trigger="click" :min-width="item.minWidth"
> :sortable="item.sortable || false"
<div v-for="(item, index) in props.columns" :key="index"> :type="item.type"
<el-checkbox v-model="item.show" :label="item.label" :true-label="true" :false-label="false" /> :width="item.width"
</div> >
<template #reference> <!-- 插槽预留功能 -->
<!-- 一个Element Plus中的图标 --> <template #default="scope" v-if="item.slot">
<el-button icon="Operation" :size="props.size"></el-button> <slot :name="item.prop" :data="scope.row"></slot>
</template> </template>
</el-popover>
</div> <!-- 枚举类型使用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> </div>
<el-table <el-row class="mt20" type="flex" justify="end">
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-pagination <el-pagination
:small="props.size == 'small'" :small="props.size == 'small'"
@current-change="handlePageChange" @current-change="handlePageChange"
@@ -182,79 +124,55 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { toRefs, watch, reactive, onMounted, Ref } from 'vue'; import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots } from 'vue';
import { TableColumn, TableQuery } from './index'; import { TableColumn } from './index';
import EnumTag from '@/components/enumtag/EnumTag.vue'; import EnumTag from '@/components/enumtag/EnumTag.vue';
import { useThemeConfig } from '@/store/themeConfig'; import { useThemeConfig } from '@/store/themeConfig';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useVModel } from '@vueuse/core'; import { useVModel, useEventListener } from '@vueuse/core';
import Api from '@/common/Api'; 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({ export interface PageTableProps {
size: { size?: string;
type: String, showSelection?: boolean;
default: '', showSearch?: boolean; // 是否显示搜索表单
}, columns: TableColumn[]; // 列配置项 ==> 必传
inputWidth: { data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
type: [Number, String], pageApi: Api; // 请求表格数据的 api
default: '200px', lazy?: boolean; // 是否自动执行请求 api ==> 非必传默认为false
}, beforeQueryFn?: (params: any) => any; // 执行查询时对查询参数进行处理,调整等
// 是否显示选择列 dataHandlerFn?: (data: any) => any; // 数据处理回调函数,用于将请求回来的数据二次加工处理等
showSelection: { searchItems?: SearchItem[];
type: Boolean, queryForm?: any; // 查询表单参数 ==> 非必传(默认为{pageNum:1, pageSize: 10}
default: false, border?: boolean; // 是否带有纵向边框 ==> 非必传默认为false
}, toolButton?: ('refresh' | 'setting' | 'search')[] | boolean; // 是否显示表格功能按钮 ==> 非必传默认为true
// 当前选择的数据 searchCol?: any; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
selectionData: { }
type: Array<any>,
}, // 接受父组件参数,配置默认值
// 列信息 const props = withDefaults(defineProps<PageTableProps>(), {
columns: { columns: () => [],
type: Array<TableColumn>, showSelection: false,
default: function () { lazy: false,
return []; initParam: {},
},
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 [];
},
},
// 绑定的查询表单
queryForm: { queryForm: {
type: Object, pageNum: 1,
default: function () { pageSize: 0,
return {
pageNum: 1,
pageSize: 10,
};
},
}, },
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 { themeConfig } = storeToRefs(useThemeConfig());
const state = reactive({ const state = reactive({
@@ -267,10 +185,18 @@ const state = reactive({
// 输入框宽度 // 输入框宽度
inputWidth_: '200px' as any, inputWidth_: '200px' as any,
formatVal: '', // 格式化后的值 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); const queryForm_: Ref<any> = useVModel(props, 'queryForm', emit);
@@ -287,9 +213,19 @@ watch(
} }
); );
onMounted(async () => { watch(
let pageSize = queryForm_.value.pageSize; () => isShowSearch.value,
() => {
console.log('watch show sa');
calcuTableHeight();
}
);
onMounted(async () => {
calcuTableHeight();
useEventListener(window, 'resize', calcuTableHeight);
let pageSize = queryForm_.value.pageSize;
// 如果pageSize设为0则使用系统全局配置的pageSize // 如果pageSize设为0则使用系统全局配置的pageSize
if (!pageSize) { if (!pageSize) {
pageSize = themeConfig.value.defaultListPageSize; pageSize = themeConfig.value.defaultListPageSize;
@@ -303,24 +239,14 @@ onMounted(async () => {
queryForm_.value.pageSize = pageSize; queryForm_.value.pageSize = pageSize;
state.pageSizes = [pageSize, pageSize * 2, pageSize * 3, pageSize * 4, pageSize * 5]; 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) { if (!props.lazy) {
await reqPageApi(); await reqPageApi();
} }
}); });
const calcuTableHeight = () => { const calcuTableHeight = () => {
state.tableMaxHeight = window.innerHeight - 240 + 'px'; const headerHeight = isShowSearch.value ? 320 : 240;
state.tableMaxHeight = window.innerHeight - headerHeight + 'px';
}; };
const formatText = (data: any) => { 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) => { const handleSelectionChange = (val: any) => {
emit('update:selectionData', val); emit('update:selectionData', val);
}; };
@@ -390,7 +304,7 @@ const queryData = () => {
const reset = () => { const reset = () => {
// 将查询参数绑定的值置空,并重新粗发查询接口 // 将查询参数绑定的值置空,并重新粗发查询接口
for (let qi of props.query) { for (let qi of props.searchItems) {
queryForm_.value[qi.prop] = null; queryForm_.value[qi.prop] = null;
} }
@@ -411,49 +325,88 @@ defineExpose({
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.page-table { .table-box,
.query { .table-main {
margin-bottom: 10px; display: flex;
overflow: hidden; flex: 1;
flex-direction: column;
width: 100%;
height: 100%;
.is-open { // 表格 header 样式
// padding: 10px 0; .table-header {
max-height: 200px; width: 100%;
margin-top: 10px; .header-button-lf {
float: left;
} }
display: flex; .header-button-ri {
align-items: flex-start; float: right;
justify-content: space-between; }
.slot { .el-button {
display: flex; margin-bottom: 10px;
justify-content: flex-end;
} }
} }
.page { // el-table 表格样式
margin-top: 10px; .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) { ::v-deep(.el-form-item__label) {
font-weight: bold; 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> </style>

View File

@@ -242,68 +242,3 @@ export class TableColumn {
this.minWidth = (flexWidth > 400 ? 400 : flexWidth) + this.addWidth; 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;
}
}

View File

@@ -138,7 +138,7 @@ export const useThemeConfig = defineStore('themeConfig', {
/** 全局设置 */ /** 全局设置 */
// 默认列表页的分页大小 // 默认列表页的分页大小
defaultListPageSize: 15, defaultListPageSize: 10,
}, },
}), }),
actions: { actions: {

View File

@@ -4,28 +4,20 @@
ref="pageTableRef" ref="pageTableRef"
:page-api="dbApi.dbs" :page-api="dbApi.dbs"
:before-query-fn="checkRouteTagPath" :before-query-fn="checkRouteTagPath"
:query="queryConfig" :search-items="searchItems"
v-model:query-form="query" v-model:query-form="query"
:show-selection="true" :show-selection="true"
v-model:selection-data="state.selectionData" v-model:selection-data="state.selectionData"
:columns="columns" :columns="columns"
> >
<template #tagPathSelect> <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-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
</el-select> </el-select>
</template> </template>
<template #instanceSelect> <template #instanceSelect>
<el-select <el-select remote :remote-method="getInstances" v-model="query.instanceId" placeholder="输入并选择实例" filterable clearable>
remote
:remote-method="getInstances"
v-model="query.instanceId"
placeholder="输入并选择实例"
filterable
clearable
style="width: 200px"
>
<el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item.id"> <el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item.id">
{{ item.name }} {{ item.name }}
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
@@ -37,7 +29,7 @@
</el-select> </el-select>
</template> </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.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> <el-button v-auth="perms.delDb" :disabled="selectionData.length < 1" @click="deleteDb()" type="danger" icon="delete">删除</el-button>
</template> </template>
@@ -170,7 +162,7 @@ import { isTrue } from '@/common/assert';
import { dateFormat } from '@/common/utils/date'; import { dateFormat } from '@/common/utils/date';
import ResourceTag from '../component/ResourceTag.vue'; import ResourceTag from '../component/ResourceTag.vue';
import PageTable from '@/components/pagetable/PageTable.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 { hasPerms } from '@/components/auth/auth';
import DbSqlExecLog from './DbSqlExecLog.vue'; import DbSqlExecLog from './DbSqlExecLog.vue';
import { DbType } from './dialect'; import { DbType } from './dialect';
@@ -178,6 +170,7 @@ import { tagApi } from '../tag/api';
import { TagResourceTypeEnum } from '@/common/commonEnum'; import { TagResourceTypeEnum } from '@/common/commonEnum';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { getDbDialect } from './dialect/index'; import { getDbDialect } from './dialect/index';
import { SearchItem } from '@/components/SearchForm';
const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue')); const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue'));
@@ -187,7 +180,7 @@ const perms = {
delDb: 'db:del', 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([ const columns = ref([
TableColumn.new('instanceName', '实例名'), TableColumn.new('instanceName', '实例名'),

View File

@@ -5,12 +5,12 @@
:page-api="dbApi.getSqlExecs" :page-api="dbApi.getSqlExecs"
:lazy="true" :lazy="true"
height="100%" height="100%"
:query="queryConfig" :search-items="searchItems"
v-model:query-form="query" v-model:query-form="query"
:columns="columns" :columns="columns"
> >
<template #dbSelect> <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-option v-for="item in dbs" :key="item" :label="`${item}`" :value="item"> </el-option>
</el-select> </el-select>
</template> </template>
@@ -40,7 +40,8 @@ import { toRefs, watch, reactive, onMounted, Ref, ref } from 'vue';
import { dbApi } from './api'; import { dbApi } from './api';
import { DbSqlExecTypeEnum } from './enums'; import { DbSqlExecTypeEnum } from './enums';
import PageTable from '@/components/pagetable/PageTable.vue'; 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({ const props = defineProps({
dbId: { dbId: {
@@ -53,13 +54,13 @@ const props = defineProps({
}, },
}); });
const queryConfig = [ const searchItems = [
TableQuery.slot('db', '数据库', 'dbSelect'), SearchItem.slot('db', '数据库', 'dbSelect'),
TableQuery.text('table', '表名'), SearchItem.text('table', '表名'),
TableQuery.select('type', '操作类型').setOptions(Object.values(DbSqlExecTypeEnum)), SearchItem.select('type', '操作类型').withEnum(DbSqlExecTypeEnum),
]; ];
const columns = [ const columns = ref([
TableColumn.new('db', '数据库'), TableColumn.new('db', '数据库'),
TableColumn.new('table', '表'), TableColumn.new('table', '表'),
TableColumn.new('type', '类型').typeTag(DbSqlExecTypeEnum).setAddWidth(10), TableColumn.new('type', '类型').typeTag(DbSqlExecTypeEnum).setAddWidth(10),
@@ -69,7 +70,7 @@ const columns = [
TableColumn.new('createTime', '执行时间').isTime(), TableColumn.new('createTime', '执行时间').isTime(),
TableColumn.new('remark', '备注'), TableColumn.new('remark', '备注'),
TableColumn.new('action', '操作').isSlot().setMinWidth(90).fixedRight().alignCenter(), TableColumn.new('action', '操作').isSlot().setMinWidth(90).fixedRight().alignCenter(),
]; ]);
const pageTableRef: Ref<any> = ref(null); const pageTableRef: Ref<any> = ref(null);

View File

@@ -3,13 +3,13 @@
<page-table <page-table
ref="pageTableRef" ref="pageTableRef"
:page-api="dbApi.instances" :page-api="dbApi.instances"
:query="queryConfig" :searchItems="searchItems"
v-model:query-form="query" v-model:query-form="query"
:show-selection="true" :show-selection="true"
v-model:selection-data="state.selectionData" v-model:selection-data="state.selectionData"
:columns="columns" :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.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 v-auth="perms.delInstance" :disabled="selectionData.length < 1" @click="deleteInstance()" type="danger" icon="delete"
>删除</el-button >删除</el-button
@@ -66,10 +66,11 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import { dbApi } from './api'; import { dbApi } from './api';
import { dateFormat } from '@/common/utils/date'; import { dateFormat } from '@/common/utils/date';
import PageTable from '@/components/pagetable/PageTable.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 { hasPerms } from '@/components/auth/auth';
import SvgIcon from '@/components/svgIcon/index.vue'; import SvgIcon from '@/components/svgIcon/index.vue';
import { getDbDialect } from './dialect'; import { getDbDialect } from './dialect';
import { SearchItem } from '@/components/SearchForm';
const InstanceEdit = defineAsyncComponent(() => import('./InstanceEdit.vue')); const InstanceEdit = defineAsyncComponent(() => import('./InstanceEdit.vue'));
@@ -78,7 +79,7 @@ const perms = {
delInstance: 'db:instance:del', delInstance: 'db:instance:del',
}; };
const queryConfig = [TableQuery.text('name', '名称')]; const searchItems = [SearchItem.text('name', '名称')];
const columns = ref([ const columns = ref([
TableColumn.new('name', '名称'), TableColumn.new('name', '名称'),

View File

@@ -4,19 +4,19 @@
ref="pageTableRef" ref="pageTableRef"
:page-api="machineApi.list" :page-api="machineApi.list"
:before-query-fn="checkRouteTagPath" :before-query-fn="checkRouteTagPath"
:query="queryConfig" :search-items="searchItems"
v-model:query-form="params" v-model:query-form="params"
:show-selection="true" :show-selection="true"
v-model:selection-data="state.selectionData" v-model:selection-data="state.selectionData"
:columns="columns" :columns="columns"
> >
<template #tagPathSelect> <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-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
</el-select> </el-select>
</template> </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.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> <el-button v-auth="perms.delMachine" :disabled="selectionData.length < 1" @click="deleteMachine()" type="danger" icon="delete">删除</el-button>
</template> </template>
@@ -190,11 +190,12 @@ import { machineApi, getMachineTerminalSocketUrl } from './api';
import { dateFormat } from '@/common/utils/date'; import { dateFormat } from '@/common/utils/date';
import ResourceTag from '../component/ResourceTag.vue'; import ResourceTag from '../component/ResourceTag.vue';
import PageTable from '@/components/pagetable/PageTable.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 { hasPerms } from '@/components/auth/auth';
import { formatByteSize } from '@/common/utils/format'; import { formatByteSize } from '@/common/utils/format';
import { tagApi } from '../tag/api'; import { tagApi } from '../tag/api';
import { TagResourceTypeEnum } from '@/common/commonEnum'; import { TagResourceTypeEnum } from '@/common/commonEnum';
import { SearchItem } from '@/components/SearchForm';
// 组件 // 组件
const TerminalDialog = defineAsyncComponent(() => import('@/components/terminal/TerminalDialog.vue')); const TerminalDialog = defineAsyncComponent(() => import('@/components/terminal/TerminalDialog.vue'));
@@ -218,9 +219,9 @@ const perms = {
closeCli: 'machine:close-cli', 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('name', '名称'),
TableColumn.new('ipPort', 'ip:port').isSlot().setAddWidth(50), TableColumn.new('ipPort', 'ip:port').isSlot().setAddWidth(50),
TableColumn.new('stat', '运行状态').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('tagPath', '关联标签').isSlot().setAddWidth(10).alignCenter(),
TableColumn.new('remark', '备注'), TableColumn.new('remark', '备注'),
TableColumn.new('action', '操作').isSlot().setMinWidth(238).fixedRight().alignCenter(), TableColumn.new('action', '操作').isSlot().setMinWidth(238).fixedRight().alignCenter(),
]); ];
// 该用户拥有的的操作列按钮权限使用v-if进行判断v-auth对el-dropdown-item无效 // 该用户拥有的的操作列按钮权限使用v-if进行判断v-auth对el-dropdown-item无效
const actionBtns = hasPerms([perms.updateMachine, perms.closeCli]); const actionBtns = hasPerms([perms.updateMachine, perms.closeCli]);

View File

@@ -14,13 +14,13 @@
:page-api="machineApi.scripts" :page-api="machineApi.scripts"
:before-query-fn="checkScriptType" :before-query-fn="checkScriptType"
:lazy="true" :lazy="true"
:query="queryConfig" :search-items="state.searchItems"
v-model:query-form="query" v-model:query-form="query"
:columns="columns" :columns="columns"
:show-selection="true" :show-selection="true"
v-model:selection-data="selectionData" 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:save'" type="primary" @click="editScript(null)" icon="plus" plain>添加</el-button>
<el-button <el-button
v-auth="'machine:script:del'" v-auth="'machine:script:del'"
@@ -93,8 +93,9 @@ import { getMachineTerminalSocketUrl, machineApi } from './api';
import { ScriptResultEnum, ScriptTypeEnum } from './enums'; import { ScriptResultEnum, ScriptTypeEnum } from './enums';
import ScriptEdit from './ScriptEdit.vue'; import ScriptEdit from './ScriptEdit.vue';
import PageTable from '@/components/pagetable/PageTable.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 { DynamicFormDialog } from '@/components/dynamic-form';
import { SearchItem } from '@/components/SearchForm';
const props = defineProps({ const props = defineProps({
visible: { type: Boolean }, visible: { type: Boolean },
@@ -110,7 +111,7 @@ const pageTableRef: Ref<any> = ref(null);
const state = reactive({ const state = reactive({
dialogVisible: false, dialogVisible: false,
selectionData: [], selectionData: [],
queryConfig: [TableQuery.select('type', '类型').setOptions(Object.values(ScriptTypeEnum))], searchItems: [SearchItem.select('type', '类型').withEnum(ScriptTypeEnum)],
columns: [ columns: [
TableColumn.new('name', '名称'), TableColumn.new('name', '名称'),
TableColumn.new('description', '描述'), TableColumn.new('description', '描述'),
@@ -129,8 +130,6 @@ const state = reactive({
title: '', title: '',
machineId: 9999999, machineId: 9999999,
}, },
total: 0,
scriptTable: [],
scriptParamsDialog: { scriptParamsDialog: {
script: null, script: null,
visible: false, 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) => { watch(props, async (newValue) => {
state.dialogVisible = newValue.visible; state.dialogVisible = newValue.visible;
@@ -281,7 +280,6 @@ const handleClose = () => {
emit('update:machineId', null); emit('update:machineId', null);
emit('cancel'); emit('cancel');
state.query.type = ScriptTypeEnum.Private.value; state.query.type = ScriptTypeEnum.Private.value;
state.scriptTable = [];
state.scriptParamsDialog.paramsFormItem = []; state.scriptParamsDialog.paramsFormItem = [];
}; };
</script> </script>

View File

@@ -3,13 +3,13 @@
<page-table <page-table
ref="pageTableRef" ref="pageTableRef"
:page-api="authCertApi.list" :page-api="authCertApi.list"
:query="state.queryConfig" :search-items="state.searchItems"
v-model:query-form="query" v-model:query-form="query"
:show-selection="true" :show-selection="true"
v-model:selection-data="selectionData" v-model:selection-data="selectionData"
:columns="state.columns" :columns="state.columns"
> >
<template #queryRight> <template #tableHeader>
<el-button type="primary" icon="plus" @click="edit(false)">添加</el-button> <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> <el-button :disabled="selectionData.length < 1" @click="deleteAc(selectionData)" type="danger" icon="delete">删除 </el-button>
</template> </template>
@@ -29,8 +29,9 @@ import AuthCertEdit from './AuthCertEdit.vue';
import { authCertApi } from '../api'; import { authCertApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import PageTable from '@/components/pagetable/PageTable.vue'; import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn, TableQuery } from '@/components/pagetable'; import { TableColumn } from '@/components/pagetable';
import { AuthMethodEnum } from '../enums'; import { AuthMethodEnum } from '../enums';
import { SearchItem } from '@/components/SearchForm';
const pageTableRef: Ref<any> = ref(null); const pageTableRef: Ref<any> = ref(null);
const state = reactive({ const state = reactive({
@@ -39,7 +40,7 @@ const state = reactive({
pageSize: 0, pageSize: 0,
name: null, name: null,
}, },
queryConfig: [TableQuery.text('name', '凭证名称')], searchItems: [SearchItem.text('name', '凭证名称')],
columns: [ columns: [
TableColumn.new('name', '名称'), TableColumn.new('name', '名称'),
TableColumn.new('authMethod', '认证方式').typeTag(AuthMethodEnum), TableColumn.new('authMethod', '认证方式').typeTag(AuthMethodEnum),
@@ -50,8 +51,6 @@ const state = reactive({
TableColumn.new('createTime', '修改时间').isTime(), TableColumn.new('createTime', '修改时间').isTime(),
TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(65).alignCenter(), TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(65).alignCenter(),
], ],
total: 0,
authcerts: [],
selectionData: [], selectionData: [],
paramsDialog: { paramsDialog: {
visible: false, visible: false,

View File

@@ -15,13 +15,13 @@
:page-api="cronJobApi.execList" :page-api="cronJobApi.execList"
:lazy="true" :lazy="true"
:data-handler-fn="parseData" :data-handler-fn="parseData"
:query="queryConfig" :search-items="searchItems"
v-model:query-form="params" v-model:query-form="params"
:data="state.data.list" :data="state.data.list"
:columns="columns" :columns="columns"
> >
<template #machineSelect> <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"> <el-option v-for="ac in machineMap.values()" :key="ac.id" :value="ac.id" :label="ac.ip">
{{ ac.ip }} {{ ac.ip }}
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
@@ -38,8 +38,9 @@
import { watch, ref, toRefs, reactive, Ref } from 'vue'; import { watch, ref, toRefs, reactive, Ref } from 'vue';
import { cronJobApi, machineApi } from '../api'; import { cronJobApi, machineApi } from '../api';
import PageTable from '@/components/pagetable/PageTable.vue'; import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn, TableQuery } from '@/components/pagetable'; import { TableColumn } from '@/components/pagetable';
import { CronJobExecStatusEnum } from '../enums'; import { CronJobExecStatusEnum } from '../enums';
import { SearchItem } from '@/components/SearchForm';
const props = defineProps({ const props = defineProps({
visible: { visible: {
@@ -55,9 +56,9 @@ const props = defineProps({
const emit = defineEmits(['update:visible', 'update:data', 'cancel']); const emit = defineEmits(['update:visible', 'update:data', 'cancel']);
const queryConfig = [ const searchItems = [
TableQuery.slot('machineId', '机器', 'machineSelect'), SearchItem.slot('machineId', '机器', 'machineSelect'),
TableQuery.select('status', '状态').setOptions(Object.values(CronJobExecStatusEnum)), SearchItem.select('status', '状态').setOptions(Object.values(CronJobExecStatusEnum)),
]; ];
const columns = ref([ const columns = ref([

View File

@@ -3,13 +3,13 @@
<page-table <page-table
ref="pageTableRef" ref="pageTableRef"
:page-api="cronJobApi.list" :page-api="cronJobApi.list"
:query="queryConfig" :query="searchItems"
v-model:query-form="params" v-model:query-form="params"
:show-selection="true" :show-selection="true"
v-model:selection-data="state.selectionData" v-model:selection-data="state.selectionData"
:columns="columns" :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.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> <el-button v-auth="perms.delCronJob" :disabled="selectionData.length < 1" @click="deleteCronJob()" type="danger" icon="delete">删除</el-button>
</template> </template>
@@ -38,8 +38,9 @@ import { ref, toRefs, reactive, onMounted, defineAsyncComponent, Ref } from 'vue
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { cronJobApi } from '../api'; import { cronJobApi } from '../api';
import PageTable from '@/components/pagetable/PageTable.vue'; import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn, TableQuery } from '@/components/pagetable'; import { TableColumn } from '@/components/pagetable';
import { CronJobStatusEnum, CronJobSaveExecResTypeEnum } from '../enums'; import { CronJobStatusEnum, CronJobSaveExecResTypeEnum } from '../enums';
import { SearchItem } from '@/components/SearchForm';
const CronJobEdit = defineAsyncComponent(() => import('./CronJobEdit.vue')); const CronJobEdit = defineAsyncComponent(() => import('./CronJobEdit.vue'));
const CronJobExecList = defineAsyncComponent(() => import('./CronJobExecList.vue')); const CronJobExecList = defineAsyncComponent(() => import('./CronJobExecList.vue'));
@@ -49,7 +50,7 @@ const perms = {
delCronJob: 'machine:cronjob:del', 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([ const columns = ref([
TableColumn.new('key', 'key'), TableColumn.new('key', 'key'),

View File

@@ -4,19 +4,19 @@
ref="pageTableRef" ref="pageTableRef"
:page-api="mongoApi.mongoList" :page-api="mongoApi.mongoList"
:before-query-fn="checkRouteTagPath" :before-query-fn="checkRouteTagPath"
:query="queryConfig" :search-items="searchItems"
v-model:query-form="query" v-model:query-form="query"
:show-selection="true" :show-selection="true"
v-model:selection-data="selectionData" v-model:selection-data="selectionData"
:columns="columns" :columns="columns"
> >
<template #tagPathSelect> <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-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
</el-select> </el-select>
</template> </template>
<template #queryRight> <template #tableHeader>
<el-button type="primary" icon="plus" @click="editMongo(true)" plain>添加</el-button> <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> <el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteMongo" plain>删除 </el-button>
</template> </template>
@@ -53,10 +53,11 @@ import { defineAsyncComponent, ref, toRefs, reactive, onMounted, Ref } from 'vue
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import ResourceTag from '../component/ResourceTag.vue'; import ResourceTag from '../component/ResourceTag.vue';
import PageTable from '@/components/pagetable/PageTable.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 { TagResourceTypeEnum } from '@/common/commonEnum';
import { tagApi } from '../tag/api'; import { tagApi } from '../tag/api';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { SearchItem } from '@/components/SearchForm';
const MongoEdit = defineAsyncComponent(() => import('./MongoEdit.vue')); const MongoEdit = defineAsyncComponent(() => import('./MongoEdit.vue'));
const MongoDbs = defineAsyncComponent(() => import('./MongoDbs.vue')); const MongoDbs = defineAsyncComponent(() => import('./MongoDbs.vue'));
@@ -65,15 +66,15 @@ const MongoRunCommand = defineAsyncComponent(() => import('./MongoRunCommand.vue
const route = useRoute(); const route = useRoute();
const pageTableRef: Ref<any> = ref(null); const pageTableRef: Ref<any> = ref(null);
const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect')]; const searchItems = [SearchItem.slot('tagPath', '标签', 'tagPathSelect')];
const columns = ref([ const columns = [
TableColumn.new('name', '名称'), TableColumn.new('name', '名称'),
TableColumn.new('uri', '连接uri'), TableColumn.new('uri', '连接uri'),
TableColumn.new('tagPath', '关联标签').isSlot().setAddWidth(20).alignCenter(), TableColumn.new('tagPath', '关联标签').isSlot().setAddWidth(20).alignCenter(),
TableColumn.new('createTime', '创建时间').isTime(), TableColumn.new('createTime', '创建时间').isTime(),
TableColumn.new('creator', '创建人'), TableColumn.new('creator', '创建人'),
TableColumn.new('action', '操作').isSlot().setMinWidth(170).fixedRight().alignCenter(), TableColumn.new('action', '操作').isSlot().setMinWidth(170).fixedRight().alignCenter(),
]); ];
const state = reactive({ const state = reactive({
tags: [], tags: [],

View File

@@ -4,19 +4,19 @@
ref="pageTableRef" ref="pageTableRef"
:page-api="redisApi.redisList" :page-api="redisApi.redisList"
:before-query-fn="checkRouteTagPath" :before-query-fn="checkRouteTagPath"
:query="queryConfig" :searchItems="searchItems"
v-model:query-form="query" v-model:query-form="query"
:show-selection="true" :show-selection="true"
v-model:selection-data="selectionData" v-model:selection-data="selectionData"
:columns="columns" :columns="columns"
> >
<template #tagPathSelect> <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-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
</el-select> </el-select>
</template> </template>
<template #queryRight> <template #tableHeader>
<el-button type="primary" icon="plus" @click="editRedis(false)" plain>添加</el-button> <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> <el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteRedis" plain>删除 </el-button>
</template> </template>
@@ -163,15 +163,16 @@ import RedisEdit from './RedisEdit.vue';
import { dateFormat } from '@/common/utils/date'; import { dateFormat } from '@/common/utils/date';
import ResourceTag from '../component/ResourceTag.vue'; import ResourceTag from '../component/ResourceTag.vue';
import PageTable from '@/components/pagetable/PageTable.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 { tagApi } from '../tag/api';
import { TagResourceTypeEnum } from '@/common/commonEnum'; import { TagResourceTypeEnum } from '@/common/commonEnum';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { SearchItem } from '@/components/SearchForm';
const route = useRoute(); const route = useRoute();
const pageTableRef: Ref<any> = ref(null); const pageTableRef: Ref<any> = ref(null);
const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect')]; const searchItems = [SearchItem.slot('tagPath', '标签', 'tagPathSelect')];
const columns = ref([ const columns = ref([
TableColumn.new('name', '名称'), TableColumn.new('name', '名称'),
TableColumn.new('host', 'host:port'), TableColumn.new('host', 'host:port'),

View File

@@ -3,13 +3,13 @@
<page-table <page-table
ref="pageTableRef" ref="pageTableRef"
:page-api="tagApi.getTeams" :page-api="tagApi.getTeams"
:query="state.queryConfig" :search-items="searchItems"
v-model:query-form="query" v-model:query-form="query"
:show-selection="true" :show-selection="true"
v-model:selection-data="selectionData" 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: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> <el-button v-auth="'team:del'" :disabled="selectionData.length < 1" @click="deleteTeam()" type="danger" icon="delete">删除</el-button>
</template> </template>
@@ -91,11 +91,11 @@
ref="showMemPageTableRef" ref="showMemPageTableRef"
:page-api="tagApi.getTeamMem" :page-api="tagApi.getTeamMem"
:lazy="true" :lazy="true"
:query="showMemDialog.queryConfig" :search-items="showMemDialog.searchItems"
v-model:query-form="showMemDialog.query" v-model:query-form="showMemDialog.query"
:columns="showMemDialog.columns" :columns="showMemDialog.columns"
> >
<template #queryRight> <template #tableHeader>
<el-button v-auth="'team:member:save'" @click="showAddMemberDialog()" type="primary" icon="plus">添加</el-button> <el-button v-auth="'team:member:save'" @click="showAddMemberDialog()" type="primary" icon="plus">添加</el-button>
</template> </template>
@@ -139,13 +139,23 @@ import { accountApi } from '../../system/api';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { notBlank } from '@/common/assert'; import { notBlank } from '@/common/assert';
import PageTable from '@/components/pagetable/PageTable.vue'; 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 teamForm: any = ref(null);
const tagTreeRef: any = ref(null); const tagTreeRef: any = ref(null);
const pageTableRef: Ref<any> = ref(null); const pageTableRef: Ref<any> = ref(null);
const showMemPageTableRef: 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({ const state = reactive({
currentEditPermissions: false, currentEditPermissions: false,
addTeamDialog: { addTeamDialog: {
@@ -158,17 +168,9 @@ const state = reactive({
pageSize: 0, pageSize: 0,
name: null, 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: [], selectionData: [],
showMemDialog: { showMemDialog: {
queryConfig: [TableQuery.text('username', '用户名')], searchItems: [SearchItem.text('username', '用户名').withSpan(2)],
columns: [ columns: [
TableColumn.new('name', '姓名'), TableColumn.new('name', '姓名'),
TableColumn.new('username', '账号'), TableColumn.new('username', '账号'),

View File

@@ -3,13 +3,13 @@
<page-table <page-table
ref="pageTableRef" ref="pageTableRef"
:page-api="accountApi.list" :page-api="accountApi.list"
:query="queryConfig" :search-items="searchItems"
v-model:query-form="query" v-model:query-form="query"
:show-selection="true" :show-selection="true"
v-model:selection-data="selectionData" v-model:selection-data="selectionData"
:columns="columns" :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.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 v-auth="perms.delAccount" :disabled="state.selectionData.length < 1" @click="deleteAccount()" type="danger" icon="delete"
>删除</el-button >删除</el-button
@@ -85,8 +85,9 @@ import { accountApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { dateFormat } from '@/common/utils/date'; import { dateFormat } from '@/common/utils/date';
import PageTable from '@/components/pagetable/PageTable.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 { hasPerms } from '@/components/auth/auth';
import { SearchItem } from '@/components/SearchForm';
const perms = { const perms = {
addAccount: 'account:add', addAccount: 'account:add',
@@ -95,8 +96,8 @@ const perms = {
changeAccountStatus: 'account:changeStatus', changeAccountStatus: 'account:changeStatus',
}; };
const queryConfig = [TableQuery.text('username', '用户名')]; const searchItems = [SearchItem.text('username', '用户名')];
const columns = ref([ const columns = [
TableColumn.new('name', '姓名'), TableColumn.new('name', '姓名'),
TableColumn.new('username', '用户名'), TableColumn.new('username', '用户名'),
TableColumn.new('status', '状态').typeTag(AccountStatusEnum), TableColumn.new('status', '状态').typeTag(AccountStatusEnum),
@@ -106,7 +107,7 @@ const columns = ref([
TableColumn.new('createTime', '创建时间').isTime(), TableColumn.new('createTime', '创建时间').isTime(),
TableColumn.new('modifier', '更新账号'), TableColumn.new('modifier', '更新账号'),
TableColumn.new('updateTime', '更新时间').isTime(), TableColumn.new('updateTime', '更新时间').isTime(),
]); ];
// 该用户拥有的的操作列按钮权限 // 该用户拥有的的操作列按钮权限
const actionBtns = hasPerms([perms.addAccount, perms.saveAccountRole, perms.changeAccountStatus]); const actionBtns = hasPerms([perms.addAccount, perms.saveAccountRole, perms.changeAccountStatus]);
@@ -155,7 +156,7 @@ const { selectionData, query, showRoleDialog, showResourceDialog, roleDialog, ac
onMounted(() => { onMounted(() => {
if (Object.keys(actionBtns).length > 0) { if (Object.keys(actionBtns).length > 0) {
columns.value.push(actionColumn); columns.push(actionColumn);
} }
}); });

View File

@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<page-table ref="pageTableRef" :page-api="configApi.list" v-model:selection-data="selectionData" :columns="columns"> <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> <el-button v-auth="perms.saveConfig" type="primary" icon="plus" @click="editConfig(false)">添加</el-button>
</template> </template>

View File

@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<page-table <page-table
:query="queryConfig" :search-items="searchItems"
v-model:query-form="query" v-model:query-form="query"
:show-selection="true" :show-selection="true"
v-model:selection-data="selectionData" v-model:selection-data="selectionData"
@@ -9,7 +9,7 @@
:page-api="roleApi.list" :page-api="roleApi.list"
ref="pageTableRef" 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.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 v-auth="perms.delRole" :disabled="selectionData.length < 1" @click="deleteRole(selectionData)" type="danger" icon="delete"
>删除</el-button >删除</el-button
@@ -43,9 +43,10 @@ import ShowResource from './ShowResource.vue';
import { roleApi, resourceApi } from '../api'; import { roleApi, resourceApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import PageTable from '@/components/pagetable/PageTable.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 { hasPerms } from '@/components/auth/auth';
import { RoleStatusEnum } from '../enums'; import { RoleStatusEnum } from '../enums';
import { SearchItem } from '@/components/SearchForm';
const perms = { const perms = {
addRole: 'role:add', addRole: 'role:add',
@@ -54,7 +55,7 @@ const perms = {
saveRoleResource: 'role:saveResources', saveRoleResource: 'role:saveResources',
}; };
const queryConfig = [TableQuery.text('name', '角色名')]; const searchItems = [SearchItem.text('name', '角色名')];
const columns = ref([ const columns = ref([
TableColumn.new('name', '角色名称'), TableColumn.new('name', '角色名称'),
TableColumn.new('code', '角色code'), TableColumn.new('code', '角色code'),

View File

@@ -1,16 +1,8 @@
<template> <template>
<div> <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> <template #selectAccount>
<el-select <el-select remote :remote-method="getAccount" v-model="query.creatorId" filterable placeholder="请输入并选择账号" clearable>
style="width: 200px"
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-option v-for="item in accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
</el-select> </el-select>
</template> </template>
@@ -22,13 +14,14 @@
import { toRefs, reactive } from 'vue'; import { toRefs, reactive } from 'vue';
import { logApi, accountApi } from '../api'; import { logApi, accountApi } from '../api';
import PageTable from '@/components/pagetable/PageTable.vue'; import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn, TableQuery } from '@/components/pagetable'; import { TableColumn } from '@/components/pagetable';
import { LogTypeEnum } from '../enums'; import { LogTypeEnum } from '../enums';
import { SearchItem } from '@/components/SearchForm';
const queryConfig = [ const searchItems = [
TableQuery.slot('creatorId', '操作人', 'selectAccount'), SearchItem.slot('creatorId', '操作人', 'selectAccount'),
TableQuery.select('type', '操作结果').setOptions(Object.values(LogTypeEnum)), SearchItem.select('type', '操作结果').withEnum(LogTypeEnum),
TableQuery.text('description', '描述'), SearchItem.text('description', '描述'),
]; ];
const columns = [ const columns = [

View File

@@ -4,7 +4,7 @@ import "fmt"
const ( const (
AppName = "mayfly-go" AppName = "mayfly-go"
Version = "v1.6.0" Version = "v1.6.1"
) )
func GetAppInfo() string { func GetAppInfo() string {