crud.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import { request } from './index';
  2. import $storage from '@/sheep/utils/storage';
  3. import { ElMessage } from 'element-plus';
  4. // 检测是否为mall前缀的接口
  5. const isMallAPI = (url) => {
  6. return url.startsWith('/mall') || url.startsWith('mall');
  7. };
  8. // 查看列表
  9. export const LIST = (url, data) => {
  10. const requestConfig = {
  11. url: url + `/list`,
  12. method: 'POST',
  13. };
  14. requestConfig.data = data;
  15. return request(requestConfig);
  16. };
  17. // 查看详情
  18. export const DETAIL = (url, id) => {
  19. const isMall = isMallAPI(url);
  20. return request({
  21. url: url + `/detail`,
  22. method: isMall ? 'GET' : 'POST', // mall用GET,其他用POST
  23. params: { id },
  24. });
  25. };
  26. // 新增
  27. export const ADD = (url, data) =>
  28. request({
  29. url: url + '/add',
  30. method: 'POST',
  31. data,
  32. options: {
  33. showSuccessMessage: false,
  34. },
  35. });
  36. // 编辑&更新
  37. export const EDIT = (url, data) => {
  38. const isMall = isMallAPI(url);
  39. return request({
  40. url: url + `/update`,
  41. method: isMall ? 'PUT' : 'POST', // mall用PUT,其他用POST
  42. data,
  43. options: {
  44. showSuccessMessage: false,
  45. },
  46. });
  47. };
  48. // 删除(软删除/真实删除)
  49. export const DELETE = (url, data) => {
  50. const isMall = isMallAPI(url);
  51. return request({
  52. url: url + `/delete`,
  53. method: isMall ? 'DELETE' : 'POST', // mall用DELETE,其他用POST
  54. params: data,
  55. options: {
  56. showSuccessMessage: true,
  57. },
  58. });
  59. };
  60. // 选择
  61. export const SELECT = (url, params) =>
  62. request({
  63. url: url + '/select',
  64. method: 'GET',
  65. params,
  66. });
  67. // 回收站列表
  68. export const RECYCLE_BIN = (url, params) =>
  69. request({
  70. url: url + '/recyclebin',
  71. method: 'GET',
  72. params,
  73. });
  74. // 销毁
  75. export const DESTROY = (url, id) =>
  76. request({
  77. url: url + '/destroy/' + id,
  78. method: 'DELETE',
  79. });
  80. // 还原
  81. export const RESTORE = (url, id) =>
  82. request({
  83. url: url + '/restore/' + id,
  84. method: 'PUT',
  85. });
  86. // 通用增删改查
  87. export const CRUD = (
  88. url,
  89. methods = ['list', 'detail', 'add', 'edit', 'delete', 'export'],
  90. options = {},
  91. ) => {
  92. const apis = {};
  93. if (methods.includes('list'))
  94. apis.list = (params, pageInParams = true) => LIST(url, params, pageInParams);
  95. if (methods.includes('detail')) apis.detail = (id) => DETAIL(url, id);
  96. if (methods.includes('add')) apis.add = (data) => ADD(url, data);
  97. if (methods.includes('edit')) apis.edit = (id, data) => EDIT(url, id, data);
  98. if (methods.includes('delete')) apis.delete = (id) => DELETE(url, id);
  99. if (methods.includes('report'))
  100. apis.report = (params, filename) =>
  101. REPORT(url, params, filename, 'report', options.reportMethod);
  102. if (methods.includes('export'))
  103. apis.export = (params, filename) =>
  104. REPORT(url, params, filename, 'export', options.exportMethod);
  105. return apis;
  106. };
  107. // 通用回收站
  108. export const RECYCLE = (url) => {
  109. return {
  110. recyclebin: (params) => RECYCLE_BIN(url, params),
  111. restore: (id) => RESTORE(url, id),
  112. destroy: (id) => DESTROY(url, id),
  113. };
  114. };
  115. // 导出报表 - 需要超级管理员权限
  116. export const REPORT = async (
  117. url,
  118. params,
  119. filename = '导出数据',
  120. typeName = 'report',
  121. method = null,
  122. ) => {
  123. // 获取用户信息进行权限验证
  124. const userInfo = $storage.get('userInfo');
  125. // 检查用户是否为超级管理员
  126. if (!userInfo || userInfo.roleName !== '超级管理员') {
  127. ElMessage.error('只有超级管理员可以导出');
  128. return Promise.reject(new Error('权限不足:只有超级管理员可以导出'));
  129. }
  130. // 确定HTTP方法:优先使用传入的method参数,否则使用默认逻辑
  131. const httpMethod = method || (typeName == 'report' ? 'GET' : 'POST');
  132. let response = null;
  133. try {
  134. // 发送导出请求
  135. response = await request({
  136. url: url + `/${typeName}`,
  137. method: httpMethod,
  138. ...(httpMethod === 'GET' ? { params } : { data: params }),
  139. responseType: 'blob', // 用于文件下载
  140. headers: {
  141. Accept:
  142. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/octet-stream, */*',
  143. },
  144. options: {
  145. showSuccessMessage: false,
  146. },
  147. });
  148. // 检查响应状态和内容类型
  149. if (response.status === 200 && response.headers['content-type']?.includes('excel')) {
  150. // 成功响应,处理文件下载
  151. const blob = response.data; // 从响应对象中获取 Blob 数据
  152. const downloadUrl = window.URL.createObjectURL(blob);
  153. const link = document.createElement('a');
  154. link.href = downloadUrl;
  155. // 优先从响应头的 content-disposition 中获取文件名
  156. let finalFilename = `${filename}_${new Date().toISOString().slice(0, 10)}.xlsx`;
  157. // 尝试多种方式获取 content-disposition
  158. const contentDisposition =
  159. response.headers['content-disposition'] ||
  160. response.headers['Content-Disposition'] ||
  161. response.headers.get?.('content-disposition') ||
  162. response.headers.get?.('Content-Disposition');
  163. if (contentDisposition) {
  164. // 尝试从 content-disposition 中提取文件名
  165. const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
  166. if (filenameMatch && filenameMatch[1]) {
  167. let extractedFilename = filenameMatch[1].replace(/['"]/g, '');
  168. // 处理 URL 编码的文件名
  169. try {
  170. extractedFilename = decodeURIComponent(extractedFilename);
  171. } catch (e) {
  172. // 如果解码失败,使用原始文件名
  173. }
  174. if (extractedFilename) {
  175. finalFilename = extractedFilename;
  176. }
  177. }
  178. }
  179. link.download = finalFilename;
  180. document.body.appendChild(link);
  181. link.click();
  182. document.body.removeChild(link);
  183. window.URL.revokeObjectURL(downloadUrl);
  184. ElMessage.success('导出成功');
  185. return Promise.resolve();
  186. } else {
  187. // 如果不是成功的文件响应,说明返回的是错误信息
  188. let errorData;
  189. try {
  190. // 尝试解析 Blob 中的 JSON 错误信息
  191. const text = await response.data.text();
  192. errorData = JSON.parse(text);
  193. } catch (parseError) {
  194. // 如果解析失败,使用默认错误信息
  195. errorData = { code: '500', message: '导出失败' };
  196. }
  197. // 检查返回的 code
  198. // if (errorData.code !== '200') {
  199. // const errorMessage = errorData.message || '导出失败';
  200. // ElMessage.error(errorMessage);
  201. // return Promise.reject(new Error(errorMessage));
  202. // }
  203. }
  204. } catch (error) {
  205. console.error('导出失败:', error);
  206. // 如果是网络错误或其他异常
  207. if (error.response) {
  208. // 有响应但状态码不是 2xx
  209. const errorMessage = error.response.data?.message || '服务器错误';
  210. ElMessage.error(errorMessage);
  211. } else if (error.message?.includes('权限不足')) {
  212. // 权限错误已经在上面处理了,不需要重复提示
  213. } else {
  214. // 其他错误
  215. ElMessage.error('导出失败,请稍后重试');
  216. }
  217. return Promise.reject(error);
  218. }
  219. };
  220. // add, list, delete, edit, detail, select, recyclebin, restore, destroy, report