ListController.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import { computed, reactive } from "vue";
  2. export class StateRoot {
  3. protected computed<F extends (state: Omit<this, "reactive">) => any>(fn: F) {
  4. return fn as ReturnType<F>;
  5. }
  6. reactive = () => {
  7. return createReactive(this) as Omit<this, "reactive">;
  8. };
  9. }
  10. function createReactive(obj: any) {
  11. const data: any = {};
  12. for (const name in obj) {
  13. if (name === "reactive" || name === "computed" || name === "proxy")
  14. continue;
  15. const objItem: any = obj[name];
  16. if (objItem instanceof Function) {
  17. data[name] = computed(() => objItem(state));
  18. } else {
  19. data[name] = objItem;
  20. }
  21. }
  22. const state: any = reactive(data);
  23. return state;
  24. }
  25. class ListItemBase {
  26. _id?: string = "";
  27. }
  28. class StatePageList<T extends ListItemBase, Q> extends StateRoot {
  29. size = 10;
  30. page = 1;
  31. total = 0;
  32. list: T[] = [];
  33. query: Q = {} as any;
  34. fields = ""; //字段使用逗号隔开
  35. loading = false;
  36. canLoadNext = this.computed((state) => {
  37. const { size, page, total } = state;
  38. return page * size < total;
  39. });
  40. }
  41. /**
  42. * 分页列表控制器
  43. */
  44. export class ListController<T extends ListItemBase, Q> {
  45. state = new StatePageList<T, Q>().reactive();
  46. request: any;
  47. dataList: any[] = [];
  48. httpGet?: (
  49. page: number,
  50. size: number,
  51. query?: any,
  52. fields?: string
  53. ) => Promise<any>;
  54. httpDelete?: (id: string) => Promise<any>;
  55. httpDetail?: (id: string) => Promise<any>;
  56. httpAdd?: (query?: any) => Promise<any>;
  57. httpSave?: (item: T) => Promise<any>;
  58. hasLimit = false;
  59. listFilter: ((item: T) => T) | null = null;
  60. constructor(
  61. request: any,
  62. get?: () => (
  63. page: number,
  64. size: number,
  65. query?: any,
  66. fields?: string
  67. ) => Promise<any>
  68. ) {
  69. this.request = request;
  70. if (get) {
  71. this.httpGet = get();
  72. }
  73. }
  74. crud(
  75. prefix: string,
  76. type: "list" | "create" | "update" | "delete" | "detail",
  77. data?: any
  78. ) {
  79. const config: any = { method: "POST" };
  80. if (type == "list") {
  81. config.method = "GET";
  82. config.params = data;
  83. } else if (type == "detail") {
  84. config.method = "GET";
  85. } else {
  86. config.data = data;
  87. }
  88. let uri = `${prefix}/${type}`;
  89. if (type == "delete" || type == "detail") uri += `/${data}`;
  90. return this.request(uri, config);
  91. }
  92. setCrudPrefix(prefix: string) {
  93. this.httpGet = (
  94. page: number,
  95. size: number,
  96. query: any,
  97. fields?: string
  98. ) => {
  99. return this.crud(prefix, "list", {
  100. page,
  101. size,
  102. query,
  103. fields,
  104. });
  105. };
  106. this.httpAdd = (data: any) => {
  107. return this.crud(prefix, "create", data);
  108. };
  109. this.httpSave = (data: any) => {
  110. return this.crud(prefix, "update", data);
  111. };
  112. this.httpDelete = (id: string) => {
  113. return this.crud(prefix, "delete", id);
  114. };
  115. this.httpDetail = (id: string) => {
  116. return this.crud(prefix, "detail", id);
  117. };
  118. }
  119. async resumeShow() {
  120. if (this.state.list.length < 1) {
  121. await this.loadPage(1);
  122. }
  123. }
  124. //刷新页面,重新获取数据
  125. async fresh() {
  126. await this.loadPage(this.state.page, this.state.size);
  127. if (this.state.list.length == 0 && this.state.page != 1) {
  128. await this.loadPage(this.state.page - 1, this.state.size);
  129. }
  130. }
  131. loadNextPage = () => {
  132. return this.loadPage(this.state.page + 1);
  133. };
  134. async loadPage(page: number, size?: number): Promise<boolean> {
  135. if (!this.httpGet) return false;
  136. this.state.loading = true;
  137. if (!size) size = this.state.size;
  138. const ret = await this.httpGet(
  139. page,
  140. size,
  141. this.state.query,
  142. this.state.fields
  143. );
  144. this.state.loading = false;
  145. if (ret.errorNo == 200) {
  146. const result = ret.result;
  147. this.state.page = page;
  148. this.state.size = size;
  149. let list = [];
  150. if (this.listFilter) {
  151. list = result.list.map(this.listFilter) || [];
  152. } else {
  153. list = [...result.list];
  154. }
  155. if (page == 1 || !this.hasLimit) {
  156. this.dataList = [...result.list];
  157. this.state.list = list;
  158. } else {
  159. this.state.list = [...this.state.list, ...list];
  160. this.dataList = [...this.dataList, ...result.list];
  161. }
  162. setTimeout(() => {
  163. this.state.total = result.total;
  164. }, 100);
  165. return true;
  166. }
  167. return false;
  168. }
  169. async addItem(data: ListItemBase) {
  170. if (!this.httpAdd) return { errorNo: 500, errorDesc: "httpAdd 未定义" };
  171. this.state.loading = true;
  172. const ret = await this.httpAdd(data);
  173. this.state.loading = false;
  174. if (ret.errorNo == 200) {
  175. await this.fresh();
  176. }
  177. return ret;
  178. }
  179. async deleteItem(id: string) {
  180. if (!this.httpDelete) return;
  181. this.state.loading = true;
  182. const ret = await this.httpDelete(id);
  183. this.state.loading = false;
  184. if (ret.errorNo == 200) {
  185. await this.fresh();
  186. }
  187. return ret;
  188. }
  189. async saveItem(item: T) {
  190. if (!this.httpSave) return;
  191. this.state.loading = true;
  192. const ret = await this.httpSave(item);
  193. this.state.loading = false;
  194. if (ret.errorNo == 200) {
  195. await this.fresh();
  196. }
  197. return ret;
  198. }
  199. async itemDetail(id: string) {
  200. if (!this.httpDetail) return;
  201. this.state.loading = true;
  202. const ret = await this.httpDetail(id);
  203. this.state.loading = false;
  204. return ret;
  205. }
  206. }