ProjectChange.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <template>
  2. <view class="document-form">
  3. <uv-loading-icon v-if="loading" mode="circle" text="正在加载项目变更详情..."></uv-loading-icon>
  4. <template v-else-if="form">
  5. <!-- 基本信息 -->
  6. <view class="common-section-card">
  7. <view class="section-title">基本信息</view>
  8. <view class="info-row">
  9. <text class="label">项目名称</text>
  10. <text class="value">{{ form.projectTitle || '-' }}</text>
  11. </view>
  12. <view class="info-row">
  13. <text class="label">变更类型</text>
  14. <text class="value">{{ getChangeType(form.changeType) }}</text>
  15. </view>
  16. <template v-if="form.projectType === '10' || form.projectType === '30'">
  17. <view class="info-row">
  18. <text class="label">项目分类</text>
  19. <text class="value">{{ form.projectClazzName || '-' }}</text>
  20. </view>
  21. </template>
  22. <template v-if="form.projectType === '20'">
  23. <view class="info-row">
  24. <text class="label">研究类型</text>
  25. <text class="value">{{ getDictLabel('sci_pjt_type', form.isClinicalTrial) || '-' }}</text>
  26. </view>
  27. </template>
  28. <view class="info-row">
  29. <text class="label">项目负责人</text>
  30. <text class="value">{{ form.projectLeaderName || form.projectHeadName || '-' }}</text>
  31. </view>
  32. <view class="info-row" v-if="form.changeType === '50'">
  33. <text class="label">中止时间</text>
  34. <text class="value">{{ formatDate(form.abortDate, 'YYYY-MM-DD') || '-' }}</text>
  35. </view>
  36. <view class="info-row">
  37. <text class="label">变更原因</text>
  38. <text class="value">{{ form.changeReason || '-' }}</text>
  39. </view>
  40. <view class="info-row" v-if="form.evidence">
  41. <text class="label">证明材料</text>
  42. <text class="value" style="color: #1c9bfd;" @click="previewFile(form.evidence, '证明材料')">查看附件</text>
  43. </view>
  44. </view>
  45. <!-- 变更前信息 -->
  46. <view class="common-section-card mt20" v-if="form.changeType !== '50'">
  47. <view class="section-title">变更前信息</view>
  48. <!-- 成员变更 -->
  49. <template v-if="form.changeType === '10'">
  50. <view class="member-list">
  51. <view class="member-item" v-for="(member, index) in changeDataPre" :key="index">
  52. <view class="member-header">
  53. <text class="m-name">{{ member.memberName }}</text>
  54. <text class="m-tag">{{ getRoleLabel(member.projectRole) }}</text>
  55. </view>
  56. <view class="m-body">
  57. <view class="m-line"><text class="l">工号:</text><text class="v">{{ member.memberNo || '-' }}</text>
  58. </view>
  59. <view class="m-line"><text class="l">职称:</text><text class="v">{{ member.technicalTitle || '-' }}</text>
  60. </view>
  61. <view class="m-line"><text class="l">学位:</text><text class="v">{{ getDegreeLabel(member.degree)
  62. }}</text></view>
  63. <view class="m-line"><text class="l">所属单位:</text><text class="v">{{ member.unit || '-' }}</text></view>
  64. <view class="m-line"><text class="l">成员类型:</text><text class="v">{{
  65. getMemberTypeLabel(member.memberType) }}</text></view>
  66. <view class="m-line"><text class="l">所属科室:</text><text class="v">{{ member.deptName || '-' }}</text>
  67. </view>
  68. <view class="m-line" v-if="member.responsibleContent"><text class="l">负责内容:</text><text class="v">{{
  69. member.responsibleContent }}</text></view>
  70. </view>
  71. </view>
  72. </view>
  73. </template>
  74. <!-- 延期变更 -->
  75. <template v-else-if="form.changeType === '30'">
  76. <view class="info-row">
  77. <text class="label">开始日期</text>
  78. <text class="value">{{ changeDataPre && changeDataPre[0] ? formatDate(changeDataPre[0], 'YYYY-MM-DD') : '-'
  79. }}</text>
  80. </view>
  81. <view class="info-row">
  82. <text class="label">结束日期</text>
  83. <text class="value">{{ changeDataPre && changeDataPre[1] ? formatDate(changeDataPre[1], 'YYYY-MM-DD') : '-'
  84. }}</text>
  85. </view>
  86. </template>
  87. <!-- 经费变更 -->
  88. <template v-else-if="form.changeType === '40'">
  89. <view class="info-row">
  90. <text class="label">{{ form.isClinicalTrial === '10' ? '科研经费(万元)' : '合同经费(万元)' }}</text>
  91. <text class="value red-color">{{ amountUnitTransfer(changeDataPre) }}</text>
  92. </view>
  93. </template>
  94. <!-- 经费调剂 -->
  95. <template v-else-if="form.changeType === '60'">
  96. <view class="funds-list">
  97. <view class="funds-item" v-for="(item, index) in changeDataPre" :key="index">
  98. <view class="f-row">
  99. <text class="f-name">{{ item.subjName || '-' }}</text>
  100. <text class="f-code">编号: {{ item.subjCode || '-' }}</text>
  101. </view>
  102. <view class="f-grid">
  103. <view class="g-item highlight">
  104. <text class="gl">剩余金额(元)</text>
  105. <text class="gv red-color">{{ item.balanceAmount }}</text>
  106. </view>
  107. </view>
  108. </view>
  109. </view>
  110. </template>
  111. </view>
  112. <!-- 变更后信息 -->
  113. <view class="common-section-card mt20" v-if="form.changeType !== '50'">
  114. <view class="section-title">变更后信息</view>
  115. <!-- 成员变更 -->
  116. <template v-if="form.changeType === '10'">
  117. <view class="member-list">
  118. <view class="member-item" v-for="(member, index) in changeDataAfter" :key="index">
  119. <view class="member-header">
  120. <text class="m-name">{{ member.memberName }}</text>
  121. <text class="m-tag">{{ getRoleLabel(member.projectRole) }}</text>
  122. </view>
  123. <view class="m-body">
  124. <view class="m-line"><text class="l">工号:</text><text class="v">{{ member.memberNo || '-' }}</text>
  125. </view>
  126. <view class="m-line"><text class="l">职称:</text><text class="v">{{ member.technicalTitle || '-' }}</text>
  127. </view>
  128. <view class="m-line"><text class="l">学位:</text><text class="v">{{ getDegreeLabel(member.degree)
  129. }}</text></view>
  130. <view class="m-line"><text class="l">所属单位:</text><text class="v">{{ member.unit || '-' }}</text></view>
  131. <view class="m-line"><text class="l">成员类型:</text><text class="v">{{
  132. getMemberTypeLabel(member.memberType) }}</text></view>
  133. <view class="m-line"><text class="l">所属科室:</text><text class="v">{{ member.deptName || '-' }}</text>
  134. </view>
  135. <view class="m-line" v-if="member.responsibleContent"><text class="l">负责内容:</text><text class="v">{{
  136. member.responsibleContent }}</text></view>
  137. </view>
  138. </view>
  139. </view>
  140. </template>
  141. <!-- 延期变更 -->
  142. <template v-else-if="form.changeType === '30'">
  143. <view class="info-row">
  144. <text class="label">开始日期</text>
  145. <text class="value">{{ changeDataAfter && changeDataAfter[0] ? formatDate(changeDataAfter[0], 'YYYY-MM-DD')
  146. : '-' }}</text>
  147. </view>
  148. <view class="info-row">
  149. <text class="label">结束日期</text>
  150. <text class="value">{{ changeDataAfter && changeDataAfter[1] ? formatDate(changeDataAfter[1], 'YYYY-MM-DD')
  151. : '-' }}</text>
  152. </view>
  153. </template>
  154. <!-- 经费变更 -->
  155. <template v-else-if="form.changeType === '40'">
  156. <view class="info-row">
  157. <text class="label">{{ form.isClinicalTrial === '10' ? '科研经费(万元)' : '合同经费(万元)' }}</text>
  158. <text class="value red-color">{{ amountUnitTransfer(changeDataAfter) }}</text>
  159. </view>
  160. </template>
  161. <!-- 经费调剂 -->
  162. <template v-else-if="form.changeType === '60'">
  163. <view class="funds-list">
  164. <view class="funds-item" v-for="(item, index) in changeDataAfter" :key="index">
  165. <view class="f-row">
  166. <text class="f-name">{{ item.subjName || '-' }}</text>
  167. <text class="f-code">编号: {{ item.subjCode || '-' }}</text>
  168. </view>
  169. <view class="f-grid">
  170. <view class="g-item highlight">
  171. <text class="gl">剩余金额(元)</text>
  172. <text class="gv red-color">{{ formatAmount(item.balanceAmount) }}</text>
  173. </view>
  174. </view>
  175. </view>
  176. </view>
  177. </template>
  178. </view>
  179. </template>
  180. <uv-empty v-else mode="data" text="暂无数据"></uv-empty>
  181. </view>
  182. </template>
  183. <script setup lang="ts">
  184. import { ref, onMounted, watch, computed } from 'vue';
  185. import { useDict } from '@/hooks/useDict';
  186. import { useDocumentApi } from '@/api/document';
  187. import { formatDate } from '@/utils/date';
  188. import { formatAmount } from '@/utils/format';
  189. import { previewFile } from '@/utils/file';
  190. import to from 'await-to-js';
  191. const props = defineProps<{
  192. code: string;
  193. }>();
  194. const { getDictLabel } = useDict('sci_pjt_level', 'sci_pjt_type');
  195. const documentApi = useDocumentApi();
  196. const form = ref<any>(null);
  197. const loading = ref(false);
  198. const changeDataPre = computed(() => {
  199. if (!form.value?.changeDataPre) return null;
  200. try {
  201. return JSON.parse(form.value.changeDataPre);
  202. } catch {
  203. return null;
  204. }
  205. });
  206. const changeDataAfter = computed(() => {
  207. if (!form.value?.changeDataAfter) return null;
  208. try {
  209. return JSON.parse(form.value.changeDataAfter);
  210. } catch {
  211. return null;
  212. }
  213. });
  214. const getChangeType = (type: string) => {
  215. const types: Record<string, string> = {
  216. '10': '成员变更',
  217. '20': '预算变更',
  218. '30': '延期变更',
  219. '40': '经费变更',
  220. '50': '中止变更',
  221. '60': '经费调剂'
  222. };
  223. return types[type] || '-';
  224. };
  225. const getRoleLabel = (role: string) => {
  226. const roles: Record<string, string> = {
  227. '10': '负责人',
  228. '20': '主要参与人',
  229. '30': '一般参与人'
  230. };
  231. return roles[role] || '-';
  232. };
  233. const getMemberTypeLabel = (type: string) => {
  234. const types: Record<string, string> = {
  235. '10': '本院人员',
  236. '20': '非本院人员',
  237. '30': '研究生'
  238. };
  239. return types[type] || '-';
  240. };
  241. const getDegreeLabel = (degree: string) => {
  242. const degrees: Record<string, string> = {
  243. '10': '无',
  244. '20': '研究生',
  245. '30': '博士生'
  246. };
  247. return degrees[degree] || '-';
  248. };
  249. const getTrialLabel = (trial: string) => {
  250. const trials: Record<string, string> = {
  251. '10': '临床试验',
  252. '1010': 'GCP',
  253. '1020': '非GCP',
  254. '20': '横向其他'
  255. };
  256. return trials[trial] || '-';
  257. };
  258. const amountUnitTransfer = (val: any) => {
  259. if (!val || isNaN(val)) return '0';
  260. return (Number(val) / 10000).toFixed(2);
  261. };
  262. const fetchData = async () => {
  263. if (!props.code) return;
  264. loading.value = true;
  265. const [err, res] = await to(documentApi.getProjectChangeByCode(props.code));
  266. if (!err && res?.data) {
  267. form.value = res.data;
  268. }
  269. loading.value = false;
  270. };
  271. onMounted(() => {
  272. fetchData();
  273. });
  274. watch(() => props.code, () => {
  275. fetchData();
  276. });
  277. </script>
  278. <style lang="scss" scoped>
  279. @import "./common.scss";
  280. .red-color {
  281. color: #ff4d4f;
  282. font-weight: bold;
  283. }
  284. .f-code {
  285. font-size: 24rpx;
  286. color: #999;
  287. margin-left: 10rpx;
  288. }
  289. .m-body {
  290. padding: 10rpx 0;
  291. .m-line {
  292. font-size: 26rpx;
  293. line-height: 1.6;
  294. display: flex;
  295. .l {
  296. color: #888;
  297. width: 140rpx;
  298. flex-shrink: 0;
  299. }
  300. .v {
  301. color: #333;
  302. }
  303. }
  304. }
  305. </style>