project.ts 17 KB

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