AchPatentForm.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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. <CommonSection title="基本信息" :isFirst="true">
  7. <CommonInfoRow label="专利名称" :value="form.patentName" />
  8. <CommonInfoRow label="所属部门" :value="form.deptName" />
  9. <CommonInfoRow label="代理公司" :value="form.companyName" />
  10. <CommonInfoRow label="专利类型" :value="getDictLabel('sci_patent_class', form.patentClass)" />
  11. <CommonInfoRow label="专利范围" :value="getDictLabel('sci_patent_scope', form.patentScope)" />
  12. <CommonInfoRow label="专利状态" :value="getDictLabel('sci_patent_condition', form.patentCondition)" />
  13. <CommonInfoRow label="专利(申请)号" :value="form.patentNumber" />
  14. <CommonInfoRow label="申请人" :value="form.applicantName" />
  15. <CommonInfoRow label="第几参与人" :value="form.patentApplicationCode" />
  16. <CommonInfoRow label="申请日期" :value="formatDate(form.patentApplicationDate)" />
  17. <CommonInfoRow label="分类号" :value="form.classNum" />
  18. <CommonInfoRow label="公开号" :value="form.patentPublicCode" />
  19. <CommonInfoRow label="公开日期" :value="formatDate(form.patentPublicDate)" />
  20. <CommonInfoRow label="授权号" :value="form.patentAccreditCode" />
  21. <CommonInfoRow label="授权日期" :value="formatDate(form.effectiveTime)" />
  22. <CommonInfoRow label="署名" :value="getDictLabel('sci_circuit_signature', form.patentSchoolSignature)
  23. " />
  24. <CommonInfoRow label="专利权人" :value="form.patentObligee" />
  25. <CommonInfoRow label="专利代理机构" :value="form.entrustUnit" />
  26. <CommonInfoRow label="代理人" :value="form.agencyUserName" />
  27. <CommonInfoRow label="代理人联系电话" :value="form.agencyPhone" />
  28. <CommonInfoRow label="所属年份" :value="form.patentYeat ? formatDate(form.patentYeat, 'YYYY') : '-'" />
  29. <CommonInfoRow label="所属平台" isColumn>
  30. <view class="platform-tags">
  31. <template v-if="platformList.length > 0">
  32. <uv-tags v-for="(p, index) in platformList" :key="index" :text="p.platformName === '其他'
  33. ? '其他'
  34. : p.platformType
  35. ? p.platformName + ' (' + p.platformType + ')'
  36. : p.platformName
  37. " type="primary" plain size="mini" class="mr5"></uv-tags>
  38. </template>
  39. <text v-else>-</text>
  40. </view>
  41. </CommonInfoRow>
  42. <CommonInfoRow label="所属团队" :value="form.belongTeam || '-'" />
  43. <CommonInfoRow label="成果权属" :value="getDictLabel('achievement_owner_unit', form.achievementOwnerUnit)" />
  44. <CommonInfoRow label="是否失效" :value="form.patentEffectiveness === '10' ? '是' : form.patentEffectiveness === '20' ? '否' : '-'" />
  45. <CommonInfoRow label="是否职务专利" :value="form.patentOffice === '10' ? '是' : form.patentOffice === '20' ? '否' : '-'" />
  46. <CommonInfoRow label="是否为PCT专利" :value="form.patentCt === '10' ? '是' : form.patentCt === '20' ? '否' : '-'" />
  47. <CommonInfoRow label="专利附件" isColumn>
  48. <view class="file-links">
  49. <template v-if="patentFileList.length">
  50. <view class="file-item" v-for="(item, index) in patentFileList" :key="'patent-' + index" @click="handlePreview(item)">
  51. <text class="file-link">{{ item.name || item.fileName || '-' }}</text>
  52. </view>
  53. </template>
  54. <text v-else>-</text>
  55. </view>
  56. </CommonInfoRow>
  57. <CommonInfoRow label="专利委托合同附件" isColumn>
  58. <view class="file-links">
  59. <template v-if="agencyContractFileList.length">
  60. <view class="file-item" v-for="(item, index) in agencyContractFileList" :key="'agency-' + index" @click="handlePreview(item)">
  61. <text class="file-link">{{ item.name || item.fileName || '-' }}</text>
  62. </view>
  63. </template>
  64. <text v-else>-</text>
  65. </view>
  66. </CommonInfoRow>
  67. <CommonInfoRow label="专利简介" :value="form.patentDesc" isColumn />
  68. </CommonSection>
  69. <!-- 转化信息 -->
  70. <CommonSection title="转化信息" v-if="['60', '70', '80'].includes(form.patentStatus)">
  71. <CommonInfoRow label="转化时间" :value="formatDate(form.invertTime)" />
  72. <CommonInfoRow label="转化金额" :value="form.invertAmount || '-'" isAmount />
  73. <CommonInfoRow label="转化单位" :value="form.invertUnit || '-'" />
  74. <CommonInfoRow label="知识产权交易类型" :value="form.tradeType === '10' ? '转让' : form.tradeType === '20' ? '许可' : '-'" />
  75. <CommonInfoRow label="转化附件" isColumn>
  76. <view class="file-links">
  77. <template v-if="invertFileList.length">
  78. <view class="file-item" v-for="(item, index) in invertFileList" :key="'invert-' + index" @click="handlePreview(item)">
  79. <text class="file-link">{{ item.name || item.fileName || '-' }}</text>
  80. </view>
  81. </template>
  82. <text v-else>-</text>
  83. </view>
  84. </CommonInfoRow>
  85. </CommonSection>
  86. <!-- 发明(设计)人信息 -->
  87. <CommonSection title="发明(设计)人信息" v-if="form.memberList?.length">
  88. <view class="member-list">
  89. <view class="member-item" v-for="(member, index) in form.memberList" :key="index">
  90. <view class="member-avatar">{{ Number(index) + 1 }}</view>
  91. <view class="member-body">
  92. <view class="member-header">
  93. <text class="name">{{ member.memberName }}</text>
  94. <text class="badge" :class="'badge-' + getMemberBadgeType(member.memberType)">
  95. {{ getDictLabel("sci_paper_member_type", member.memberType) }}
  96. </text>
  97. </view>
  98. <view class="member-tags">
  99. <text class="tag" v-if="member.degree">{{
  100. getDictLabel("sci_academic_degree", member.degree)
  101. }}</text>
  102. <text class="tag" v-if="member.education">{{
  103. getDictLabel("sci_education", member.education)
  104. }}</text>
  105. <text class="tag" v-if="member.jobTitle">{{
  106. getDictLabel("sci_discipline_job_title", member.jobTitle)
  107. }}</text>
  108. </view>
  109. <view class="member-meta">
  110. <view class="meta-item" v-if="member.order">
  111. <text class="meta-label">排名</text>
  112. <text class="meta-value">{{ member.order }}</text>
  113. </view>
  114. <view class="meta-item" v-if="member.contributionRate">
  115. <text class="meta-label">贡献率</text>
  116. <text class="meta-value">{{ member.contributionRate }}%</text>
  117. </view>
  118. </view>
  119. <view class="member-dept" v-if="member.deptName">
  120. <uv-icon name="home" size="24rpx" color="#909399"></uv-icon>
  121. <text>{{ member.deptName }}</text>
  122. </view>
  123. </view>
  124. </view>
  125. </view>
  126. </CommonSection>
  127. <!-- 标注经济来源 -->
  128. <CommonSection title="标注经济来源" v-if="form.projList?.length">
  129. <view class="achievement-card" v-for="(row, index) in form.projList" :key="index">
  130. <view class="a-row">
  131. <text class="al">关联类型:</text>
  132. <text class="av">{{
  133. row.sourceType === "10" ? "项目" : "学科"
  134. }}</text>
  135. </view>
  136. <view class="a-row">
  137. <text class="al">项目/学科:</text>
  138. <text class="av">{{ row.projectSource || "-" }}</text>
  139. </view>
  140. </view>
  141. </CommonSection>
  142. <!-- 审批信息 -->
  143. <CommonSection title="审批信息">
  144. <FlowTable :id="form.id" :businessCode="'学术专利-' + form.patentCode" defCode="sci_academic_achievement" />
  145. </CommonSection>
  146. <!-- 授权审核进度 -->
  147. <CommonSection title="授权审核进度">
  148. <FlowTable :id="form.id" :businessCode="'学术专利授权-' + form.patentCode" defCode="sci_academic_achievement_auth" />
  149. </CommonSection>
  150. <!-- 转化审批信息 -->
  151. <CommonSection title="转化审批信息">
  152. <FlowTable :id="form.id" :businessCode="'专利转化-' + form.patentCode" defCode="sci_academic_achievement" />
  153. </CommonSection>
  154. </template>
  155. <uv-empty v-else mode="data" text="暂无数据"></uv-empty>
  156. </view>
  157. </template>
  158. <script setup lang="ts">
  159. import { ref, onMounted, watch, computed } from "vue";
  160. import { useDict } from "@/hooks/useDict";
  161. import { useDocumentApi } from "@/api/document";
  162. import { formatDate } from "@/utils/date";
  163. import { previewFile } from "@/utils/file";
  164. import to from "await-to-js";
  165. import FlowTable from "@/pages/project/components/detail/FlowTable.vue";
  166. import CommonSection from "@/components/ui/CommonSection.vue";
  167. import CommonInfoRow from "@/components/ui/CommonInfoRow.vue";
  168. const props = defineProps<{
  169. code: string;
  170. }>();
  171. const { getDictLabel } = useDict(
  172. "sci_patent_class",
  173. "sci_patent_scope",
  174. "sci_patent_condition",
  175. "sci_circuit_signature",
  176. "sci_cooperation",
  177. "achievement_owner_unit",
  178. "sci_paper_member_type",
  179. "sci_academic_degree",
  180. "sci_education",
  181. "sci_discipline_job_title"
  182. );
  183. const documentApi = useDocumentApi();
  184. const form = ref<any>(null);
  185. const loading = ref(false);
  186. const platformList = computed(() => {
  187. if (form.value?.belongPlatform) {
  188. try {
  189. const data = JSON.parse(form.value.belongPlatform);
  190. return Array.isArray(data) ? data : [];
  191. } catch (e) {
  192. if (form.value.belongPlatform === "其他")
  193. return [{ platformName: "其他" }];
  194. return [{ platformName: form.value.belongPlatform }];
  195. }
  196. }
  197. return [];
  198. });
  199. const parseFileList = (fileStr: string) => {
  200. if (!fileStr) return [];
  201. try {
  202. const files = JSON.parse(fileStr);
  203. return Array.isArray(files) ? files : [files];
  204. } catch (e) {
  205. return [];
  206. }
  207. };
  208. const patentFileList = computed(() => parseFileList(form.value?.patentFile));
  209. const agencyContractFileList = computed(() => parseFileList(form.value?.agencyContractFile));
  210. const invertFileList = computed(() => parseFileList(form.value?.invertUrl));
  211. const handlePreview = (file: any) => {
  212. const url = file.fileUrl || file.url;
  213. const name = file.fileName || file.name;
  214. if (url) {
  215. previewFile(url, name);
  216. }
  217. };
  218. const fetchData = async () => {
  219. if (!props.code) return;
  220. loading.value = true;
  221. const patentCode = props.code.includes("-")
  222. ? props.code.split("-")[1]
  223. : props.code;
  224. const [err, res] = await to(documentApi.getPatentByCode(patentCode));
  225. if (!err && res?.data) {
  226. form.value = res.data;
  227. }
  228. loading.value = false;
  229. };
  230. onMounted(() => {
  231. fetchData();
  232. });
  233. watch(
  234. () => props.code,
  235. () => {
  236. fetchData();
  237. }
  238. );
  239. const getMemberBadgeType = (type: string) => {
  240. const map: Record<string, string> = {
  241. "10": "primary",
  242. "20": "success",
  243. "30": "warning",
  244. };
  245. return map[type] || "default";
  246. };
  247. </script>
  248. <style lang="scss" scoped>
  249. @import "./common.scss";
  250. .member-list {
  251. .member-item {
  252. display: flex;
  253. align-items: flex-start;
  254. background: #fff;
  255. border: 2rpx solid #f0f2f5;
  256. border-radius: 16rpx;
  257. padding: 24rpx;
  258. margin-bottom: 20rpx;
  259. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04);
  260. &:last-child {
  261. margin-bottom: 0;
  262. }
  263. }
  264. .member-avatar {
  265. width: 56rpx;
  266. height: 56rpx;
  267. background: linear-gradient(135deg, #2979ff 0%, #1a66ff 100%);
  268. color: #fff;
  269. border-radius: 50%;
  270. display: flex;
  271. align-items: center;
  272. justify-content: center;
  273. font-size: 26rpx;
  274. font-weight: 600;
  275. flex-shrink: 0;
  276. margin-right: 20rpx;
  277. }
  278. .member-body {
  279. flex: 1;
  280. min-width: 0;
  281. }
  282. .member-header {
  283. display: flex;
  284. align-items: center;
  285. margin-bottom: 16rpx;
  286. flex-wrap: wrap;
  287. .name {
  288. font-size: 32rpx;
  289. font-weight: 600;
  290. color: #1a1a1a;
  291. margin-right: 16rpx;
  292. }
  293. .badge {
  294. font-size: 22rpx;
  295. padding: 4rpx 16rpx;
  296. border-radius: 20rpx;
  297. color: #fff;
  298. }
  299. .badge-primary {
  300. background: #2979ff;
  301. }
  302. .badge-success {
  303. background: #19be6b;
  304. }
  305. .badge-warning {
  306. background: #ff9900;
  307. }
  308. .badge-default {
  309. background: #909399;
  310. }
  311. }
  312. .member-tags {
  313. display: flex;
  314. flex-wrap: wrap;
  315. margin-bottom: 16rpx;
  316. .tag {
  317. font-size: 24rpx;
  318. color: #606266;
  319. background: #f4f4f5;
  320. padding: 6rpx 16rpx;
  321. border-radius: 8rpx;
  322. margin-right: 12rpx;
  323. margin-bottom: 8rpx;
  324. &:last-child {
  325. margin-right: 0;
  326. }
  327. }
  328. }
  329. .member-meta {
  330. display: flex;
  331. flex-wrap: wrap;
  332. margin-bottom: 12rpx;
  333. .meta-item {
  334. display: flex;
  335. align-items: center;
  336. margin-right: 32rpx;
  337. margin-bottom: 8rpx;
  338. &:last-child {
  339. margin-right: 0;
  340. }
  341. .meta-label {
  342. font-size: 24rpx;
  343. color: #909399;
  344. margin-right: 8rpx;
  345. }
  346. .meta-value {
  347. font-size: 24rpx;
  348. color: #606266;
  349. font-weight: 500;
  350. }
  351. }
  352. }
  353. .member-dept {
  354. display: flex;
  355. align-items: center;
  356. font-size: 24rpx;
  357. color: #909399;
  358. text {
  359. margin-left: 8rpx;
  360. }
  361. }
  362. }
  363. .file-links {
  364. .file-item {
  365. padding: 6rpx 0;
  366. }
  367. .file-link {
  368. color: #2979ff;
  369. text-decoration: underline;
  370. word-break: break-all;
  371. }
  372. }
  373. </style>