project.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. import { defineStore } from 'pinia';
  2. import { ref } from 'vue';
  3. import { useProjectApi } from '@/api/project/index';
  4. import to from 'await-to-js';
  5. const projectApi = useProjectApi();
  6. export const useProjectStore = defineStore('project', () => {
  7. // ==================== State ====================
  8. // 定义三种类型的列表字典和分页结构
  9. const verticalList = ref<any[]>([]);
  10. const verticalPagination = ref({ pageNum: 1, pageSize: 25, total: 0 });
  11. const horizontalList = ref<any[]>([]);
  12. const horizontalPagination = ref({ pageNum: 1, pageSize: 25, total: 0 });
  13. const spontaneityList = ref<any[]>([]);
  14. const spontaneityPagination = ref({ pageNum: 1, pageSize: 25, total: 0 });
  15. const changeList = ref<any[]>([]);
  16. // 变更详情数据
  17. const changeDetailData = ref<any>({});
  18. const projectBaseData = ref<any>({});
  19. const projectDetailData = ref<any>({});
  20. const changePreData = ref<any>(null);
  21. const changeAfterData = ref<any>(null);
  22. // 中检任务数据
  23. const inspList = ref<any[]>([]);
  24. // 单条中检详情数据
  25. const inspDetailData = ref<any>({});
  26. // 科研成果数据
  27. const paperData = ref<any[]>([]);
  28. const workData = ref<any[]>([]);
  29. const patentData = ref<any[]>([]);
  30. const awardData = ref<any[]>([]);
  31. // 结题数据
  32. const conclusionList = ref<any[]>([]);
  33. // 经费数据
  34. const claimdData = ref<any[]>([]);
  35. const outBoundData = ref<any[]>([]);
  36. const rebateData = ref<any[]>([]);
  37. const fundsData = ref<any>({});
  38. const loadings = {
  39. fetchVerticalLoading: ref<boolean>(false),
  40. fetchHorizontalLoading: ref<boolean>(false),
  41. fetchSpontaneityLoading: ref<boolean>(false),
  42. fetchChangeListLoading: ref<boolean>(false),
  43. fetchChangeDetailLoading: ref<boolean>(false),
  44. fetchInspListLoading: ref<boolean>(false),
  45. fetchAchievementLoading: ref<boolean>(false),
  46. fetchConclusionLoading: ref<boolean>(false),
  47. fetchFundingLoading: ref<boolean>(false)
  48. } as const;
  49. /**
  50. * 设置加载状态
  51. */
  52. function setRequestLoading(field: keyof typeof loadings, value: boolean) {
  53. loadings[field].value = value;
  54. }
  55. // ==================== Actions ====================
  56. /**
  57. * 获取纵向项目列表
  58. * @param queryParams 外部额外传入的查询条件
  59. * @param reset 是否重置页码刷新
  60. */
  61. async function fetchVerticalProjects(queryParams: any = {}, reset = false) {
  62. if (reset) {
  63. verticalPagination.value.pageNum = 1;
  64. verticalList.value = [];
  65. }
  66. setRequestLoading('fetchVerticalLoading', true);
  67. const params = {
  68. pageNum: verticalPagination.value.pageNum,
  69. pageSize: verticalPagination.value.pageSize,
  70. ...queryParams
  71. };
  72. const [err, res] = await to(projectApi.getVerticalList(params)) as [any, any];
  73. setRequestLoading('fetchVerticalLoading', false);
  74. if (err) {
  75. console.error('fetchVerticalProjects error:', err);
  76. return { success: false, rows: [], total: 0 };
  77. }
  78. if (res && res.code === 200) {
  79. let rows = res.data?.list || res.data?.rows || res.rows || (Array.isArray(res.data) ? res.data : []);
  80. if (!Array.isArray(rows)) rows = [];
  81. const total = res.data?.total || res.total || 0;
  82. verticalList.value = reset ? rows : [...verticalList.value, ...rows];
  83. verticalPagination.value.total = total;
  84. return { success: true, rows, total };
  85. }
  86. return { success: false, rows: [], total: 0 };
  87. }
  88. /**
  89. * 纵向列表加载下一页
  90. */
  91. async function loadMoreVerticalProjects(queryParams: any = {}) {
  92. verticalPagination.value.pageNum++;
  93. return await fetchVerticalProjects(queryParams, false);
  94. }
  95. /**
  96. * 获取横向项目列表
  97. */
  98. async function fetchHorizontalProjects(queryParams: any = {}, reset = false) {
  99. if (reset) {
  100. horizontalPagination.value.pageNum = 1;
  101. horizontalList.value = [];
  102. }
  103. setRequestLoading('fetchHorizontalLoading', true);
  104. const params = {
  105. pageNum: horizontalPagination.value.pageNum,
  106. pageSize: horizontalPagination.value.pageSize,
  107. ...queryParams
  108. };
  109. const [err, res] = await to(projectApi.getHorizontalList(params)) as [any, any];
  110. setRequestLoading('fetchHorizontalLoading', false);
  111. if (err) {
  112. console.error('fetchHorizontalProjects error:', err);
  113. return { success: false, rows: [], total: 0 };
  114. }
  115. if (res && res.code === 200) {
  116. let rows = res.data?.list || res.data?.rows || res.rows || (Array.isArray(res.data) ? res.data : []);
  117. if (!Array.isArray(rows)) rows = [];
  118. const total = res.data?.total || res.total || 0;
  119. horizontalList.value = reset ? rows : [...horizontalList.value, ...rows];
  120. horizontalPagination.value.total = total;
  121. return { success: true, rows, total };
  122. }
  123. return { success: false, rows: [], total: 0 };
  124. }
  125. async function loadMoreHorizontalProjects(queryParams: any = {}) {
  126. horizontalPagination.value.pageNum++;
  127. return await fetchHorizontalProjects(queryParams, false);
  128. }
  129. /**
  130. * 获取校内项目列表
  131. */
  132. async function fetchSpontaneityProjects(queryParams: any = {}, reset = false) {
  133. if (reset) {
  134. spontaneityPagination.value.pageNum = 1;
  135. spontaneityList.value = [];
  136. }
  137. setRequestLoading('fetchSpontaneityLoading', true);
  138. const params = {
  139. pageNum: spontaneityPagination.value.pageNum,
  140. pageSize: spontaneityPagination.value.pageSize,
  141. ...queryParams
  142. };
  143. const [err, res] = await to(projectApi.getSpontaneityList(params)) as [any, any];
  144. setRequestLoading('fetchSpontaneityLoading', false);
  145. if (err) {
  146. console.error('fetchSpontaneityProjects error:', err);
  147. return { success: false, rows: [], total: 0 };
  148. }
  149. if (res && res.code === 200) {
  150. let rows = res.data?.list || res.data?.rows || res.rows || (Array.isArray(res.data) ? res.data : []);
  151. if (!Array.isArray(rows)) rows = [];
  152. const total = res.data?.total || res.total || 0;
  153. spontaneityList.value = reset ? rows : [...spontaneityList.value, ...rows];
  154. spontaneityPagination.value.total = total;
  155. return { success: true, rows, total };
  156. }
  157. return { success: false, rows: [], total: 0 };
  158. }
  159. async function loadMoreSpontaneityProjects(queryParams: any = {}) {
  160. spontaneityPagination.value.pageNum++;
  161. return await fetchSpontaneityProjects(queryParams, false);
  162. }
  163. /**
  164. * 获取项目变更列表
  165. */
  166. async function fetchProjectChanges(projectId: number, projectType: string = '10') {
  167. setRequestLoading('fetchChangeListLoading', true);
  168. let typeCode = projectType;
  169. if (projectType === 'vertical') typeCode = '10';
  170. if (projectType === 'horizontal') typeCode = '20';
  171. if (projectType === 'spontaneity') typeCode = '30';
  172. const params = {
  173. projectId: projectId,
  174. projectType: typeCode,
  175. noPage: true
  176. };
  177. const [err, res] = await to(projectApi.getProjectDetailsList(params)) as [any, any];
  178. setRequestLoading('fetchChangeListLoading', false);
  179. if (err) {
  180. console.error('fetchProjectChanges error:', err);
  181. changeList.value = [];
  182. return { success: false };
  183. }
  184. if (res && res.code === 200) {
  185. changeList.value = res.data || [];
  186. return { success: true };
  187. }
  188. changeList.value = [];
  189. return { success: false };
  190. }
  191. /**
  192. * 获取项目中检列表
  193. */
  194. async function fetchInspList(projectId: number, projectType: string = '10') {
  195. setRequestLoading('fetchInspListLoading', true);
  196. let typeCode = projectType;
  197. if (projectType === 'vertical') typeCode = '10';
  198. if (projectType === 'horizontal') typeCode = '20';
  199. if (projectType === 'spontaneity') typeCode = '30';
  200. const params = {
  201. projectId: projectId,
  202. projectType: typeCode,
  203. pageSize: 9999
  204. };
  205. const [err, res] = await to(projectApi.getTaskList(params)) as [any, any];
  206. setRequestLoading('fetchInspListLoading', false);
  207. if (err) {
  208. console.error('fetchInspList error:', err);
  209. inspList.value = [];
  210. return { success: false };
  211. }
  212. if (res && res.code === 200) {
  213. inspList.value = res.data?.list || [];
  214. return { success: true };
  215. }
  216. inspList.value = [];
  217. return { success: false };
  218. }
  219. /**
  220. * 获取项目中检详情
  221. */
  222. async function fetchInspDetail(taskId: number, operatorType?: string, projectType?: string) {
  223. setRequestLoading('fetchInspListLoading', true);
  224. inspDetailData.value = {};
  225. let apiCall = projectApi.getTaskDetail({ id: taskId, type: operatorType == '10' ? '10' : '' });
  226. if (projectType === 'horizontal' || projectType === '20') {
  227. apiCall = projectApi.getHorizontalInspTaskDetail({ id: taskId, type: operatorType == '10' ? '10' : '' });
  228. }
  229. const [err, res] = await to(apiCall) as [any, any];
  230. setRequestLoading('fetchInspListLoading', false);
  231. if (!err && res?.code === 200) {
  232. inspDetailData.value = res.data || {};
  233. return { success: true };
  234. }
  235. return { success: false };
  236. }
  237. /**
  238. * 获取项目变更的具体详情
  239. * 这个方法统筹了获取变更信息以及对应的主项目信息
  240. */
  241. async function fetchProjectChangeDetail(changeId: number, projectType: string, projectId: number) {
  242. setRequestLoading('fetchChangeDetailLoading', true);
  243. // 1. 获取变更单详细信息
  244. const [errChange, resChange] = await to(projectApi.getProjectChangeDetail({ id: changeId })) as [any, any];
  245. // 2. 获取对应的原项目详细信息
  246. let baseApiCall = null;
  247. if (projectType === '10' || projectType === 'vertical') {
  248. baseApiCall = projectApi.getVerticalEntityById({ id: projectId });
  249. } else if (projectType === '20' || projectType === 'horizontal') {
  250. baseApiCall = projectApi.getHoriEntityById({ id: projectId });
  251. } else if (projectType === '30' || projectType === 'spontaneity') {
  252. baseApiCall = projectApi.getSpontaneityEntityById({ id: projectId });
  253. }
  254. if (baseApiCall) {
  255. const [errBase, resBase] = await to(baseApiCall) as [any, any];
  256. if (!errBase && resBase?.code === 200) {
  257. projectBaseData.value = resBase.data || {};
  258. } else {
  259. projectBaseData.value = {};
  260. }
  261. }
  262. // 3. 业务数据填充和反序列化解析(无需在页面写JSON.parse)
  263. if (!errChange && resChange?.code === 200) {
  264. const data = resChange.data || {};
  265. changeDetailData.value = data;
  266. try {
  267. changePreData.value = data.changeDataPre ? JSON.parse(data.changeDataPre) : null;
  268. } catch {
  269. changePreData.value = null;
  270. }
  271. try {
  272. changeAfterData.value = data.changeDataAfter ? JSON.parse(data.changeDataAfter) : null;
  273. } catch {
  274. changeAfterData.value = null;
  275. }
  276. } else {
  277. changeDetailData.value = {};
  278. changePreData.value = null;
  279. changeAfterData.value = null;
  280. }
  281. setRequestLoading('fetchChangeDetailLoading', false);
  282. }
  283. /**
  284. * 获取项目及相关详细信息 (主数据详情)
  285. * 针对纵向、横向、校园项目类型,分发到不同的 API 接口
  286. */
  287. async function fetchProjectDetailData(id: number, type: string) {
  288. projectDetailData.value = {};
  289. let apiCall = null;
  290. if (type === 'vertical' || type === '10') {
  291. apiCall = projectApi.getVerticalEntityById({ id });
  292. } else if (type === 'horizontal' || type === '20') {
  293. apiCall = projectApi.getHoriEntityById({ id });
  294. } else if (type === 'spontaneity' || type === '30') {
  295. apiCall = projectApi.getSpontaneityEntityById({ id });
  296. }
  297. if (!apiCall) return { success: false, msg: '未知项目类型' };
  298. const [err, res] = await to(apiCall) as [any, any];
  299. if (!err && res?.code === 200) {
  300. projectDetailData.value = res.data || {};
  301. return { success: true };
  302. }
  303. return { success: false };
  304. }
  305. /**
  306. * 获取项目科研成果数据
  307. */
  308. async function fetchAchievements(projectCode: string, projectType: string = '10') {
  309. setRequestLoading('fetchAchievementLoading', true);
  310. let typeCode = projectType;
  311. if (projectType === 'vertical') typeCode = '10';
  312. if (projectType === 'horizontal') typeCode = '20';
  313. if (projectType === 'spontaneity') typeCode = '30';
  314. const query = { projectCode, projectType: typeCode };
  315. Promise.all([
  316. projectApi.getPaperByProject(query),
  317. projectApi.getWorkByProject(query),
  318. projectApi.getPatentByProject(query),
  319. projectApi.getAwardsByProject(query)
  320. ]).then(([paper, work, patent, awards]: any) => {
  321. paperData.value = paper?.data || [];
  322. workData.value = work?.data || [];
  323. patentData.value = patent?.data || [];
  324. awardData.value = awards?.data || [];
  325. }).catch(err => {
  326. console.error('fetchAchievements error:', err);
  327. }).finally(() => {
  328. setRequestLoading('fetchAchievementLoading', false);
  329. });
  330. }
  331. /**
  332. * 获取结题信息
  333. */
  334. async function fetchConclusion(projectId: number, projectType: string = '10') {
  335. setRequestLoading('fetchConclusionLoading', true);
  336. let typeCode = projectType;
  337. if (projectType === 'vertical') typeCode = '10';
  338. if (projectType === 'horizontal') typeCode = '20';
  339. if (projectType === 'spontaneity') typeCode = '30';
  340. const [err, res] = await to(projectApi.getConclusionTaskList({ projectId, projectType: typeCode, pageSize: 9999 })) as [any, any];
  341. setRequestLoading('fetchConclusionLoading', false);
  342. if (!err && res?.code === 200) {
  343. conclusionList.value = res.data?.list || [];
  344. } else {
  345. conclusionList.value = [];
  346. }
  347. }
  348. /**
  349. * 获取经费信息
  350. */
  351. async function fetchFunding(projectId: number, projectType: string = '10') {
  352. setRequestLoading('fetchFundingLoading', true);
  353. let typeCode = projectType;
  354. if (projectType === 'vertical') typeCode = '10';
  355. if (projectType === 'horizontal') typeCode = '20';
  356. if (projectType === 'spontaneity') typeCode = '30';
  357. const params = { projectId, projectType: typeCode, pageSize: 9999 };
  358. Promise.all([
  359. projectApi.getRecordList(params),
  360. projectApi.getOutBoundList(params),
  361. projectApi.getExpenseList(params),
  362. projectApi.getFundsStatistics({ projectId, projectType: typeCode })
  363. ]).then(([claimd, outbound, rebate, statistics]: any) => {
  364. claimdData.value = claimd?.data?.list || [];
  365. outBoundData.value = outbound?.data?.list || [];
  366. rebateData.value = rebate?.data?.list || [];
  367. fundsData.value = statistics?.data || {};
  368. }).catch(err => {
  369. console.error('fetchFunding error:', err);
  370. }).finally(() => {
  371. setRequestLoading('fetchFundingLoading', false);
  372. });
  373. }
  374. return {
  375. ...loadings,
  376. verticalList,
  377. verticalPagination,
  378. horizontalList,
  379. horizontalPagination,
  380. spontaneityList,
  381. spontaneityPagination,
  382. fetchVerticalProjects,
  383. loadMoreVerticalProjects,
  384. fetchHorizontalProjects,
  385. loadMoreHorizontalProjects,
  386. fetchSpontaneityProjects,
  387. loadMoreSpontaneityProjects,
  388. changeList,
  389. changeDetailData,
  390. projectDetailData,
  391. projectBaseData,
  392. changePreData,
  393. changeAfterData,
  394. inspList,
  395. inspDetailData,
  396. paperData,
  397. workData,
  398. patentData,
  399. awardData,
  400. conclusionList,
  401. claimdData,
  402. outBoundData,
  403. rebateData,
  404. fundsData,
  405. fetchProjectChanges,
  406. fetchProjectChangeDetail,
  407. fetchProjectDetailData,
  408. fetchInspList,
  409. fetchInspDetail,
  410. fetchAchievements,
  411. fetchConclusion,
  412. fetchFunding
  413. };
  414. });