吉光研途前端代码仓库
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.

747 lines
20 KiB

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