import { computed, reactive } from "vue"; export class StateRoot { protected computed) => any>(fn: F) { return fn as ReturnType; } reactive = () => { return createReactive(this) as Omit; }; } function createReactive(obj: any) { const data: any = {}; for (const name in obj) { if (name === "reactive" || name === "computed" || name === "proxy") continue; const objItem: any = obj[name]; if (objItem instanceof Function) { data[name] = computed(() => objItem(state)); } else { data[name] = objItem; } } const state: any = reactive(data); return state; } class ListItemBase { _id?: string = ""; } class StatePageList extends StateRoot { size = 10; page = 1; total = 0; list: T[] = []; query: Q = {} as any; fields = ""; //字段使用逗号隔开 loading = false; canLoadNext = this.computed((state) => { const { size, page, total } = state; return page * size < total; }); } /** * 分页列表控制器 */ export class ListController { state = new StatePageList().reactive(); request: any; dataList: any[] = []; httpGet?: ( page: number, size: number, query?: any, fields?: string ) => Promise; httpDelete?: (id: string) => Promise; httpDetail?: (id: string) => Promise; httpAdd?: (query?: any) => Promise; httpSave?: (item: T) => Promise; hasLimit = false; listFilter: ((item: T) => T) | null = null; constructor( request: any, get?: () => ( page: number, size: number, query?: any, fields?: string ) => Promise ) { this.request = request; if (get) { this.httpGet = get(); } } crud( prefix: string, type: "list" | "create" | "update" | "delete" | "detail", data?: any ) { const config: any = { method: "POST" }; if (type == "list") { config.method = "GET"; config.params = data; } else if (type == "detail") { config.method = "GET"; } else { config.data = data; } let uri = `${prefix}/${type}`; if (type == "delete" || type == "detail") uri += `/${data}`; return this.request(uri, config); } setCrudPrefix(prefix: string) { this.httpGet = ( page: number, size: number, query: any, fields?: string ) => { return this.crud(prefix, "list", { page, size, query, fields, }); }; this.httpAdd = (data: any) => { return this.crud(prefix, "create", data); }; this.httpSave = (data: any) => { return this.crud(prefix, "update", data); }; this.httpDelete = (id: string) => { return this.crud(prefix, "delete", id); }; this.httpDetail = (id: string) => { return this.crud(prefix, "detail", id); }; } async resumeShow() { if (this.state.list.length < 1) { await this.loadPage(1); } } //刷新页面,重新获取数据 async fresh() { await this.loadPage(this.state.page, this.state.size); if (this.state.list.length == 0 && this.state.page != 1) { await this.loadPage(this.state.page - 1, this.state.size); } } loadNextPage = () => { return this.loadPage(this.state.page + 1); }; async loadPage(page: number, size?: number): Promise { if (!this.httpGet) return false; this.state.loading = true; if (!size) size = this.state.size; const ret = await this.httpGet( page, size, this.state.query, this.state.fields ); this.state.loading = false; if (ret.errorNo == 200) { const result = ret.result; this.state.page = page; this.state.size = size; let list = []; if (this.listFilter) { list = result.list.map(this.listFilter) || []; } else { list = [...result.list]; } if (page == 1 || !this.hasLimit) { this.dataList = [...result.list]; this.state.list = list; } else { this.state.list = [...this.state.list, ...list]; this.dataList = [...this.dataList, ...result.list]; } setTimeout(() => { this.state.total = result.total; }, 100); return true; } return false; } async addItem(data: ListItemBase) { if (!this.httpAdd) return { errorNo: 500, errorDesc: "httpAdd 未定义" }; this.state.loading = true; const ret = await this.httpAdd(data); this.state.loading = false; if (ret.errorNo == 200) { await this.fresh(); } return ret; } async deleteItem(id: string) { if (!this.httpDelete) return; this.state.loading = true; const ret = await this.httpDelete(id); this.state.loading = false; if (ret.errorNo == 200) { await this.fresh(); } return ret; } async saveItem(item: T) { if (!this.httpSave) return; this.state.loading = true; const ret = await this.httpSave(item); this.state.loading = false; if (ret.errorNo == 200) { await this.fresh(); } return ret; } async itemDetail(id: string) { if (!this.httpDetail) return; this.state.loading = true; const ret = await this.httpDetail(id); this.state.loading = false; return ret; } }