userCrud.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. import request from "@/utils/request";
  2. import { PageOption } from "@smallwei/avue";
  3. import { ElMessageBox, ElMessage } from "element-plus";
  4. import { useUserStoreHook } from "@/store/modules/user";
  5. import { checkPerm } from "@/directive/permission";
  6. import { configs } from "@typescript-eslint/eslint-plugin";
  7. interface UseCrudConfig {
  8. // 模块的url,用来进行增删改查
  9. src?: string;
  10. dataListUrl?: string;
  11. afterDataList?: Function;
  12. afterAdd?: Function;
  13. // 需要操作的数据
  14. row?: any;
  15. // done用于结束操作
  16. done?: () => void;
  17. // 具体操作的行
  18. index?: number;
  19. // 用于中断操作
  20. loading?: () => void;
  21. // 额外查询参数 一般用search的值就可以了
  22. params?: object;
  23. // 是否是编辑,如果是编辑调用更新,否则调用新增
  24. isEdit?: boolean;
  25. // 用于多选选中后添加进入,toDeleteIds中的key值。
  26. multipleSelectKey?: string;
  27. }
  28. export const useCrud = (config?: UseCrudConfig) => {
  29. const url = ref(config?.src);
  30. const commonConfig = ref(config);
  31. /** 表格配置属性 */
  32. const option = ref({
  33. searchIcon: true,
  34. // searchSpan: 4,
  35. searchIndex: 3, //searchIcon是否启用功能按钮, searchIndex配置收缩展示的个数,默认为2个
  36. index: true, //是否显示第几项
  37. indexLabel: "序号",
  38. indexWidth: "55px",
  39. refreshBtn: false,
  40. border: true,
  41. viewBtn: true,
  42. tip: false, //选中的提示
  43. });
  44. const data = ref<any>([]); //表格数据
  45. const form = ref({}); //新增或者编辑弹出的表单绑定值
  46. /** 表格顶部搜索的表单的变量 v-model */
  47. const search = ref({});
  48. /** 表格的分页数据 v-model */
  49. const page = ref<PageOption>({
  50. total: 0,
  51. currentPage: 1,
  52. pageSize: 10,
  53. });
  54. /** 配置项结构 v-model */
  55. // defaults ?: AvueCrudDefaults;
  56. // 多选返回的数组,命名是因为后来增加了很多功能,现在这个数组里面的值是根据multipleSelectKey来决定
  57. const toDeleteIds = ref<Array<string>>([]);
  58. // 多选返回的数组,返回每一行的row数据
  59. const selectedRows = ref<any[]>([]);
  60. const save = async (config?: UseCrudConfig) => {
  61. try {
  62. const path = config?.isEdit ? "/update" : "/add";
  63. const res = (await request({
  64. url: `${url.value}${path}`,
  65. method: "post",
  66. data: form.value,
  67. })) as any;
  68. if (res?.code == 200) {
  69. Methords.dataList();
  70. config?.done && config?.done();
  71. ElMessage.success(res?.msg ?? "");
  72. if (commonConfig.value?.afterAdd) {
  73. commonConfig.value?.afterAdd(res?.data);
  74. }
  75. } else {
  76. config?.loading && config?.loading();
  77. ElMessage.error(res?.msg ?? "");
  78. }
  79. } catch (err) {
  80. config?.loading && config?.loading();
  81. }
  82. };
  83. const handleSearchData = () => {
  84. // const array = [null, undefined, ""];
  85. search.value = Object.fromEntries(
  86. Object.entries(search.value).filter(
  87. ([key, value]) =>
  88. value !== null &&
  89. value !== undefined &&
  90. Object.keys(value).length !== 0
  91. )
  92. );
  93. };
  94. /**
  95. * 表格增删改查等基本方法
  96. */
  97. const Methords = {
  98. /**
  99. * 查询方法
  100. */
  101. dataList: async (config?: UseCrudConfig) => {
  102. handleSearchData();
  103. try {
  104. const res = await request({
  105. url: commonConfig.value?.dataListUrl ?? `${url.value}/page`,
  106. method: "post",
  107. data: {
  108. pageNo: page.value.currentPage,
  109. pageSize: page.value.pageSize,
  110. ...(commonConfig.value?.params ?? {}),
  111. ...search.value,
  112. },
  113. });
  114. if (res?.data) {
  115. if (res?.data instanceof Array) {
  116. data.value = res?.data || [];
  117. page.value.total = res?.data?.length || 0;
  118. } else {
  119. data.value = res?.data?.records || [];
  120. page.value.total = res?.data?.totalCount || 0;
  121. }
  122. if (commonConfig.value?.afterDataList) {
  123. commonConfig.value?.afterDataList();
  124. }
  125. }
  126. config?.done && config?.done();
  127. } catch (err) {
  128. config?.loading && config?.loading();
  129. } finally {
  130. config?.done && config?.done();
  131. }
  132. },
  133. dataEditList: async (config?: UseCrudConfig) => {
  134. handleSearchData();
  135. try {
  136. const res = await request({
  137. url: `${url.value}/page`,
  138. method: "post",
  139. data: {
  140. pageNo: page.value.currentPage,
  141. pageSize: page.value.pageSize,
  142. ...search.value,
  143. ...(commonConfig.value?.params ?? {}),
  144. },
  145. });
  146. if (res?.data) {
  147. if (res?.data instanceof Array) {
  148. data.value = res?.data || [];
  149. page.value.total = res?.data?.length || 0;
  150. } else {
  151. data.value = res?.data?.records || [];
  152. for (let i = 0; i < data.value.length; i++) {
  153. data.value[i].$cellEdit = true;
  154. if (
  155. data.value[i].children != undefined &&
  156. data.value[i].children != null &&
  157. data.value[i].children.length > 0
  158. ) {
  159. for (let j = 0; j < data.value[i].children.length; j++) {
  160. data.value[i].children[j].$cellEdit = true;
  161. }
  162. }
  163. }
  164. page.value.total = res?.data?.totalCount || 0;
  165. }
  166. }
  167. config?.done && config?.done();
  168. } catch (err) {
  169. config?.loading && config?.loading();
  170. } finally {
  171. config?.done && config?.done();
  172. }
  173. },
  174. dataNoPageList: async (config?: UseCrudConfig) => {
  175. handleSearchData();
  176. try {
  177. const res = await request({
  178. url: `${url.value}/list`,
  179. method: "post",
  180. data: {
  181. ...search.value,
  182. },
  183. });
  184. if (res?.data) {
  185. data.value = res?.data || [];
  186. }
  187. config?.done && config?.done();
  188. } catch (err) {
  189. config?.loading && config?.loading();
  190. } finally {
  191. config?.done && config?.done();
  192. }
  193. },
  194. createRow: (row: any, done: () => void, loading: () => void) => {
  195. save({ row: row, done: done, loading: loading });
  196. },
  197. updateRow: (
  198. row: any,
  199. index: number,
  200. done: () => void,
  201. loading: () => void
  202. ) => {
  203. save({
  204. row: row,
  205. done: done,
  206. loading: loading,
  207. index: index,
  208. isEdit: true,
  209. });
  210. },
  211. deleteRow: async (row: any, index: number, done: () => void) => {
  212. ElMessageBox.confirm("是否删除所选中数据?", "提示", {
  213. confirmButtonText: "确定",
  214. cancelButtonText: "取消",
  215. type: "warning",
  216. }).then(async () => {
  217. if (row.children && row.children.length > 0) {
  218. ElMessage.error("请先解绑下级关系");
  219. return;
  220. }
  221. try {
  222. const res = await request({
  223. url: `${url.value}/del`,
  224. method: "post",
  225. data: row,
  226. });
  227. Methords.dataList();
  228. config?.done && config?.done();
  229. } catch (err) {
  230. config?.loading && config?.loading();
  231. } finally {
  232. config?.done && config?.done();
  233. }
  234. });
  235. },
  236. // 设置selection: true,后监听选中改变事件,将Id存入数组
  237. selectionChange: (rows?: any[]) => {
  238. toDeleteIds.value = [];
  239. selectedRows.value = [];
  240. rows?.forEach((element) => {
  241. toDeleteIds.value.push(
  242. element[commonConfig.value?.multipleSelectKey ?? "id"]
  243. );
  244. selectedRows.value.push(element);
  245. });
  246. },
  247. /**
  248. * 表格上方的多选删除方法
  249. */
  250. multipleDelete: async () => {
  251. ElMessageBox.confirm("是否删除所选中数据?", "提示", {
  252. confirmButtonText: "确定",
  253. cancelButtonText: "取消",
  254. type: "warning",
  255. }).then(async () => {
  256. try {
  257. const res = await request({
  258. url: `${url.value}/batch-del`,
  259. method: "post",
  260. data: { ids: toDeleteIds.value },
  261. });
  262. Methords.dataList();
  263. config?.done && config?.done();
  264. } catch (err) {
  265. config?.loading && config?.loading();
  266. } finally {
  267. config?.done && config?.done();
  268. }
  269. });
  270. },
  271. /**
  272. * 表格拖拽后批量保存
  273. * */
  274. multipleUpdate: async () => {
  275. try {
  276. // 由于数据带有$开头的属性,所以需要处理下,改为只传id和sortNum。
  277. const dtosArray: { id: string; sortNum: number }[] = [];
  278. for (let i = 0; i < data.value.length; i++) {
  279. let cur = page.value.currentPage ?? 1;
  280. cur = cur - 1;
  281. const size = page.value.pageSize ?? 10;
  282. let sortNum = cur * size;
  283. sortNum = sortNum + i;
  284. dtosArray.push({ id: data.value[i].id, sortNum: sortNum });
  285. }
  286. const res = await request({
  287. url: `${url.value}/batch-update`,
  288. method: "post",
  289. data: dtosArray,
  290. });
  291. Methords.dataList();
  292. config?.done && config?.done();
  293. } catch (err) {
  294. config?.loading && config?.loading();
  295. } finally {
  296. config?.done && config?.done();
  297. }
  298. },
  299. /**
  300. * 点击搜索按钮触发
  301. */
  302. searchChange: async (params: any, done: () => void) => {
  303. // 点击搜索的时候重置search的值,要不然会传过去所有的值包括空字符串的值
  304. search.value = params;
  305. page.value.currentPage = 1;
  306. Methords.dataList({ done: done });
  307. },
  308. /**
  309. * 点击清空按钮触发
  310. */
  311. resetChange: async (item: any) => {
  312. page.value.currentPage = 1;
  313. Methords.dataList();
  314. },
  315. };
  316. /**
  317. * 表格辅助方法,按钮权限,导入导出
  318. */
  319. const Utils = {
  320. checkBtnPerm: (str: string) => {
  321. // 「超级管理员」拥有所有的按钮权限
  322. const { roles, perms } = useUserStoreHook().user;
  323. if (roles.includes("ROOT")) {
  324. return true;
  325. }
  326. const hasPerm = perms?.some((perm) => {
  327. return str.includes(perm);
  328. });
  329. return hasPerm;
  330. },
  331. /**
  332. * 根据返回的数据下载文件
  333. */
  334. downloadFile: (response: any) => {
  335. const fileData = response.data;
  336. const fileName = decodeURI(
  337. response.headers["content-disposition"].split(";")[1].split("=")[1]
  338. );
  339. const fileType =
  340. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
  341. const blob = new Blob([fileData], { type: fileType });
  342. const downloadUrl = window.URL.createObjectURL(blob);
  343. const downloadLink = document.createElement("a");
  344. downloadLink.href = downloadUrl;
  345. downloadLink.download = fileName;
  346. document.body.appendChild(downloadLink);
  347. downloadLink.click();
  348. document.body.removeChild(downloadLink);
  349. window.URL.revokeObjectURL(downloadUrl);
  350. },
  351. /**
  352. * 下载模版 传入url
  353. */
  354. downloadTemplate: async (urlStr: string) => {
  355. const response = await request({
  356. url: urlStr,
  357. method: "get",
  358. responseType: "arraybuffer",
  359. });
  360. Utils.downloadFile(response);
  361. },
  362. /**
  363. * 根据搜索项导出数据
  364. */
  365. exportData: async (urlStr: string) => {
  366. handleSearchData();
  367. const response = await request({
  368. url: urlStr,
  369. method: "post",
  370. data: search.value,
  371. responseType: "arraybuffer",
  372. });
  373. Utils.downloadFile(response);
  374. },
  375. /**
  376. * 根据搜索项导出数据
  377. */
  378. downloadSPCTemplate: async (urlStr: string, filePath: string) => {
  379. handleSearchData();
  380. const response = await request({
  381. url: urlStr,
  382. method: "post",
  383. data: search.value,
  384. params: { filePath: filePath },
  385. responseType: "arraybuffer",
  386. });
  387. Utils.downloadFile(response);
  388. },
  389. };
  390. return {
  391. url,
  392. option,
  393. data,
  394. form,
  395. search,
  396. page,
  397. toDeleteIds,
  398. Methords,
  399. Utils,
  400. selectedRows,
  401. commonConfig,
  402. };
  403. };