feat: 优化其他

master
luoer 2023-11-27 17:23:53 +08:00
parent 9a15a88eb0
commit 01df5849cf
9 changed files with 107 additions and 71 deletions

5
.env
View File

@ -8,7 +8,8 @@ VITE_SUBTITLE = 绝弹管理中心
# 部署路径: 当为 ./ 时路由模式需为 hash # 部署路径: 当为 ./ 时路由模式需为 hash
VITE_BASE = / VITE_BASE = /
# 接口前缀:参见 axios 的 baseURL # 接口前缀:参见 axios 的 baseURL
VITE_API = http://127.0.0.1:3030/ # VITE_API = http://127.0.0.1:3030/
VITE_API = https://appnify.app.juetan.cn/
# 首页路径 # 首页路径
VITE_HOME_PATH = /home VITE_HOME_PATH = /home
# 路由模式web(路径) hash(锚点) # 路由模式web(路径) hash(锚点)
@ -24,7 +25,7 @@ VITE_PORT = 3020
# 代理前缀 # 代理前缀
VITE_PROXY_PREFIX = /api,/upload VITE_PROXY_PREFIX = /api,/upload
# 代理地址 # 代理地址
VITE_PROXY = http://127.0.0.1:3030/ VITE_PROXY = https://appnify.app.juetan.cn/
# API文档 说明:需返回符合 OPENAPI 规范的json内容 # API文档 说明:需返回符合 OPENAPI 规范的json内容
VITE_OPENAPI = http://127.0.0.1:3030/openapi.json VITE_OPENAPI = http://127.0.0.1:3030/openapi.json
# 文件后缀 说明设为dev时会优先加载index.dev.vue文件否则回退至index.vue文件 # 文件后缀 说明设为dev时会优先加载index.dev.vue文件否则回退至index.vue文件

View File

@ -14,6 +14,11 @@ 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>;
export type ArcoTableProps = Omit<
TableInstance['$props'],
'ref' | 'pagination' | 'loading' | 'data' | 'onPageChange' | 'onPageSizeChange'
>;
export const AnTableContextKey = Symbol('AnTableContextKey') as InjectionKey<AnTableContext>; export const AnTableContextKey = Symbol('AnTableContextKey') as InjectionKey<AnTableContext>;
/** /**
@ -64,9 +69,7 @@ export const AnTable = defineComponent({
* Table * Table
*/ */
tableProps: { tableProps: {
type: Object as PropType< type: Object as PropType<ArcoTableProps>,
Omit<TableInstance['$props'], 'ref' | 'pagination' | 'loading' | 'data' | 'onPageChange' | 'onPageSizeChange'>
>,
}, },
/** /**
* *
@ -126,8 +129,9 @@ export const AnTable = defineComponent({
try { try {
loading.value = true; loading.value = true;
let params = { ...search, ...paging }; let params = { ...search, ...paging };
params = props.pluginer?.callBeforeSearchHook(params) ?? params; params = props.pluginer?.callLoadHook(params) ?? params;
const resData = await props.source(params); let resData = await props.source(params);
resData = props.pluginer?.callLoadedHook(resData) ?? params;
const { data = [], total = 0 } = resData?.data || {}; const { data = [], total = 0 } = resData?.data || {};
renderData.value = data; renderData.value = data;
setPaging({ total }); setPaging({ total });
@ -160,13 +164,11 @@ 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();
}; };

View File

@ -67,8 +67,8 @@ export interface AnTablePlugin {
* *
*/ */
onBeforeSearch?: (args: { page: number; size: number; [key: string]: any }) => Recordable | null | undefined | void; onBeforeSearch?: (args: { page: number; size: number; [key: string]: any }) => Recordable | null | undefined | void;
onPageChange?: (page: number) => void; onLoad?: (search: Recordable) => void;
onSizeChange?: (size: number) => void; onLoaded?: (res: any) => void;
} }
export class PluginContainer { export class PluginContainer {
@ -76,13 +76,7 @@ export class PluginContainer {
widgets: any[] = []; widgets: any[] = [];
constructor(private plugins: AnTablePlugin[]) { constructor(private plugins: AnTablePlugin[]) {
this.plugins.unshift( this.plugins.unshift(useTableRefresh(), useColumnConfig(), useRowFormat(), useRowDelete(), useRowModify());
useTableRefresh(),
useColumnConfig(),
useRowFormat(),
useRowDelete(),
useRowModify()
);
for (const plugin of plugins) { for (const plugin of plugins) {
const action = plugin.action?.(); const action = plugin.action?.();
if (action) { if (action) {
@ -129,15 +123,17 @@ export class PluginContainer {
return options; return options;
} }
callPageChangeHook(page: number) { callLoadHook(search: Recordable) {
for (const plugin of this.plugins) { for (const plugin of this.plugins) {
plugin.onPageChange?.(page); search = plugin.onLoad?.(search) ?? search;
} }
return search as any;
} }
callSizeChangeHook(page: number) { callLoadedHook(res: any) {
for (const plugin of this.plugins) { for (const plugin of this.plugins) {
plugin.onPageChange?.(page); res = plugin.onLoaded?.(res) ?? res;
} }
return res;
} }
} }

View File

@ -1,16 +1,27 @@
import { Ref } from 'vue'; import { Ref } from 'vue';
import { AnTableContext } from '../components/Table'; import { AnTableContext, ArcoTableProps } from '../components/Table';
import { AnTablePlugin } from '../hooks/useTablePlugin'; import { AnTablePlugin } from '../hooks/useTablePlugin';
import { useTableSelect } from './useTableSelect'; import { useTableSelect } from './useTableSelect';
import { delConfirm, delOptions, sleep } from '@/utils'; import { delConfirm, delOptions, sleep } from '@/utils';
import { Button, Message } from '@arco-design/web-vue'; import { Button, Message, TableInstance } from '@arco-design/web-vue';
export function useTableDelete(): AnTablePlugin { interface UseTableDeleteOptions {
confirm?: string;
onDelete?: (keys: (string | number)[]) => any | Promise<any>;
}
export function useTableDelete(options: UseTableDeleteOptions = {}): AnTablePlugin {
let selected: Ref<any[]>; let selected: Ref<any[]>;
let context: AnTableContext; let context: AnTableContext;
let tableProps: ArcoTableProps;
const { confirm, onDelete } = options;
return { return {
id: 'deletemany', id: 'deletemany',
onSetup(ctx) {
context = ctx;
tableProps = ctx.props.tableProps!;
},
options(options) { options(options) {
let selectPlugin = options.plugins?.find(i => i.id === 'selection'); let selectPlugin = options.plugins?.find(i => i.id === 'selection');
if (!selectPlugin) { if (!selectPlugin) {
@ -20,18 +31,31 @@ export function useTableDelete(): AnTablePlugin {
selected = selectPlugin.provide!.selectedKeys; selected = selectPlugin.provide!.selectedKeys;
return options; return options;
}, },
onLoaded() {
console.log('loaded');
selected.value = [];
},
action() { action() {
const onClick = async (props: any) => { const onClick = async (props: any) => {
delConfirm({ delConfirm({
...delOptions, ...delOptions,
content: '危险操作,确定删除所选数据吗?', content: confirm ?? '危险操作,确定删除所选数据吗?',
async onBeforeOk() { async onBeforeOk() {
await sleep(3000); await sleep(3000);
const res: any = await onClick?.(props); try {
const res: any = await onDelete?.(props);
const msg = res?.data?.message; const msg = res?.data?.message;
msg && Message.success(`提示: ${msg}`); msg && Message.success(`提示: ${msg}`);
if (tableProps) {
(tableProps as any).selectedKeys = [];
}
selected.value = []; selected.value = [];
context.refresh(); context.refresh();
return true;
} catch (e) {
console.log('删除失败:', e);
}
return false;
}, },
}); });
}; };

View File

@ -1,12 +1,7 @@
import { cloneDeep, defaultsDeep, merge } from 'lodash-es'; import { cloneDeep, defaultsDeep, merge } from 'lodash-es';
import { TableUseOptions } from '../hooks/useTable'; import { TableUseOptions } from '../hooks/useTable';
import { AnTablePlugin } from '../hooks/useTablePlugin'; import { AnTablePlugin } from '../hooks/useTablePlugin';
import { AnTableContext, ArcoTableProps } from '../components/Table';
// declare module '@/components/AnTable/hooks/useTable' {
// interface TableUseOptions {
// todo?: string;
// }
// }
const defaults: TableUseOptions = { const defaults: TableUseOptions = {
tableProps: { tableProps: {
@ -26,8 +21,15 @@ interface UseTableSelectOptions {
* @description 使 * @description 使
*/ */
export function useTableSelect({ key = 'id', mode = 'key' }: UseTableSelectOptions = {}): AnTablePlugin { export function useTableSelect({ key = 'id', mode = 'key' }: UseTableSelectOptions = {}): AnTablePlugin {
let context: AnTableContext;
const selectedKeys = ref<(string | number)[]>([]); const selectedKeys = ref<(string | number)[]>([]);
const selectedRows = ref<any[]>([]); const selectedRows = ref<any[]>([]);
const setKeys = (keys: any[]) => {
const tableProps = context.props.tableProps;
if (tableProps) {
(tableProps as any).selectedKeys = keys;
}
};
return { return {
id: 'selection', id: 'selection',
@ -35,6 +37,9 @@ export function useTableSelect({ key = 'id', mode = 'key' }: UseTableSelectOptio
selectedKeys, selectedKeys,
selectedRows, selectedRows,
}, },
onSetup(ctx) {
context = ctx;
},
options(options) { options(options) {
const opts: TableUseOptions = defaultsDeep({}, defaults); const opts: TableUseOptions = defaultsDeep({}, defaults);
@ -45,6 +50,8 @@ export function useTableSelect({ key = 'id', mode = 'key' }: UseTableSelectOptio
if (mode === 'both' || mode === 'key') { if (mode === 'both' || mode === 'key') {
opts.tableProps!.onSelectionChange = rowkeys => { opts.tableProps!.onSelectionChange = rowkeys => {
selectedKeys.value = rowkeys; selectedKeys.value = rowkeys;
setKeys(rowkeys);
console.log(rowkeys);
}; };
} }
@ -54,6 +61,7 @@ export function useTableSelect({ key = 'id', mode = 'key' }: UseTableSelectOptio
if (index > -1) { if (index > -1) {
selectedRows.value.splice(index, 1); selectedRows.value.splice(index, 1);
} }
setKeys(selectedRows.value.map(i => i.id));
}; };
opts.tableProps!.onSelectAll = checked => { opts.tableProps!.onSelectAll = checked => {
if (checked) { if (checked) {
@ -61,10 +69,14 @@ export function useTableSelect({ key = 'id', mode = 'key' }: UseTableSelectOptio
} else { } else {
selectedRows.value = []; selectedRows.value = [];
} }
setKeys(selectedRows.value.map(i => i.id));
}; };
} }
return merge(options, opts); return merge(options, opts);
}, },
onLoaded() {
setKeys([]);
},
}; };
} }

View File

@ -73,7 +73,9 @@ const { component: UserTable } = useTable({
], ],
}, },
], ],
source: search => api.user.getUsers(search), source: search => {
return [];
},
search: [ search: [
{ {
field: 'username', field: 'username',

View File

@ -2,12 +2,12 @@
<div class="w-[210px] h-full overflow-hidden grid grid-rows-[auto_1fr]"> <div class="w-[210px] h-full overflow-hidden grid grid-rows-[auto_1fr]">
<div class="flex gap-2"> <div class="flex gap-2">
<a-input-search allow-clear placeholder="字典类型" class="mb-2"></a-input-search> <a-input-search allow-clear placeholder="字典类型" class="mb-2"></a-input-search>
<a-button @click="formCtx.open"> <a-button @click="open()">
<template #icon> <template #icon>
<i class="icon-park-outline-add"></i> <i class="icon-park-outline-add"></i>
</template> </template>
</a-button> </a-button>
<form-modal></form-modal> <DictTypeModal />
</div> </div>
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto"> <a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto">
<ul v-if="list.length" class="pl-0 mt-0"> <ul v-if="list.length" class="pl-0 mt-0">
@ -29,7 +29,7 @@
</template> </template>
</a-button> </a-button>
<template #content> <template #content>
<a-doption @click="formCtx.open(item)"> <a-doption @click="open(item)">
<template #icon> <template #icon>
<i class="icon-park-outline-edit"></i> <i class="icon-park-outline-edit"></i>
</template> </template>
@ -46,17 +46,17 @@
</div> </div>
</li> </li>
</ul> </ul>
<ani-empty v-else></ani-empty> <an-empty v-else></an-empty>
</a-scrollbar> </a-scrollbar>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { DictType, api } from "@/api"; import { DictType, api } from '@/api';
import { useAniFormModal } from "@/components"; import { useFormModal } from '@/components/AnForm';
import { delConfirm } from "@/utils"; import { delConfirm } from '@/utils';
import { Message } from "@arco-design/web-vue"; import { Message } from '@arco-design/web-vue';
import { PropType } from "vue"; import { PropType } from 'vue';
defineProps({ defineProps({
current: { current: {
@ -64,13 +64,13 @@ defineProps({
}, },
}); });
const emit = defineEmits(["change"]); const emit = defineEmits(['change']);
const list = ref<DictType[]>([]); const list = ref<DictType[]>([]);
const updateDictTypes = async () => { const updateDictTypes = async () => {
const res = await api.dictType.getDictTypes({ size: 0 }); const res = await api.dictType.getDictTypes({ size: 0 });
list.value = res.data.data ?? []; list.value = res.data.data ?? [];
list.value.length && emit("change", list.value[0]); list.value.length && emit('change', list.value[0]);
}; };
onMounted(updateDictTypes); onMounted(updateDictTypes);
@ -81,38 +81,33 @@ const onDeleteRow = async (row: DictType) => {
Message.success(res.data.message); Message.success(res.data.message);
}; };
const [formModal, formCtx] = useAniFormModal({ const { component: DictTypeModal, open } = useFormModal({
title: ({ model }) => (!model.id ? "新建字典类型" : "修改字典类型"), title: ({ model }) => (!model.id ? '新建字典类型' : '修改字典类型'),
trigger: false, trigger: false,
modalProps: {
width: 580, width: 580,
},
model: {
id: undefined,
},
items: [ items: [
{ {
field: "name", field: 'name',
label: "名称", label: '名称',
type: "input", setter: 'input',
}, },
{ {
field: "code", field: 'code',
label: "唯一编码", label: '唯一编码',
type: "input", setter: 'input',
}, },
{ {
field: "description", field: 'description',
label: "备注信息", label: '备注信息',
type: "textarea", setter: 'textarea',
}, },
], ],
submit: async ({ model }) => { submit: async model => {
let res; let res;
if (model.id) { if (model.id) {
res = await api.dictType.setDictType(model.id, model); res = await api.dictType.setDictType(model.id, model as any);
} else { } else {
res = await api.dictType.addDictType(model); res = await api.dictType.addDictType(model as any);
} }
updateDictTypes(); updateDictTypes();
return res; return res;

View File

@ -56,7 +56,9 @@ const { component: OperationTable } = useTable({
render: ({ record }) => dayjs(record.createdAt).fromNow(), render: ({ record }) => dayjs(record.createdAt).fromNow(),
}, },
], ],
source: model => api.log.getLoginLogs(model), source: model => {
return api.log.getLoginLogs(model);
},
search: [ search: [
{ {
field: 'nickname', field: 'nickname',

View File

@ -84,7 +84,9 @@ const { component: UserTable } = useTable({
], ],
}, },
], ],
source: model => api.user.getUsers(model), source: model => {
return api.user.getUsers(model);
},
search: [ search: [
{ {
field: 'nickname', field: 'nickname',