feat: 添加表格插件系统
自动部署 / build (push) Failing after 58s
Details
自动部署 / build (push) Failing after 58s
Details
parent
1f7c1a95b3
commit
2b5c367117
|
|
@ -76,9 +76,10 @@ export const AnForm = defineComponent({
|
||||||
{this.items.map(item => (
|
{this.items.map(item => (
|
||||||
<AnFormItem key={item.field} item={item} items={this.items} model={this.model}></AnFormItem>
|
<AnFormItem key={item.field} item={item} items={this.items} model={this.model}></AnFormItem>
|
||||||
))}
|
))}
|
||||||
{this.submit && this.submitItem && (
|
{this.$slots.submit?.(this.model, this.validate) ||
|
||||||
<AnFormItem item={this.submitItem} items={this.items} model={this.model}></AnFormItem>
|
(this.submit && this.submitItem && (
|
||||||
)}
|
<AnFormItem item={this.submitItem} items={this.items} model={this.model}></AnFormItem>
|
||||||
|
))}
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,29 @@ export type FormUseOptions = Partial<Omit<IAnForm, 'items'>> & {
|
||||||
* 表单项
|
* 表单项
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* [
|
* [{
|
||||||
* {
|
|
||||||
* field: 'name',
|
* field: 'name',
|
||||||
* label: '昵称',
|
* label: '昵称',
|
||||||
* setter: 'input'
|
* setter: 'input'
|
||||||
* }
|
* }]
|
||||||
* ]
|
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
items?: FormItem[];
|
items?: FormItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function useFormProps(options: FormUseOptions) {
|
||||||
|
const _model = options.model ?? {};
|
||||||
|
const _items = options.items ?? [];
|
||||||
|
const items = useItems(_items, _model);
|
||||||
|
const props = reactive({
|
||||||
|
formProps: options.formProps ?? {},
|
||||||
|
items: items.value,
|
||||||
|
submit: options.submit,
|
||||||
|
model: _model,
|
||||||
|
});
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建表单组件的参数
|
* 构建表单组件的参数
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { IAnForm } from '@/components/AnForm';
|
import { AnForm, AnFormInstance, IAnForm } from '@/components/AnForm';
|
||||||
import AniEmpty from '@/components/empty/AniEmpty.vue';
|
import AniEmpty from '@/components/empty/AniEmpty.vue';
|
||||||
import { FormModalProps } from '@/components/form';
|
import { FormModalProps } from '@/components/form';
|
||||||
import {
|
import {
|
||||||
|
|
@ -8,9 +8,9 @@ import {
|
||||||
Button,
|
Button,
|
||||||
PaginationProps,
|
PaginationProps,
|
||||||
} from '@arco-design/web-vue';
|
} from '@arco-design/web-vue';
|
||||||
import { merge } from 'lodash-es';
|
import { isArray, isFunction, merge } from 'lodash-es';
|
||||||
import { PropType, defineComponent, ref } from 'vue';
|
import { PropType, defineComponent, ref } from 'vue';
|
||||||
import { TableColumnConfig } from './TableColumnConfig';
|
import { PluginContainer } from '../hooks/useTablePlugin';
|
||||||
|
|
||||||
type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise<any>;
|
type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise<any>;
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ export const AnTable = defineComponent({
|
||||||
/**
|
/**
|
||||||
* 分页配置
|
* 分页配置
|
||||||
*/
|
*/
|
||||||
pagination: {
|
paging: {
|
||||||
type: Object as PropType<PaginationProps & { hide?: boolean }>,
|
type: Object as PropType<PaginationProps & { hide?: boolean }>,
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|
@ -64,24 +64,31 @@ export const AnTable = defineComponent({
|
||||||
tableProps: {
|
tableProps: {
|
||||||
type: Object as PropType<InstanceType<typeof BaseTable>['$props']>,
|
type: Object as PropType<InstanceType<typeof BaseTable>['$props']>,
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 插件列表
|
||||||
|
*/
|
||||||
|
pluginer: {
|
||||||
|
type: Object as PropType<PluginContainer>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const tableRef = ref<InstanceType<typeof BaseTable>>();
|
const tableRef = ref<InstanceType<typeof BaseTable>>();
|
||||||
const renderData = ref<BaseData[]>([]);
|
const renderData = ref<BaseData[]>([]);
|
||||||
const reloadData = () => loadData();
|
const searchRef = ref<AnFormInstance | null>(null);
|
||||||
|
|
||||||
const useTablePaging = () => {
|
const useTablePaging = () => {
|
||||||
const getPaging = () => {
|
const getPaging = () => {
|
||||||
return {
|
return {
|
||||||
page: props.pagination?.current ?? 1,
|
page: props.paging?.current ?? 1,
|
||||||
size: props.pagination?.pageSize ?? 10,
|
size: props.paging?.pageSize ?? 10,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPaging = (paging: PaginationProps) => {
|
const setPaging = (paging: PaginationProps) => {
|
||||||
if (props.pagination) {
|
if (props.paging) {
|
||||||
merge(props.pagination, paging);
|
merge(props.paging, paging);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -98,16 +105,24 @@ export const AnTable = defineComponent({
|
||||||
|
|
||||||
const { getPaging, setPaging, resetPaging } = useTablePaging();
|
const { getPaging, setPaging, resetPaging } = useTablePaging();
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载数据
|
|
||||||
* @param pagination 自定义分页
|
|
||||||
*/
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
|
if (await searchRef.value?.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const paging = getPaging();
|
const paging = getPaging();
|
||||||
if (typeof props.data === 'function') {
|
const search = searchRef.value?.getModel() ?? {};
|
||||||
|
|
||||||
|
if (isArray(props.data)) {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFunction(props.data)) {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const resData = await props.data({ ...paging });
|
let params = { ...search, ...paging };
|
||||||
|
params = props.pluginer?.callBeforeSearchHook(params) ?? params;
|
||||||
|
const resData = await props.data(params);
|
||||||
const { data = [], total = 0 } = resData?.data || {};
|
const { data = [], total = 0 } = resData?.data || {};
|
||||||
renderData.value = data;
|
renderData.value = data;
|
||||||
setPaging({ total });
|
setPaging({ total });
|
||||||
|
|
@ -119,6 +134,15 @@ export const AnTable = defineComponent({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
setPaging({ current: 1, pageSize: 10 });
|
||||||
|
return loadData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const refresh = () => {
|
||||||
|
return loadData();
|
||||||
|
};
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (Array.isArray(props.data)) {
|
if (Array.isArray(props.data)) {
|
||||||
renderData.value = props.data;
|
renderData.value = props.data;
|
||||||
|
|
@ -131,11 +155,13 @@ export const AnTable = defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
const onPageChange = (page: number) => {
|
||||||
|
props.pluginer?.callPageChangeHook(page);
|
||||||
setPaging({ current: page });
|
setPaging({ current: page });
|
||||||
loadData();
|
loadData();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPageSizeChange = (size: number) => {
|
const onPageSizeChange = (size: number) => {
|
||||||
|
props.pluginer?.callSizeChangeHook(size);
|
||||||
setPaging({ current: 1, pageSize: size });
|
setPaging({ current: 1, pageSize: size });
|
||||||
loadData();
|
loadData();
|
||||||
};
|
};
|
||||||
|
|
@ -143,13 +169,18 @@ export const AnTable = defineComponent({
|
||||||
const state = {
|
const state = {
|
||||||
loading,
|
loading,
|
||||||
tableRef,
|
tableRef,
|
||||||
|
searchRef,
|
||||||
renderData,
|
renderData,
|
||||||
loadData,
|
loadData,
|
||||||
reloadData,
|
reload,
|
||||||
|
refresh,
|
||||||
onPageChange,
|
onPageChange,
|
||||||
onPageSizeChange,
|
onPageSizeChange,
|
||||||
|
props,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
props.pluginer?.callSetupHook(state);
|
||||||
|
|
||||||
provide('ref:table', { ...state, ...props });
|
provide('ref:table', { ...state, ...props });
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
|
@ -159,16 +190,36 @@ export const AnTable = defineComponent({
|
||||||
return (
|
return (
|
||||||
<div class="table w-full">
|
<div class="table w-full">
|
||||||
<div class={`mb-3 flex toolbar justify-between`}>
|
<div class={`mb-3 flex toolbar justify-between`}>
|
||||||
<div class={`flex-1 flex gap-2 `}>
|
{this.pluginer?.actions && (
|
||||||
<Button type='primary'>{{ icon: () => <i class="icon-park-outline-add"></i>, default: () => '新增' }}</Button>
|
<div class={`flex-1 flex gap-2 items-center`}>
|
||||||
</div>
|
{this.pluginer.actions.map(Action => (
|
||||||
<div>
|
<Action />
|
||||||
<div class="flex gap-1">
|
))}
|
||||||
<Button loading={this.loading} onClick={this.loadData}>
|
|
||||||
{{ icon: () => <span class="icon-park-outline-redo"></span> }}
|
|
||||||
</Button>
|
|
||||||
<TableColumnConfig columns={this.columns} />
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
{this.search && (
|
||||||
|
<AnForm
|
||||||
|
ref="searchRef"
|
||||||
|
v-model:model={this.search.model}
|
||||||
|
items={this.search.items}
|
||||||
|
formProps={this.search.formProps}
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
submit: () => (
|
||||||
|
<Button type="primary" loading={this.loading} onClick={this.reload}>
|
||||||
|
{{
|
||||||
|
default: () => '查询',
|
||||||
|
icon: () => <i class="icon-park-outline-search"></i>,
|
||||||
|
}}
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
</AnForm>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 ml-2">
|
||||||
|
<div class="flex gap-1">{this.pluginer?.widgets && this.pluginer.widgets?.map(Widget => <Widget />)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -179,7 +230,7 @@ export const AnTable = defineComponent({
|
||||||
{...this.tableProps}
|
{...this.tableProps}
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
loading={this.loading}
|
loading={this.loading}
|
||||||
pagination={this.pagination?.hide ? false : this.pagination}
|
pagination={this.paging?.hide ? false : this.paging}
|
||||||
data={this.renderData}
|
data={this.renderData}
|
||||||
columns={this.columns}
|
columns={this.columns}
|
||||||
onPageChange={this.onPageChange}
|
onPageChange={this.onPageChange}
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
import { merge } from "lodash-es";
|
|
||||||
import { FormUseOptions } from "../../AnForm";
|
|
||||||
import { IAnFormItem } from "../../AnForm/components/FormItem";
|
|
||||||
import { FormItem } from "../../AnForm/hooks/useItems";
|
|
||||||
|
|
||||||
export type ExtendFormItem = Partial<
|
|
||||||
FormItem & {
|
|
||||||
/**
|
|
||||||
* 从新建弹窗继承表单项
|
|
||||||
* @example 'name'
|
|
||||||
*/
|
|
||||||
extend: string;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
|
|
||||||
type SearchFormItem = ExtendFormItem & {
|
|
||||||
/**
|
|
||||||
* 是否点击图标后进行搜索
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
searchable?: boolean;
|
|
||||||
/**
|
|
||||||
* 是否回车后进行查询
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
enterable?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SearchForm = Omit<FormUseOptions, "items"> & {
|
|
||||||
/**
|
|
||||||
* 搜索表单项
|
|
||||||
*/
|
|
||||||
items?: SearchFormItem[];
|
|
||||||
/**
|
|
||||||
* 是否隐藏查询按钮
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
hideSearch?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useSearchForm(search: SearchForm, extendItems: IAnFormItem[] = []) {
|
|
||||||
const data: any[] = [];
|
|
||||||
const { items = [], hideSearch, ...rest } = search;
|
|
||||||
|
|
||||||
for (const item of items) {
|
|
||||||
const { searchable, enterable, ...itemRest } = item;
|
|
||||||
let _item;
|
|
||||||
if (item.extend) {
|
|
||||||
const extend = extendItems.find((i) => i.field === item.extend);
|
|
||||||
if (extend) {
|
|
||||||
_item = merge({}, extend, itemRest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (searchable) {
|
|
||||||
(item as any).nodeProps.onSearch = () => null;
|
|
||||||
}
|
|
||||||
if (enterable) {
|
|
||||||
(item as any).nodeProps.onPressEnter = () => null;
|
|
||||||
}
|
|
||||||
data.push(_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hideSearch) {
|
|
||||||
const index = data.findIndex((i) => i.type === "submit");
|
|
||||||
if (index > -1) {
|
|
||||||
data.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
items: data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
import { defaultsDeep, isArray, merge } from 'lodash-es';
|
||||||
|
import { FormUseOptions } from '../../AnForm';
|
||||||
|
import { IAnFormItem } from '../../AnForm/components/FormItem';
|
||||||
|
import { FormItem, useItems } from '../../AnForm/hooks/useItems';
|
||||||
|
|
||||||
|
export type ExtendFormItem = Partial<
|
||||||
|
FormItem & {
|
||||||
|
/**
|
||||||
|
* 从新建弹窗继承表单项
|
||||||
|
* @example 'name'
|
||||||
|
*/
|
||||||
|
extend: string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
type SearchFormItem = ExtendFormItem & {
|
||||||
|
/**
|
||||||
|
* 是否点击图标后进行搜索
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
searchable?: boolean;
|
||||||
|
/**
|
||||||
|
* 是否回车后进行查询
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
enterable?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SearchFormObject = Omit<FormUseOptions, 'items' | 'submit'> & {
|
||||||
|
/**
|
||||||
|
* 搜索表单项
|
||||||
|
*/
|
||||||
|
items?: SearchFormItem[];
|
||||||
|
/**
|
||||||
|
* 是否隐藏查询按钮
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
hideSearch?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SearchForm = SearchFormObject | SearchFormItem[];
|
||||||
|
|
||||||
|
export function useSearchForm(search?: SearchForm, extendItems: IAnFormItem[] = []) {
|
||||||
|
if (!search) {
|
||||||
|
return ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isArray(search)) {
|
||||||
|
search = { items: search };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { items: _items = [], hideSearch, model: _model, formProps: _formProps } = search;
|
||||||
|
const extendMap = extendItems.reduce((m, v) => ((m[v.field] = v), m), {} as Record<string, IAnFormItem>);
|
||||||
|
|
||||||
|
const props = ref({
|
||||||
|
items: [] as IAnFormItem[],
|
||||||
|
model: _model ?? {},
|
||||||
|
formProps: defaultsDeep({}, _formProps, { layout: 'inline' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defualts: Partial<IAnFormItem> = {
|
||||||
|
setter: 'input',
|
||||||
|
itemProps: {
|
||||||
|
hideLabel: true,
|
||||||
|
},
|
||||||
|
setterProps: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const items: any[] = [];
|
||||||
|
for (const _item of _items) {
|
||||||
|
const { searchable, enterable, field, extend, ...itemRest } = _item;
|
||||||
|
if ((field || extend) === 'submit' && hideSearch) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let item: IAnFormItem = defaultsDeep({}, itemRest, defualts);
|
||||||
|
if (extend) {
|
||||||
|
const extendItem = extendMap[extend];
|
||||||
|
if (extendItem) {
|
||||||
|
item = merge({}, extendItem, itemRest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchable) {
|
||||||
|
(item as any).nodeProps.onSearch = () => null;
|
||||||
|
}
|
||||||
|
if (enterable) {
|
||||||
|
(item as any).nodeProps.onPressEnter = () => null;
|
||||||
|
}
|
||||||
|
if (item.setterProps) {
|
||||||
|
(item.setterProps as any).placeholder = (item.setterProps as any).placeholder ?? item.label;
|
||||||
|
}
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
props.value.items = useItems(items, props.value.model).value;
|
||||||
|
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
@ -1,50 +1,98 @@
|
||||||
import { FormModalUseOptions } from '../../AnForm/hooks/useFormModal';
|
import { FormModalUseOptions, useFormModal } from '../../AnForm/hooks/useFormModal';
|
||||||
import { AnTable, TableProps } from '../components/Table';
|
import { AnTable, TableProps } from '../components/Table';
|
||||||
import { ModifyForm } from './useModiyForm';
|
import { ModifyForm } from './useModiyForm';
|
||||||
import { SearchForm } from './useSearchForm';
|
import { SearchForm, useSearchForm } from './useSearchForm';
|
||||||
import { TableColumn, useTableColumns } from './useTableColumn';
|
import { TableColumn, useTableColumns } from './useTableColumn';
|
||||||
|
import { AnTablePlugin, PluginContainer } from './useTablePlugin';
|
||||||
|
|
||||||
export interface TableUseOptions extends Pick<TableProps, 'data' | 'tableProps' | 'pagination'> {
|
export interface TableUseOptions extends Pick<TableProps, 'data' | 'tableProps' | 'paging'> {
|
||||||
|
/**
|
||||||
|
* 唯一ID
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* 'UserTable'
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
id?: string;
|
||||||
|
/**
|
||||||
|
* 插件列表
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* [useRefresh()]
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
plugins?: AnTablePlugin[];
|
||||||
/**
|
/**
|
||||||
* 表格列
|
* 表格列
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* [{
|
||||||
|
* dataIndex: 'title',
|
||||||
|
* title: '标题'
|
||||||
|
* }]
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
columns?: TableColumn[];
|
columns?: TableColumn[];
|
||||||
/**
|
/**
|
||||||
* 搜索表单
|
* 搜索表单
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* [{
|
||||||
|
* field: 'name',
|
||||||
|
* label: '用户名称',
|
||||||
|
* setter: 'input'
|
||||||
|
* }]
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
search?: SearchForm;
|
search?: SearchForm;
|
||||||
/**
|
/**
|
||||||
* 新建弹窗
|
* 新建弹窗
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* {
|
||||||
|
* title: '添加用户',
|
||||||
|
* items: [],
|
||||||
|
* submit: (model) => {}
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
create?: FormModalUseOptions;
|
create?: FormModalUseOptions;
|
||||||
/**
|
/**
|
||||||
* 新建弹窗
|
* 修改弹窗
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* {
|
||||||
|
* extend: true, // 基于新建弹窗扩展
|
||||||
|
* title: '修改用户',
|
||||||
|
* submit: (model) => {}
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
modify?: ModifyForm;
|
modify?: ModifyForm;
|
||||||
/**
|
|
||||||
* 详情弹窗
|
|
||||||
*/
|
|
||||||
detail?: any;
|
|
||||||
/**
|
|
||||||
* 批量删除
|
|
||||||
*/
|
|
||||||
delete?: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTable(options: TableUseOptions) {
|
export function useTable(options: TableUseOptions) {
|
||||||
|
const pluginer = new PluginContainer(options.plugins ?? []);
|
||||||
|
|
||||||
|
options = pluginer.callOptionsHook(options);
|
||||||
|
|
||||||
const { columns } = useTableColumns(options.columns ?? []);
|
const { columns } = useTableColumns(options.columns ?? []);
|
||||||
const data = ref(options.data);
|
const data = ref(options.data);
|
||||||
const pagination = ref({ hide: false, showTotal: true, showPageSize: true, ...(options.pagination ?? {}) });
|
const pagination = ref({ hide: false, showTotal: true, showPageSize: true, ...(options.paging ?? {}) });
|
||||||
const tableProps = ref(options.tableProps ?? {});
|
const tableProps = ref(options.tableProps ?? {});
|
||||||
const tableRef = ref<InstanceType<typeof AnTable> | null>(null);
|
const tableRef = ref<InstanceType<typeof AnTable> | null>(null);
|
||||||
|
const searchProps = useSearchForm(options.search);
|
||||||
|
// const create = options.create && useFormModal(options.create);
|
||||||
|
|
||||||
const AnTabler = () => (
|
const AnTabler = () => (
|
||||||
<AnTable
|
<AnTable
|
||||||
ref={(el: any) => (tableRef.value = el)}
|
ref={(el: any) => (tableRef.value = el)}
|
||||||
columns={columns.value}
|
columns={columns.value}
|
||||||
data={data.value}
|
data={data.value}
|
||||||
pagination={pagination.value}
|
paging={pagination.value}
|
||||||
tableProps={tableProps.value}
|
tableProps={tableProps.value}
|
||||||
|
search={searchProps.value}
|
||||||
|
pluginer={pluginer}
|
||||||
></AnTable>
|
></AnTable>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,10 @@ interface TableButtonColumn {
|
||||||
type: 'button';
|
type: 'button';
|
||||||
/**
|
/**
|
||||||
* 按钮列表
|
* 按钮列表
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* [{ text: '删除', onClick: (args) => api.user.rmUser(args.record.id) }]
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
buttons: TableColumnButton[];
|
buttons: TableColumnButton[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
import { Button, Checkbox, Divider, InputNumber, Popover, Scrollbar, Tag } from '@arco-design/web-vue';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
interface Item {
|
||||||
|
dataIndex: string;
|
||||||
|
enable: boolean;
|
||||||
|
autoWidth: boolean;
|
||||||
|
width: number;
|
||||||
|
editable: boolean;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TableColumnConfig = defineComponent({
|
||||||
|
props: {
|
||||||
|
columns: {
|
||||||
|
type: Object as PropType<any[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const checkAll = ref(false);
|
||||||
|
const visible = ref(false);
|
||||||
|
const items = ref<Item[]>([]);
|
||||||
|
const checked = computed(() => items.value.filter(i => i.enable));
|
||||||
|
|
||||||
|
const indeterminate = computed(() => {
|
||||||
|
const check = checked.value.length;
|
||||||
|
const total = items.value.length;
|
||||||
|
return 0 < check && check < total;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => visible.value,
|
||||||
|
value => {
|
||||||
|
if (value) {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
const list: Item[] = [];
|
||||||
|
for (const column of props.columns) {
|
||||||
|
list.push({
|
||||||
|
dataIndex: column.dataIndex,
|
||||||
|
title: column.title,
|
||||||
|
enable: true,
|
||||||
|
autoWidth: !column.width,
|
||||||
|
width: column.width ?? 60,
|
||||||
|
editable: !column.configable,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
items.value = list;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onItemChange = () => {
|
||||||
|
if (checked.value.length === 0) {
|
||||||
|
checkAll.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (checked.value.length === items.value.length) {
|
||||||
|
checkAll.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCheckAllChange = (value: any) => {
|
||||||
|
for (const item of items.value) {
|
||||||
|
if (item.editable) {
|
||||||
|
item.enable = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirm = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onItemUp = (index: number) => {
|
||||||
|
[items.value[index - 1], items.value[index]] = [items.value[index], items.value[index - 1]];
|
||||||
|
};
|
||||||
|
|
||||||
|
const onItemDown = (index: number) => {
|
||||||
|
[items.value[index + 1], items.value[index]] = [items.value[index], items.value[index + 1]];
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<Popover v-model:popup-visible={visible.value} position="br" trigger="click">
|
||||||
|
{{
|
||||||
|
default: () => (
|
||||||
|
<Button class="float-right">{{ icon: () => <span class="icon-park-outline-config"></span> }}</Button>
|
||||||
|
),
|
||||||
|
content: () => (
|
||||||
|
<>
|
||||||
|
<div class="mb-1 leading-none border-b border-gray-100 pb-3">设置表格列</div>
|
||||||
|
<Scrollbar outer-class="h-96 overflow-hidden" class="h-full overflow-auto">
|
||||||
|
<ul class="grid m-0 p-0 divide-y divide-gray-100 w-[700px] overflow-auto overscroll-contain">
|
||||||
|
{items.value.map((item, index) => (
|
||||||
|
<li
|
||||||
|
key={item.dataIndex}
|
||||||
|
class="group flex items-center justify-between gap-6 py-2 pr-8 select-none"
|
||||||
|
>
|
||||||
|
<div class="flex-1 flex justify-between gap-2">
|
||||||
|
<Checkbox v-model={item.enable} disabled={!item.editable} onChange={onItemChange}>
|
||||||
|
{item.title}
|
||||||
|
</Checkbox>
|
||||||
|
<span class="hidden group-hover:inline-block ml-4">
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
shape="circle"
|
||||||
|
size="mini"
|
||||||
|
disabled={index == 0}
|
||||||
|
onClick={() => onItemUp(index)}
|
||||||
|
>
|
||||||
|
{{ icon: () => <i class="icon-park-outline-arrow-up"></i> }}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
shape="circle"
|
||||||
|
size="mini"
|
||||||
|
disabled={index == items.value.length - 1}
|
||||||
|
onClick={() => onItemDown(index)}
|
||||||
|
>
|
||||||
|
{{ icon: () => <i class="icon-park-outline-arrow-down"></i> }}
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 items-center">
|
||||||
|
<Checkbox v-model={item.autoWidth} disabled={!item.editable}>
|
||||||
|
{{
|
||||||
|
checkbox: ({ checked }: any) => (
|
||||||
|
<Tag checked={checked} checkable={item.editable} color="blue">
|
||||||
|
自适应
|
||||||
|
</Tag>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
</Checkbox>
|
||||||
|
<Divider direction="vertical" margin={8}></Divider>
|
||||||
|
<InputNumber
|
||||||
|
size="small"
|
||||||
|
v-model={item.width}
|
||||||
|
disabled={item.autoWidth || !item.editable}
|
||||||
|
min={60}
|
||||||
|
step={10}
|
||||||
|
class="!w-20"
|
||||||
|
/>
|
||||||
|
<span class="text-gray-400">像素</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Scrollbar>
|
||||||
|
<div class="mt-4 flex gap-2 items-center justify-between">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Checkbox indeterminate={indeterminate.value} v-model={checkAll.value} onChange={onCheckAllChange}>
|
||||||
|
全选
|
||||||
|
</Checkbox>
|
||||||
|
<span class="text-xs text-gray-400 ml-1">
|
||||||
|
({checked.value.length}/{items.value.length})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="space-x-2">
|
||||||
|
<Button onClick={onReset}>重置</Button>
|
||||||
|
<Button type="primary" onClick={onConfirm}>
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { TableUseOptions } from './useTable';
|
||||||
|
|
||||||
|
export interface AnTablePlugin {
|
||||||
|
/**
|
||||||
|
* 插件ID(唯一)
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* 'Plugin:Refresh'
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* 提供给其他插件使用的变量
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* { isOk: true }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
provide?: Recordable;
|
||||||
|
/**
|
||||||
|
* 组件钩子
|
||||||
|
*/
|
||||||
|
onSetup?: (context: any) => void;
|
||||||
|
/**
|
||||||
|
* 钩子
|
||||||
|
*/
|
||||||
|
options?: (options: TableUseOptions) => TableUseOptions | null | undefined | void;
|
||||||
|
/**
|
||||||
|
* 添加部件栏组件
|
||||||
|
*/
|
||||||
|
widget?: () => any;
|
||||||
|
/**
|
||||||
|
* 添加操作栏组件
|
||||||
|
*/
|
||||||
|
action?: () => any;
|
||||||
|
/**
|
||||||
|
* 搜索前处理
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
onBeforeSearch?: (args: { page: number; size: number; [key: string]: any }) => Recordable | null | undefined | void;
|
||||||
|
onPageChange?: (page: number) => void;
|
||||||
|
onSizeChange?: (size: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PluginContainer {
|
||||||
|
actions: any[] = [];
|
||||||
|
widgets: any[] = [];
|
||||||
|
|
||||||
|
constructor(private plugins: AnTablePlugin[]) {
|
||||||
|
for (const plugin of plugins) {
|
||||||
|
const action = plugin.action?.();
|
||||||
|
const widget = plugin.widget?.();
|
||||||
|
if (action) {
|
||||||
|
this.actions.push(action);
|
||||||
|
}
|
||||||
|
if (widget) {
|
||||||
|
this.widgets.push(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callSetupHook(context: any) {
|
||||||
|
for (const plugin of this.plugins) {
|
||||||
|
plugin.onSetup?.(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callOptionsHook(options: any) {
|
||||||
|
for (const plugin of this.plugins) {
|
||||||
|
options = plugin.options?.(options) ?? options;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
callActionHook(options: any) {
|
||||||
|
for (const plugin of this.plugins) {
|
||||||
|
options = plugin.options?.(options) ?? options;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
callWidgetHook(options: any) {
|
||||||
|
for (const plugin of this.plugins) {
|
||||||
|
options = plugin.options?.(options) ?? options;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
callBeforeSearchHook(options: any) {
|
||||||
|
for (const plugin of this.plugins) {
|
||||||
|
options = plugin.onBeforeSearch?.(options) ?? options;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
callPageChangeHook(page: number) {
|
||||||
|
for (const plugin of this.plugins) {
|
||||||
|
plugin.onPageChange?.(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callSizeChangeHook(page: number) {
|
||||||
|
for (const plugin of this.plugins) {
|
||||||
|
plugin.onPageChange?.(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
import { Button, Checkbox, Divider, InputNumber, Popover, Scrollbar, Tag } from '@arco-design/web-vue';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
import { AnTablePlugin } from '../hooks/useTablePlugin';
|
||||||
|
|
||||||
|
interface Item {
|
||||||
|
dataIndex: string;
|
||||||
|
enable: boolean;
|
||||||
|
autoWidth: boolean;
|
||||||
|
width: number;
|
||||||
|
editable: boolean;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TableColumnConfig = defineComponent({
|
||||||
|
props: {
|
||||||
|
columns: {
|
||||||
|
type: Object as PropType<any[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const checkAll = ref(false);
|
||||||
|
const visible = ref(false);
|
||||||
|
const items = ref<Item[]>([]);
|
||||||
|
const checked = computed(() => items.value.filter(i => i.enable));
|
||||||
|
const indeterminate = computed(() => {
|
||||||
|
const check = checked.value.length;
|
||||||
|
const total = items.value.length;
|
||||||
|
return 0 < check && check < total;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => visible.value,
|
||||||
|
value => {
|
||||||
|
if (value) {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
const list: Item[] = [];
|
||||||
|
for (const column of props.columns) {
|
||||||
|
list.push({
|
||||||
|
dataIndex: column.dataIndex,
|
||||||
|
title: column.title,
|
||||||
|
enable: true,
|
||||||
|
autoWidth: !column.width,
|
||||||
|
width: column.width ?? 60,
|
||||||
|
editable: !column.configable,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
items.value = list;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onItemChange = () => {
|
||||||
|
if (checked.value.length === 0) {
|
||||||
|
checkAll.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (checked.value.length === items.value.length) {
|
||||||
|
checkAll.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCheckAllChange = (value: any) => {
|
||||||
|
for (const item of items.value) {
|
||||||
|
if (item.editable) {
|
||||||
|
item.enable = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirm = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onItemUp = (index: number) => {
|
||||||
|
[items.value[index - 1], items.value[index]] = [items.value[index], items.value[index - 1]];
|
||||||
|
};
|
||||||
|
|
||||||
|
const onItemDown = (index: number) => {
|
||||||
|
[items.value[index + 1], items.value[index]] = [items.value[index], items.value[index + 1]];
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<Popover v-model:popup-visible={visible.value} position="br" trigger="click">
|
||||||
|
{{
|
||||||
|
default: () => (
|
||||||
|
<Button class="float-right">{{ icon: () => <span class="icon-park-outline-config"></span> }}</Button>
|
||||||
|
),
|
||||||
|
content: () => (
|
||||||
|
<>
|
||||||
|
<div class="mb-1 leading-none border-b border-gray-100 pb-3">设置表格列</div>
|
||||||
|
<Scrollbar outer-class="h-96 overflow-hidden" class="h-full overflow-auto">
|
||||||
|
<ul class="grid m-0 p-0 divide-y divide-gray-100 w-[700px] overflow-auto overscroll-contain">
|
||||||
|
{items.value.map((item, index) => (
|
||||||
|
<li
|
||||||
|
key={item.dataIndex}
|
||||||
|
class="group flex items-center justify-between gap-6 py-2 pr-8 select-none"
|
||||||
|
>
|
||||||
|
<div class="flex-1 flex justify-between gap-2">
|
||||||
|
<Checkbox v-model={item.enable} disabled={!item.editable} onChange={onItemChange}>
|
||||||
|
{item.title}
|
||||||
|
</Checkbox>
|
||||||
|
<span class="hidden group-hover:inline-block ml-4">
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
shape="circle"
|
||||||
|
size="mini"
|
||||||
|
disabled={index == 0}
|
||||||
|
onClick={() => onItemUp(index)}
|
||||||
|
>
|
||||||
|
{{ icon: () => <i class="icon-park-outline-arrow-up"></i> }}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
shape="circle"
|
||||||
|
size="mini"
|
||||||
|
disabled={index == items.value.length - 1}
|
||||||
|
onClick={() => onItemDown(index)}
|
||||||
|
>
|
||||||
|
{{ icon: () => <i class="icon-park-outline-arrow-down"></i> }}
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 items-center">
|
||||||
|
<Checkbox v-model={item.autoWidth} disabled={!item.editable}>
|
||||||
|
{{
|
||||||
|
checkbox: ({ checked }: any) => (
|
||||||
|
<Tag checked={checked} checkable={item.editable} color="blue">
|
||||||
|
自适应
|
||||||
|
</Tag>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
</Checkbox>
|
||||||
|
<Divider direction="vertical" margin={8}></Divider>
|
||||||
|
<InputNumber
|
||||||
|
size="small"
|
||||||
|
v-model={item.width}
|
||||||
|
disabled={item.autoWidth || !item.editable}
|
||||||
|
min={60}
|
||||||
|
step={10}
|
||||||
|
class="!w-20"
|
||||||
|
/>
|
||||||
|
<span class="text-gray-400">像素</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Scrollbar>
|
||||||
|
<div class="mt-4 flex gap-2 items-center justify-between">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Checkbox indeterminate={indeterminate.value} v-model={checkAll.value} onChange={onCheckAllChange}>
|
||||||
|
全选
|
||||||
|
</Checkbox>
|
||||||
|
<span class="text-xs text-gray-400 ml-1">
|
||||||
|
({checked.value.length}/{items.value.length})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="space-x-2">
|
||||||
|
<Button onClick={onReset}>重置</Button>
|
||||||
|
<Button type="primary" onClick={onConfirm}>
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件:表格列配置
|
||||||
|
* @description 配置ID将缓存结果在本地
|
||||||
|
*/
|
||||||
|
export function useColumnConfig(): AnTablePlugin {
|
||||||
|
let context: any;
|
||||||
|
return {
|
||||||
|
id: "columnconfig",
|
||||||
|
onSetup(args) {
|
||||||
|
context = args;
|
||||||
|
},
|
||||||
|
widget() {
|
||||||
|
return () => <TableColumnConfig columns={context.props.columns} />;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Button } from '@arco-design/web-vue';
|
||||||
|
import { AnTablePlugin } from '../hooks/useTablePlugin';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件:添加刷新按钮
|
||||||
|
* @description 位于搜索栏附近
|
||||||
|
*/
|
||||||
|
export function useRefresh(): AnTablePlugin {
|
||||||
|
let context: any = {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: 'refresh',
|
||||||
|
onSetup(ctx) {
|
||||||
|
context = ctx;
|
||||||
|
},
|
||||||
|
widget() {
|
||||||
|
return () => {
|
||||||
|
const { loading, refresh } = context;
|
||||||
|
return (
|
||||||
|
<Button disabled={loading.value} onClick={refresh}>
|
||||||
|
{{
|
||||||
|
icon: () => <span class="icon-park-outline-redo"></span>,
|
||||||
|
}}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { cloneDeep, defaultsDeep, merge } from 'lodash-es';
|
||||||
|
import { TableUseOptions } from '../hooks/useTable';
|
||||||
|
import { AnTablePlugin } from '../hooks/useTablePlugin';
|
||||||
|
|
||||||
|
// declare module '@/components/AnTable/hooks/useTable' {
|
||||||
|
// interface TableUseOptions {
|
||||||
|
// todo?: string;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const defaults: TableUseOptions = {
|
||||||
|
tableProps: {
|
||||||
|
rowSelection: {
|
||||||
|
showCheckedAll: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useSelection<T extends any>({ key = 'id', mode = 'key' } = {}): AnTablePlugin {
|
||||||
|
const selected = ref<T[]>([]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: 'selection',
|
||||||
|
provide: {
|
||||||
|
selected,
|
||||||
|
},
|
||||||
|
options(options) {
|
||||||
|
const opts: TableUseOptions = defaultsDeep({}, defaults);
|
||||||
|
|
||||||
|
if (!opts.tableProps!.rowKey) {
|
||||||
|
opts.tableProps!.rowKey = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'key') {
|
||||||
|
opts.tableProps!.onSelectionChange = rowkeys => {
|
||||||
|
selected.value = rowkeys as any[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'row') {
|
||||||
|
opts.tableProps!.onSelect = (rowkeys, rowkey, record) => {
|
||||||
|
const index = selected.value.findIndex((i: any) => i[key] == record[key]);
|
||||||
|
if (index > -1) {
|
||||||
|
selected.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
opts.tableProps!.onSelectAll = checked => {
|
||||||
|
if (checked) {
|
||||||
|
selected.value = cloneDeep([]);
|
||||||
|
} else {
|
||||||
|
selected.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return merge(options, opts);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="m-4 bg-white p-4">
|
<div class="m-4 bg-white p-4">
|
||||||
<div class="border-2 border-green-500 px-2 w-40 text-3xl text-green-500 mb-4">AR K056</div>
|
|
||||||
<user-table></user-table>
|
<user-table></user-table>
|
||||||
<div>{{ formatModel(emodel) }}</div>
|
<div>{{ formatModel(emodel) }}</div>
|
||||||
<UpForm />
|
<UpForm />
|
||||||
|
|
@ -11,12 +10,121 @@
|
||||||
import { api } from '@/api';
|
import { api } from '@/api';
|
||||||
import { formatModel, useForm } from '@/components/AnForm';
|
import { formatModel, useForm } from '@/components/AnForm';
|
||||||
import { useTable } from '@/components/AnTable';
|
import { useTable } from '@/components/AnTable';
|
||||||
|
import { useSelection } from '@/components/AnTable/plugins/useSelectionPlugin';
|
||||||
|
import { useRefresh } from '@/components/AnTable/plugins/useRefreshPlugin';
|
||||||
|
import { useColumnConfig } from '@/components/AnTable/plugins/useColumnConfig';
|
||||||
|
import { Ref } from 'vue';
|
||||||
|
import { Button, Message } from '@arco-design/web-vue';
|
||||||
|
import { delConfirm, sleep } from '@/utils';
|
||||||
|
|
||||||
const { component: UserTable } = useTable({
|
const { component: UserTable } = useTable({
|
||||||
|
plugins: [
|
||||||
|
useRefresh(),
|
||||||
|
useColumnConfig(),
|
||||||
|
(() => {
|
||||||
|
let selected: Ref<any[]>;
|
||||||
|
return {
|
||||||
|
id: 'deletemany',
|
||||||
|
options(options: any) {
|
||||||
|
let selectPlugin = options.plugins.find((i: any) => i.id === 'selection');
|
||||||
|
if (!selectPlugin) {
|
||||||
|
selectPlugin = useSelection();
|
||||||
|
options.plugins.push(selectPlugin);
|
||||||
|
}
|
||||||
|
selected = selectPlugin.provide.selected;
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
action() {
|
||||||
|
const loading = ref(false);
|
||||||
|
const onClick = async () => {
|
||||||
|
await delConfirm();
|
||||||
|
loading.value = true;
|
||||||
|
await sleep(3000);
|
||||||
|
loading.value = false;
|
||||||
|
selected.value = [];
|
||||||
|
Message.success('提示: 删除成功!');
|
||||||
|
};
|
||||||
|
return () => (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
status="danger"
|
||||||
|
disabled={!selected.value.length}
|
||||||
|
loading={loading.value}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
(() => {
|
||||||
|
let selected: Ref<any[]>;
|
||||||
|
return {
|
||||||
|
id: 'export',
|
||||||
|
options(options: any) {
|
||||||
|
let selectPlugin = options.plugins.find((i: any) => i.id === 'selection');
|
||||||
|
if (!selectPlugin) {
|
||||||
|
selectPlugin = useSelection();
|
||||||
|
options.plugins.push(selectPlugin);
|
||||||
|
}
|
||||||
|
selected = selectPlugin.provide.selected;
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
action() {
|
||||||
|
const onClick = async () => {
|
||||||
|
await delConfirm('确认导出选中数据吗?');
|
||||||
|
await sleep(3000);
|
||||||
|
selected.value = [];
|
||||||
|
Message.success('提示: 删除成功!');
|
||||||
|
};
|
||||||
|
return () => (
|
||||||
|
<Button disabled={!selected.value.length} onClick={onClick}>
|
||||||
|
{{
|
||||||
|
icon: () => <i class="icon-park-outline-export"></i>,
|
||||||
|
default: () => '导出',
|
||||||
|
}}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
(() => {
|
||||||
|
return {
|
||||||
|
id: 'import',
|
||||||
|
action() {
|
||||||
|
const onClick = async () => {
|
||||||
|
Message.success('提示: TODO!');
|
||||||
|
};
|
||||||
|
return () => (
|
||||||
|
<Button onClick={onClick}>
|
||||||
|
{{
|
||||||
|
icon: () => <i class="icon-park-outline-import"></i>,
|
||||||
|
default: () => '导入',
|
||||||
|
}}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
(() => {
|
||||||
|
return {
|
||||||
|
id: 'format',
|
||||||
|
options(options) {
|
||||||
|
for (const column of options.columns ?? []) {
|
||||||
|
if (column.render) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
column.render = ({ record, column }) => record[column.dataIndex!] ?? '-';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
],
|
||||||
data(search) {
|
data(search) {
|
||||||
return api.user.getUsers(search);
|
return api.user.getUsers(search);
|
||||||
},
|
},
|
||||||
pagination: {
|
paging: {
|
||||||
hide: false,
|
hide: false,
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
|
|
@ -29,10 +137,6 @@ const { component: UserTable } = useTable({
|
||||||
dataIndex: 'nickname',
|
dataIndex: 'nickname',
|
||||||
title: '用户名称',
|
title: '用户名称',
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// dataIndex: 'description',
|
|
||||||
// title: '用户描述',
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
dataIndex: 'username',
|
dataIndex: 'username',
|
||||||
title: '登录账号',
|
title: '登录账号',
|
||||||
|
|
@ -64,23 +168,49 @@ const { component: UserTable } = useTable({
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
width: 140,
|
width: 180,
|
||||||
configable: false,
|
configable: false,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: '修改',
|
text: '修改',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '修改',
|
text: '移动',
|
||||||
visible: () => false,
|
// visible: () => false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '修改',
|
text: '删除',
|
||||||
disable: () => true,
|
disable: () => true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
search: [
|
||||||
|
{
|
||||||
|
field: 'username',
|
||||||
|
label: '用户名称',
|
||||||
|
setter: 'input',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
create: {
|
||||||
|
title: '新增',
|
||||||
|
modalProps: {
|
||||||
|
width: 111,
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
field: 'title',
|
||||||
|
label: '标题',
|
||||||
|
setter: 'input',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
submit: async model => {
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
modify: {
|
||||||
|
extend: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { component: UpForm, model: emodel } = useForm({
|
const { component: UpForm, model: emodel } = useForm({
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ declare module '@vue/runtime-core' {
|
||||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
||||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
||||||
AButton: typeof import('@arco-design/web-vue')['Button']
|
AButton: typeof import('@arco-design/web-vue')['Button']
|
||||||
|
ACard: typeof import('@arco-design/web-vue')['Card']
|
||||||
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
||||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
||||||
ADivider: typeof import('@arco-design/web-vue')['Divider']
|
ADivider: typeof import('@arco-design/web-vue')['Divider']
|
||||||
|
|
@ -20,8 +21,8 @@ declare module '@vue/runtime-core' {
|
||||||
AEmpty: typeof import('@arco-design/web-vue')['Empty']
|
AEmpty: typeof import('@arco-design/web-vue')['Empty']
|
||||||
AForm: typeof import('@arco-design/web-vue')['Form']
|
AForm: typeof import('@arco-design/web-vue')['Form']
|
||||||
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
||||||
|
AImage: typeof import('@arco-design/web-vue')['Image']
|
||||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
|
||||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||||
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
||||||
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
||||||
|
|
@ -29,15 +30,20 @@ declare module '@vue/runtime-core' {
|
||||||
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
|
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
|
||||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
||||||
ALink: typeof import('@arco-design/web-vue')['Link']
|
ALink: typeof import('@arco-design/web-vue')['Link']
|
||||||
|
AList: typeof import('@arco-design/web-vue')['List']
|
||||||
|
AListItem: typeof import('@arco-design/web-vue')['ListItem']
|
||||||
|
AListItemMeta: typeof import('@arco-design/web-vue')['ListItemMeta']
|
||||||
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
||||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
||||||
|
AModal: typeof import('@arco-design/web-vue')['Modal']
|
||||||
AniEmpty: typeof import('./../components/empty/AniEmpty.vue')['default']
|
AniEmpty: typeof import('./../components/empty/AniEmpty.vue')['default']
|
||||||
APopover: typeof import('@arco-design/web-vue')['Popover']
|
APagination: typeof import('@arco-design/web-vue')['Pagination']
|
||||||
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
||||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||||
ATag: typeof import('@arco-design/web-vue')['Tag']
|
ATag: typeof import('@arco-design/web-vue')['Tag']
|
||||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
||||||
|
ATree: typeof import('@arco-design/web-vue')['Tree']
|
||||||
AUpload: typeof import('@arco-design/web-vue')['Upload']
|
AUpload: typeof import('@arco-design/web-vue')['Upload']
|
||||||
BaseOption: typeof import('./../components/editor/components/BaseOption.vue')['default']
|
BaseOption: typeof import('./../components/editor/components/BaseOption.vue')['default']
|
||||||
BreadCrumb: typeof import('./../components/breadcrumb/bread-crumb.vue')['default']
|
BreadCrumb: typeof import('./../components/breadcrumb/bread-crumb.vue')['default']
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue