吉光研途前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

727 lines
19 KiB

  1. <template>
  2. <view :class="['page__view', hasPoster ? 'with-bottom' : '']">
  3. <!-- 导航栏 -->
  4. <navbar :title="details.title" leftClick @leftClick="$utils.navigateBack" />
  5. <view class="cover-image" :style="{ height: coverImageHeight }">
  6. <image class="img" :src="details.image" mode="scaleToFill" ></image>
  7. <view class="shadow" :style="{ height: coverImageShadowHeight }"></view>
  8. </view>
  9. <view class="page-title">
  10. <view v-if="details.process || (details.educationProcessList && details.educationProcessList.length)">{{ details.processTitle || '发表全流程辅导' }}</view>
  11. <view v-else>{{ details.title }}</view>
  12. <view class="page-title-line"></view>
  13. </view>
  14. <!-- 发表全流程辅导 -->
  15. <view class="process" v-if="details.process || (details.educationProcessList && details.educationProcessList.length)">
  16. <view class="process-custom" v-if="details.process">
  17. <uv-parse :content="details.process"></uv-parse>
  18. </view>
  19. <view class="process-item" v-for="item in details.educationProcessList" :key="item.id">
  20. <view class="process-item-title">{{ item.title }}</view>
  21. <view class="process-item-content">{{ item.content }}</view>
  22. </view>
  23. </view>
  24. <!-- 可发表方向 -->
  25. <view class="section" v-if="details.educationTargetList && details.educationTargetList.length">
  26. <view class="section-header">
  27. <view class="section-header-line"></view>
  28. <view>{{ details.targetTitle || '可发表方向' }}</view>
  29. </view>
  30. <view class="section-content direction">
  31. <uv-read-more show-height="300rpx" :toggle="true" textIndent="0" closeText="展开" :shadowStyle="{ backgroundImage: 'linear-gradient(-180deg, rgba(255, 255, 255, 0) 0%, #fff 80%)', paddingTop: '100rpx', marginTop: '-100rpx' }">
  32. <view class="table">
  33. <view class="table-row" v-for="row in details.educationTargetList" :key="row.id">
  34. <view class="table-cell">{{ row.title }}</view>
  35. <view class="table-cell">{{ row.description }}</view>
  36. </view>
  37. </view>
  38. </uv-read-more>
  39. </view>
  40. </view>
  41. <!-- 师资介绍 -->
  42. <view class="section" v-if="details.educationTeacherList && details.educationTeacherList.length">
  43. <view class="section-header">
  44. <view class="section-header-line"></view>
  45. <view>{{ details.teacherTitle || '师资介绍' }}</view>
  46. </view>
  47. <view class="section-content teachers">
  48. <view class="card" v-for="item in details.educationTeacherList" :key="item.id">
  49. <view class="info">
  50. <view class="pic">
  51. <image class="img" :src="item.image" mode="aspectFill" ></image>
  52. </view>
  53. <view class="title">
  54. <view class="name">{{ item.title }}</view>
  55. <view class="label" v-if="item.career">
  56. <view class="label-text">{{ item.career }}</view>
  57. <view class="label-line"></view>
  58. <view class="label-text display">{{ item.career }}</view>
  59. </view>
  60. </view>
  61. <view class="tag">
  62. <image class="tag-icon" src="@/static/image/icon-degree.png" mode="widthFix"></image>
  63. <view>{{ item.qualification }}</view>
  64. </view>
  65. <view class="desc desc-top">专业经历</view>
  66. <view class="desc desc-bottom">{{ item.experience }}</view>
  67. </view>
  68. </view>
  69. </view>
  70. </view>
  71. <!-- 课程安排 -->
  72. <view class="section" v-if="details.educationCourseList && details.educationCourseList.length">
  73. <view class="section-header">
  74. <view class="section-header-line"></view>
  75. <view>{{ details.courseTitle || '课程安排' }}</view>
  76. </view>
  77. <view class="section-content">
  78. <view class="table">
  79. <view class="table-row" v-for="row in details.educationCourseList" :key="row.id">
  80. <view class="table-cell">{{ row.title }}</view>
  81. <view class="table-cell">{{ row.description }}</view>
  82. </view>
  83. </view>
  84. </view>
  85. </view>
  86. <!-- 适用人群 -->
  87. <view class="section" v-if="details.suit">
  88. <view class="section-header">
  89. <view class="section-header-line"></view>
  90. <view>{{ details.suitTitle || '适用人群' }}</view>
  91. </view>
  92. <view class="section-content target-audience">
  93. <view class="paragraph">
  94. <uv-parse :content="details.suit"></uv-parse>
  95. </view>
  96. </view>
  97. </view>
  98. <!-- 期刊推荐 -->
  99. <view class="section" v-if="details.educationPeriodicalList && details.educationPeriodicalList.length">
  100. <view class="section-header">
  101. <view class="section-header-line"></view>
  102. <view>{{ details.periodicalTitle || '期刊推荐' }}</view>
  103. </view>
  104. <view class="section-content journal">
  105. <view class="box" v-for="item in details.educationPeriodicalList" :key="item.id">
  106. <view class="card">
  107. <view class="top">
  108. <view class="pic">
  109. <image class="img" :src="item.image" mode="aspectFill"></image>
  110. </view>
  111. <view class="info">
  112. <view class="name">{{ item.title }}</view>
  113. <view class="desc">{{ item.shortTitle }}</view>
  114. </view>
  115. </view>
  116. <view class="main">
  117. {{ item.description }}
  118. </view>
  119. </view>
  120. </view>
  121. </view>
  122. </view>
  123. <!-- 附加材料 -->
  124. <view class="section" v-if="details.educationDocumentList && details.educationDocumentList.length">
  125. <view class="section-header">
  126. <view class="section-header-line"></view>
  127. <view>{{ details.documentTitle || '附加材料' }}</view>
  128. </view>
  129. <view class="section-content attachment">
  130. <view class="flex file" v-for="item in details.educationDocumentList" :key="item.id">
  131. <view class="file-info">
  132. <template v-if="item.type == 'pdf'">
  133. <image class="file-icon" src="@/static/image/icon-pdf.png" mode="widthFix"></image>
  134. </template>
  135. <template v-else>
  136. <image class="file-icon" src="@/static/image/icon-word.png" mode="widthFix"></image>
  137. </template>
  138. <view class="file-detail">
  139. <view class="title">{{ item.title }}</view>
  140. <!-- <view class="desc">{{ `${getFileType(item.document)} ${item.size || '-'}MB` }}</view> -->
  141. </view>
  142. </view>
  143. <button class="btn" @click="downloadFile(item.document)">下载</button>
  144. </view>
  145. </view>
  146. </view>
  147. <view class="flex bottom" v-if="hasPoster">
  148. <view class="flex btns">
  149. <button class="btn" @click="jumpToPoster">保存海报</button>
  150. <button class="btn btn-share" open-type="share">分享文章</button>
  151. </view>
  152. </view>
  153. </view>
  154. </template>
  155. <script>
  156. export default {
  157. data() {
  158. return {
  159. details: {},
  160. coverImageHeight: '566rpx',
  161. coverImageShadowHeight: '113rpx'
  162. }
  163. },
  164. onLoad({ thesisId }) {
  165. const windowWidth = uni.getSystemInfoSync().windowWidth
  166. const coverImageHeight = (windowWidth) * 566 / 714
  167. const coverImageShadowHeight = (windowWidth) * 113 / 714
  168. this.coverImageHeight = `${coverImageHeight}px`
  169. this.coverImageShadowHeight = `${coverImageShadowHeight}px`
  170. this.getData(thesisId)
  171. // this.getData('1949729528544800770')
  172. },
  173. onShareAppMessage(res) {
  174. return {
  175. title: this.posterData.paperDesc,
  176. imageUrl: this.posterData.paperImage,
  177. path: this.posterData.path,
  178. }
  179. },
  180. computed: {
  181. posterData() {
  182. const { id, title, image, paperDesc, paperImage } = this.details || {}
  183. return {
  184. paperDesc: paperDesc || title,
  185. paperImage: paperImage || image || '',
  186. path: `pages_order/thesis/index?thesisId=${id}`
  187. }
  188. },
  189. hasPoster() {
  190. const { paperImage } = this.details || {}
  191. return !!paperImage
  192. }
  193. },
  194. methods: {
  195. async getData(thesisId) {
  196. try {
  197. this.details = await this.$fetch('queryThesisById', { thesisId })
  198. } catch (err) {
  199. }
  200. },
  201. getFileType(fileName) {
  202. const pdfReg = /(.pdf)$/g
  203. // const officeReg = /(doc|docx|ppt|pptx|xls|xlsx)$/g
  204. return pdfReg.test(fileName) ? 'pdf' : ''
  205. },
  206. downloadFile(url) {
  207. console.log('downloadFile', url)
  208. uni.downloadFile({
  209. url, // 文件地址
  210. success: (downloadRes) => {
  211. console.log('downloadRes', downloadRes)
  212. if (downloadRes.statusCode === 200) {
  213. uni.openDocument({
  214. showMenu: true,
  215. filePath: downloadRes.tempFilePath,
  216. success: () => console.log('打开成功')
  217. });
  218. } else {
  219. uni.showToast({ title: '下载失败', icon: 'none' });
  220. }
  221. },
  222. fail: (err) => {
  223. console.log('downloadFile fail', err)
  224. uni.showToast({ title: '网络异常', icon: 'none' });
  225. }
  226. });
  227. },
  228. jumpToPoster() {
  229. uni.setStorageSync('posterData', this.posterData)
  230. uni.navigateTo({
  231. url: `/pages_order/thesis/poster`
  232. })
  233. },
  234. },
  235. }
  236. </script>
  237. <style scoped lang="scss">
  238. .page__view {
  239. background: #FFFFFF;
  240. padding-bottom: 62rpx;
  241. &.with-bottom {
  242. padding-bottom: calc(62rpx + 110rpx + env(safe-area-inset-bottom));
  243. }
  244. }
  245. .cover-image {
  246. position: relative;
  247. width: 100%;
  248. background: #FFFFFF;
  249. .img {
  250. width: 100%;
  251. height: 100%;
  252. }
  253. .shadow {
  254. position: absolute;
  255. bottom: 0;
  256. left: 0;
  257. width: 100%;
  258. height: 100%;
  259. background: linear-gradient(transparent, #FFFFFF);
  260. }
  261. }
  262. .page-title {
  263. padding: 0 36rpx;
  264. font-size: 60rpx;
  265. font-weight: 700;
  266. color: #4783F9;
  267. &-line {
  268. margin: 12rpx 0 24rpx 0;
  269. width: 120rpx;
  270. height: 8rpx;
  271. background: #4783F9;
  272. }
  273. }
  274. .process {
  275. position: relative;
  276. width: 100%;
  277. padding: 0 36rpx;
  278. box-sizing: border-box;
  279. &-custom {
  280. padding: 6rpx 18rpx;
  281. background: #E6EEFD;
  282. border-top-right-radius: 32rpx;
  283. border-bottom-right-radius: 32rpx;
  284. }
  285. &-item {
  286. margin-top: 24rpx;
  287. padding: 10rpx 18rpx;
  288. background: #E6EEFD;
  289. border-radius: 25rpx;
  290. &-title {
  291. font-size: 30rpx;
  292. font-weight: 600;
  293. color: #3378EA;
  294. }
  295. &-content {
  296. font-size: 28rpx;
  297. white-space: pre-wrap;
  298. color: #000000;
  299. }
  300. }
  301. }
  302. .section {
  303. margin-top: 40rpx;
  304. width: 100%;
  305. padding: 0 18rpx;
  306. box-sizing: border-box;
  307. // & + & {
  308. // margin-top: 40rpx;
  309. // }
  310. &-header {
  311. display: flex;
  312. align-items: center;
  313. justify-content: flex-start;
  314. column-gap: 15rpx;
  315. padding-left: 18rpx;
  316. font-size: 32rpx;
  317. font-weight: 700;
  318. color: #000000;
  319. &-line {
  320. width: 11rpx;
  321. height: 45rpx;
  322. border-radius: 6rpx;
  323. background-image: linear-gradient(#FFFFFF, #4883F9);
  324. }
  325. }
  326. &-content {
  327. margin-top: 37rpx;
  328. .paragraph {
  329. width: 100%;
  330. padding: 22rpx;
  331. box-sizing: border-box;
  332. white-space: pre-line;
  333. font-size: 28rpx;
  334. color: #000000;
  335. background: #F8F8F8;
  336. border-radius: 15rpx;
  337. }
  338. .table {
  339. width: 100%;
  340. border-radius: 15rpx;
  341. overflow: hidden;
  342. &-row {
  343. display: grid;
  344. grid-template-columns: 218rpx auto;
  345. background: #EEEEEE;
  346. &:nth-child(2n) {
  347. background: #DEDEDE;
  348. }
  349. }
  350. &-cell {
  351. display: inline-flex;
  352. flex-direction: column;
  353. align-items: flex-start;
  354. justify-content: center;
  355. padding: 17rpx;
  356. box-sizing: border-box;
  357. font-family: PingFang SC;
  358. font-weight: 400;
  359. font-size: 28rpx;
  360. white-space: pre-wrap;
  361. color: #080808;
  362. &:first-child {
  363. border-right: 1rpx solid #E5E5E5;
  364. }
  365. }
  366. }
  367. &.direction {
  368. /deep/ .uv-read-more__toggle {
  369. justify-content: flex-end;
  370. }
  371. /deep/ .uv-read-more__toggle__text .uv-text__value {
  372. color: $uni-color !important;
  373. font-size: 22rpx !important;
  374. }
  375. }
  376. &.teachers {
  377. .card {
  378. display: flex;
  379. align-items: center;
  380. column-gap: 12rpx;
  381. background: #ffffff;
  382. border-radius: 15rpx;
  383. box-shadow: 0rpx 3rpx 6rpx 0rpx rgba(0,0,0,0.16);
  384. .info {
  385. flex: 1;
  386. padding: 19rpx 17rpx 28rpx 17rpx;
  387. .title {
  388. display: flex;
  389. align-items: center;
  390. padding-left: 11rpx;
  391. color: #000000;
  392. .name {
  393. font-size: 32rpx;
  394. font-weight: 700;
  395. }
  396. .label {
  397. margin-left: 12rpx;
  398. position: relative;
  399. // height: 30rpx;
  400. &-line {
  401. position: absolute;
  402. left: 0;
  403. bottom: 0;
  404. transform: translate(30rpx, -2rpx);
  405. // margin: 24rpx 0 0 25rpx;
  406. // width: 195rpx;
  407. width: calc(100% - 20rpx);
  408. height: 4rpx;
  409. background-image: linear-gradient(#4883F9, #FFFFFF);
  410. border-radius: 2rpx;
  411. }
  412. &-text {
  413. font-size: 22rpx;
  414. font-weight: 400;
  415. opacity: 0;
  416. &.display {
  417. opacity: 1;
  418. position: absolute;
  419. left: 0;
  420. top: 0;
  421. }
  422. }
  423. }
  424. }
  425. .tag {
  426. margin: 5rpx 0 18rpx 0;
  427. display: inline-flex;
  428. align-items: center;
  429. padding: 5rpx 43rpx 5rpx 16rpx;
  430. column-gap: 16rpx;
  431. font-size: 28rpx;
  432. color: #FFFFFF;
  433. background: rgba(72,131,249,0.58);
  434. border-radius: 30rpx 0rpx 30rpx 0rpx;
  435. &-icon {
  436. width: 38rpx;
  437. height: auto;
  438. }
  439. }
  440. .desc {
  441. // padding: 12rpx 15rpx;
  442. font-size: 28rpx;
  443. font-weight: 500;
  444. white-space: pre-wrap;
  445. color: #000000;
  446. background: #f8f8f8;
  447. // border-radius: 16rpx;
  448. &-top {
  449. padding: 12rpx 15rpx 0 15rpx;
  450. border-top-left-radius: 16rpx;
  451. border-top-right-radius: 16rpx;
  452. }
  453. &-bottom {
  454. padding: 0 15rpx 12rpx 15rpx;
  455. border-bottom-left-radius: 16rpx;
  456. border-bottom-right-radius: 16rpx;
  457. }
  458. }
  459. }
  460. .pic {
  461. // margin: 48rpx 9rpx 48rpx 0;
  462. margin: 44rpx 0 0 14rpx;
  463. width: 253rpx;
  464. height: 345rpx;
  465. border-radius: 127rpx;
  466. overflow: hidden;
  467. float: right;
  468. .img {
  469. width: 100%;
  470. height: 100%;
  471. }
  472. }
  473. &:nth-child(2n) {
  474. flex-direction: row-reverse;
  475. .info {
  476. padding: 19rpx 17rpx 28rpx 17rpx;
  477. }
  478. .pic {
  479. // margin: 48rpx 0 48rpx 9rpx;
  480. margin: 44rpx 14rpx 0 0;
  481. float: left;
  482. }
  483. }
  484. }
  485. .card + .card {
  486. margin-top: 24rpx;
  487. }
  488. }
  489. &.journal {
  490. .box {
  491. padding-top: 73rpx;
  492. .card {
  493. padding: 29rpx 12rpx 19rpx 12rpx;
  494. background: #f6f6f6;
  495. border-radius: 15rpx;
  496. box-shadow: 0rpx 3rpx 6rpx 0rpx rgba(0,0,0,0.16);
  497. .top {
  498. position: relative;
  499. .pic {
  500. position: absolute;
  501. left: 42rpx;
  502. bottom: 0;
  503. width: 213rpx;
  504. height: 285rpx;
  505. .img {
  506. width: 100%;
  507. height: 100%;
  508. }
  509. }
  510. .info {
  511. min-height: 181rpx;
  512. padding: 0 14rpx 0 266rpx;
  513. .name {
  514. padding-left: 6rpx;
  515. font-size: 32rpx;
  516. font-weight: 700;
  517. color: #000000;
  518. }
  519. .desc {
  520. margin-top: 24rpx;
  521. // font-size: 28rpx;
  522. font-size: 24rpx;
  523. white-space: pre-wrap;
  524. font-weight: 700;
  525. color: #707070;
  526. // overflow: hidden;
  527. // text-overflow: ellipsis;
  528. // display:-webkit-box; //作为弹性伸缩盒子模型显示。
  529. // -webkit-box-orient:vertical; //设置伸缩盒子的子元素排列方式--从上到下垂直排列
  530. // -webkit-line-clamp:3; //显示的行
  531. }
  532. }
  533. }
  534. .main {
  535. margin-top: 18rpx;
  536. padding: 14rpx;
  537. font-size: 28rpx;
  538. white-space: pre-wrap;
  539. color: #000000;
  540. background: #ebebeb;
  541. border-radius: 30rpx 0rpx 30rpx 0rpx;
  542. }
  543. }
  544. &:nth-child(2n) {
  545. .card {
  546. .top {
  547. .info {
  548. padding: 0 258rpx 0 14rpx;
  549. }
  550. .pic {
  551. left: unset;
  552. right: 29rpx;
  553. }
  554. }
  555. }
  556. }
  557. }
  558. .box + .box {
  559. margin-top: 28rpx;
  560. }
  561. }
  562. &.attachment {
  563. .file {
  564. justify-content: space-between;
  565. padding: 32rpx 15rpx 32rpx 26rpx;
  566. background: #f8f8f8;
  567. border-radius: 15rpx;
  568. &-info {
  569. display: flex;
  570. // align-items: flex-end;
  571. align-items: center;
  572. }
  573. &-icon {
  574. width: 64rpx;
  575. height: auto;
  576. }
  577. &-detail {
  578. margin-left: 14rpx;
  579. .title {
  580. font-size: 30rpx;
  581. color: #000000;
  582. }
  583. .desc {
  584. font-size: 28rpx;
  585. color: #999999;
  586. }
  587. }
  588. .btn {
  589. padding: 7rpx 30rpx;
  590. font-size: 28rpx;
  591. color: #FFFFFF;
  592. background: #4883f9;
  593. border-radius: 27rpx;
  594. }
  595. }
  596. .file + .file {
  597. margin-top: 21rpx;
  598. }
  599. }
  600. }
  601. }
  602. .bottom {
  603. position: fixed;
  604. left: 0;
  605. bottom: 0;
  606. width: 100vw;
  607. height: 110rpx;
  608. padding-bottom: env(safe-area-inset-bottom);
  609. background: #FFFFFF;
  610. box-shadow: 0px 3px 6px 0px rgba(0,0,0,0.16);
  611. .btns {
  612. column-gap: 34rpx;
  613. }
  614. .btn {
  615. padding: 20rpx 90rpx;
  616. font-size: 28rpx;
  617. color: #FFFFFF;
  618. background: #4883F9;
  619. border-radius: 42rpx;
  620. &-share {
  621. background: #FFD019;
  622. }
  623. }
  624. }
  625. </style>