AchievementCard.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <template>
  2. <view class="achievement-card" @click="$emit('click')" hover-class="card-hover">
  3. <!-- 右上角角标状态 - 采用全局统一样式 -->
  4. <view class="status-tag" :class="statusInfo.type">
  5. {{ statusInfo.text }}
  6. </view>
  7. <view class="card-header">
  8. <view class="title-box">
  9. <text class="title">{{ title }}</text>
  10. </view>
  11. </view>
  12. <view class="card-content">
  13. <!-- 创建人 -->
  14. <view class="info-item" v-if="creator">
  15. <text class="label">创建人:</text>
  16. <text class="value">{{ creator }}</text>
  17. </view>
  18. <!-- 所属部门 -->
  19. <view class="info-item" v-if="org">
  20. <text class="label">所属部门:</text>
  21. <text class="value">{{ org }}</text>
  22. </view>
  23. <!-- 创建时间 -->
  24. <view class="info-item" v-if="createTime">
  25. <text class="label">创建时间:</text>
  26. <text class="value">{{ createTime }}</text>
  27. </view>
  28. </view>
  29. <view class="card-footer">
  30. <view class="type-pill" :class="'type-' + type">
  31. <uv-icon :name="typeIcon" size="12" :color="typeColor"></uv-icon>
  32. <text class="type-name" :style="{ color: typeColor }">{{ typeName }}</text>
  33. </view>
  34. <view class="click-tip">
  35. <text>查看详情</text>
  36. <uv-icon name="arrow-right" size="12" color="#cbd5e1"></uv-icon>
  37. </view>
  38. </view>
  39. </view>
  40. </template>
  41. <script setup lang="ts">
  42. import { computed } from 'vue';
  43. const props = defineProps<{
  44. item: any;
  45. type: string;
  46. }>();
  47. defineEmits(['click']);
  48. const title = computed(() => {
  49. return props.item.patentName || props.item.softwareName || props.item.achievementName ||
  50. props.item.standardName || props.item.reportTitle || props.item.workName ||
  51. props.item.awardCode || props.item.paperName || props.item.codTitle ||
  52. props.item.activityName || '未命名成果';
  53. });
  54. const creator = computed(() => {
  55. return props.item.createdName || props.item.createBy || '-';
  56. });
  57. const createTime = computed(() => {
  58. const d = props.item.createdTime || props.item.createTime || '';
  59. if (!d) return '-';
  60. return d.substring(0, 10);
  61. });
  62. const org = computed(() => {
  63. return props.item.deptName || props.item.organization || props.item.belongUnit ||
  64. props.item.achievementOwnerUnit || props.item.achievementBelongUnitName || '-';
  65. });
  66. const typeName = computed(() => {
  67. const names: any = {
  68. patent: '专利',
  69. software: '软著',
  70. other: '其他成果',
  71. standard: '标准',
  72. decision: '决策咨询',
  73. work: '学术著作',
  74. awards: '奖项荣誉',
  75. paper: '学术论文',
  76. conference: '学术会议',
  77. special_activity: '科技专项活动'
  78. };
  79. return names[props.type] || '成果';
  80. });
  81. const typeIcon = computed(() => {
  82. const icons: any = {
  83. patent: 'integral',
  84. software: 'file-text',
  85. other: 'grid',
  86. standard: 'tags',
  87. decision: 'chat',
  88. work: 'file-text',
  89. awards: 'gift',
  90. paper: 'list',
  91. conference: 'mic',
  92. special_activity: 'calendar'
  93. };
  94. return icons[props.type] || 'file-text';
  95. });
  96. const typeColor = computed(() => {
  97. const colors: any = {
  98. patent: '#3b82f6',
  99. software: '#10b981',
  100. other: '#f59e0b',
  101. standard: '#ef4444',
  102. decision: '#8b5cf6',
  103. work: '#06b6d4',
  104. awards: '#f43f5e',
  105. paper: '#14b8a6',
  106. conference: '#6366f1',
  107. special_activity: '#f97316'
  108. };
  109. return colors[props.type] || '#3b82f6';
  110. });
  111. const statusInfo = computed(() => {
  112. const type = props.type;
  113. const item = props.item;
  114. if (type === 'patent') {
  115. const s = String(item.patentStatus);
  116. const map: any = {
  117. '10': { text: '初始', type: 'primary' },
  118. '20': { text: '待审批', type: 'primary' },
  119. '30': { text: '专利审批中', type: 'info' },
  120. '40': { text: '专利审批通过', type: 'success' },
  121. '50': { text: '专利审批拒绝', type: 'error' },
  122. '60': { text: '转化审批中', type: 'info' },
  123. '70': { text: '转化成功', type: 'success' },
  124. '80': { text: '转化失败', type: 'error' },
  125. '90': { text: '专利授权审批中', type: 'info' },
  126. '100': { text: '专利授权审批通过', type: 'success' },
  127. '110': { text: '专利授权审批不通过', type: 'error' },
  128. };
  129. return map[s] || { text: '未知', type: 'info' };
  130. }
  131. if (type === 'software' || type === 'other') {
  132. const s = String(item.status);
  133. const map: any = {
  134. '10': { text: '初始', type: 'primary' },
  135. '20': { text: '审批中', type: 'info' },
  136. '30': { text: '审批通过', type: 'success' },
  137. '40': { text: '审批拒绝', type: 'error' },
  138. };
  139. return map[s] || { text: '未知', type: 'info' };
  140. }
  141. if (type === 'standard') {
  142. const s = String(item.approveStatus);
  143. const map: any = {
  144. '10': { text: '待提交', type: 'primary' },
  145. '20': { text: '待审批', type: 'primary' },
  146. '30': { text: '已通过', type: 'success' },
  147. '40': { text: '已拒绝', type: 'error' },
  148. '50': { text: '已取消', type: 'info' },
  149. '60': { text: '已撤回', type: 'info' },
  150. };
  151. return map[s] || { text: '未知', type: 'info' };
  152. }
  153. if (type === 'decision') {
  154. const s = String(item.status);
  155. const map: any = {
  156. '10': { text: '待提交', type: 'primary' },
  157. '20': { text: '审批中', type: 'info' },
  158. '30': { text: '审批通过', type: 'success' },
  159. '40': { text: '审批驳回', type: 'error' },
  160. };
  161. return map[s] || { text: '未知', type: 'info' };
  162. }
  163. if (type === 'work') {
  164. const s = String(item.workStatus);
  165. const map: any = {
  166. '10': { text: '初始', type: 'primary' },
  167. '20': { text: '待审批', type: 'primary' },
  168. '30': { text: '审批中', type: 'info' },
  169. '40': { text: '审批通过', type: 'success' },
  170. '50': { text: '审批拒绝', type: 'error' },
  171. };
  172. return map[s] || { text: '未知', type: 'info' };
  173. }
  174. if (type === 'awards') {
  175. const s = String(item.awardStatus);
  176. const map: any = {
  177. '10': { text: '初始', type: 'primary' },
  178. '20': { text: '待审批', type: 'primary' },
  179. '30': { text: '奖项荣誉审批中', type: 'info' },
  180. '40': { text: '奖项荣誉审批通过', type: 'success' },
  181. '50': { text: '奖项荣誉审批拒绝', type: 'error' },
  182. '90': { text: '登记审批中', type: 'info' },
  183. '100': { text: '登记审批通过', type: 'success' },
  184. '110': { text: '登记审批拒绝', type: 'error' },
  185. };
  186. return map[s] || { text: '未知', type: 'info' };
  187. }
  188. if (type === 'paper') {
  189. const s = String(item.paperStatus);
  190. const map: any = {
  191. '10': { text: '初始', type: 'primary' },
  192. '20': { text: '待审批', type: 'primary' },
  193. '30': { text: '审批中', type: 'info' },
  194. '40': { text: '审批通过', type: 'success' },
  195. '50': { text: '审批拒绝', type: 'error' },
  196. };
  197. return map[s] || { text: '未知', type: 'info' };
  198. }
  199. if (type === 'conference') {
  200. const s = String(item.approvalStatus);
  201. const map: any = {
  202. '10': { text: '待提交', type: 'primary' },
  203. '20': { text: '审批中', type: 'info' },
  204. '30': { text: '审批通过', type: 'success' },
  205. '40': { text: '审批退回', type: 'error' },
  206. };
  207. return map[s] || { text: '未知', type: 'info' };
  208. }
  209. if (type === 'special_activity') {
  210. const s = Number(item.auditStatus);
  211. const map: any = {
  212. 0: { text: '待审核', type: 'primary' },
  213. 1: { text: '驳回', type: 'error' },
  214. 2: { text: '再次提交', type: 'primary' },
  215. 3: { text: '审核通过', type: 'success' },
  216. };
  217. return map[s] || { text: '未知', type: 'info' };
  218. }
  219. return { text: '未知', type: 'info' };
  220. });
  221. </script>
  222. <style lang="scss" scoped>
  223. .achievement-card {
  224. background-color: #ffffff;
  225. border-radius: 20rpx;
  226. padding: 40rpx 30rpx 30rpx;
  227. margin-bottom: 28rpx;
  228. box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
  229. position: relative;
  230. overflow: hidden;
  231. border: 1rpx solid #f1f5f9;
  232. }
  233. .status-tag {
  234. position: absolute;
  235. top: 0;
  236. right: 0;
  237. padding: 8rpx 24rpx;
  238. font-size: 22rpx;
  239. font-weight: bold;
  240. color: #ffffff;
  241. border-bottom-left-radius: 20rpx;
  242. z-index: 5;
  243. &.primary { background-color: #3b82f6; }
  244. &.success { background-color: #10b981; }
  245. &.error { background-color: #ef4444; }
  246. &.warning { background-color: #f59e0b; }
  247. &.info { background-color: #94a3b8; }
  248. }
  249. .card-header {
  250. margin-bottom: 24rpx;
  251. padding-right: 140rpx;
  252. .title {
  253. font-size: 32rpx;
  254. font-weight: 600;
  255. color: #1e293b;
  256. line-height: 1.5;
  257. display: -webkit-box;
  258. -webkit-box-orient: vertical;
  259. -webkit-line-clamp: 2;
  260. overflow: hidden;
  261. }
  262. }
  263. .card-content {
  264. .info-item {
  265. display: flex;
  266. justify-content: space-between;
  267. align-items: flex-start;
  268. margin-bottom: 16rpx;
  269. font-size: 26rpx;
  270. line-height: 36rpx;
  271. .label {
  272. color: #94a3b8;
  273. width: 140rpx;
  274. flex-shrink: 0;
  275. }
  276. .value {
  277. color: #334155;
  278. flex: 1;
  279. text-align: right;
  280. font-weight: 400;
  281. word-break: break-all;
  282. &.highlight {
  283. color: #3b82f6;
  284. }
  285. }
  286. }
  287. }
  288. .card-footer {
  289. margin-top: 24rpx;
  290. padding-top: 24rpx;
  291. border-top: 1rpx solid #f1f5f9;
  292. display: flex;
  293. justify-content: space-between;
  294. align-items: center;
  295. .type-pill {
  296. display: flex;
  297. align-items: center;
  298. background-color: #f8fafc;
  299. padding: 6rpx 20rpx;
  300. border-radius: 100rpx;
  301. gap: 8rpx;
  302. border: 1rpx solid #f1f5f9;
  303. .type-name {
  304. font-size: 22rpx;
  305. font-weight: bold;
  306. }
  307. }
  308. .click-tip {
  309. display: flex;
  310. align-items: center;
  311. gap: 4rpx;
  312. text {
  313. font-size: 24rpx;
  314. color: #cbd5e1;
  315. }
  316. }
  317. }
  318. .card-hover {
  319. background-color: #fafbfc;
  320. }
  321. </style>