resourceDetails.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. <template>
  2. <view class="rf-category" style="position: relative;">
  3. <u-toast ref="uToast" />
  4. <view>
  5. <!--搜索导航栏-->
  6. <view class="icoStyle">
  7. <!--<view class="leftArrow" @tap="back">
  8. <u-icon name="arrow-left" color="#fff" size="36"></u-icon>
  9. </view>
  10. <view style="width:90%;height:99upx">
  11. <rf-search-bar @search="navToSearch" icon="iconkuaijiecaidan" />
  12. </view>-->
  13. <u-navbar :back-text="i18n('Back')" :title="i18n('ResearchResources')+i18n('resourceDetails')"
  14. :background="barBackground" back-icon-color="white" title-color="white"
  15. :back-text-style="backStyle">
  16. <view slot="right" style="margin-right:20upx">
  17. <u-icon name="share-fill" style="margin-right: 30upx;" color="white" size="50" @click="share">
  18. </u-icon>
  19. </view>
  20. </u-navbar>
  21. </view>
  22. <view class="content">
  23. <!--信息-->
  24. <view
  25. style="width: 100%;min-height: 700upx;margin-top: 10upx;padding: 20upx 30upx;background-color: white;">
  26. <view
  27. style="width: 690upx;height: 340upx;border-radius: 40upx;border-radius: 40upx;overflow: hidden;">
  28. <u-swiper :list="bannerImg" v-if="bannerImg.length!=0&&bannerImg.length!=undefined" height="340"
  29. mode="none">
  30. </u-swiper>
  31. <image v-else src="@/static/img/resourceSharing/lavle.png" mode=""
  32. style="width: 100%;height: 100%;"></image>
  33. </view>
  34. <view style="width: 100%;margin-top: 30upx;color: #666">
  35. <view style="height: 42upx;color: #333">
  36. <text style="color: #333;font-size: 30upx; line-height: 42upx;">{{details.name}}</text>
  37. </view>
  38. <view style="min-height: 36upx;margin-top: 10upx;opacity: 0.7;">
  39. <text
  40. style="width:140upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;">{{i18n('resourceCountries')}}:</text>
  41. <text style="width:550upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;"
  42. v-if="details.countryName&&details.countryName!='null'&&details.countryName!=null">{{details.countryName}}</text>
  43. <text v-else style="color: #666;font-size: 24upx; line-height: 36upx;float: left;">/</text>
  44. <view style="clear: both;"></view>
  45. </view>
  46. <view style="min-height: 36upx;margin-top: 10upx;opacity: 0.7;">
  47. <text
  48. style="width:140upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;">{{i18n('resourceAddress')}}:</text>
  49. <text style="width:550upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;"
  50. v-if="details.address&&details.address!='null'&&details.address!=null">{{details.address}}</text>
  51. <text v-else style="color: #666;font-size: 24upx; line-height: 36upx;float: left;">/</text>
  52. <view style="clear: both;"></view>
  53. </view>
  54. <view style="min-height: 36upx;margin-top: 10upx;opacity: 0.7;">
  55. <text
  56. style="width:140upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;">{{i18n('resourceSubordinateUnits')}}:</text>
  57. <text style="width:550upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;"
  58. v-if="details.affiliatedUnitName&&details.affiliatedUnitName!='null'&&details.affiliatedUnitName!=null">{{details.affiliatedUnitName}}</text>
  59. <text v-else style="color: #666;font-size: 24upx; line-height: 36upx;float: left;">/</text>
  60. <view style="clear: both;"></view>
  61. </view>
  62. <view style="min-height: 36upx;margin-top: 10upx;opacity: 0.7;">
  63. <text
  64. style="width:140upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;">{{i18n('resourceContact')}}:</text>
  65. <text style="width:550upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;"
  66. v-if="details.contactsNumber&&details.contactsNumber!='null'&&details.contactsNumber!=null">{{details.contactsNumber}}</text>
  67. <text v-else style="color: #666;font-size: 24upx; line-height: 36upx;float: left;">/</text>
  68. <view style="clear: both;"></view>
  69. </view>
  70. <view style="min-height: 36upx;margin-top: 10upx;opacity: 0.7;">
  71. <text
  72. style="width:140upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;">{{i18n('resourceEmail')}}:</text>
  73. <text style="width:550upx;color: #666;font-size: 24upx; line-height: 36upx;float: left;"
  74. v-if="details.contactsEmail&&details.contactsEmail!='null'&&details.contactsEmail!=null">{{details.contactsEmail}}</text>
  75. <text v-else style="color: #666;font-size: 24upx; line-height: 36upx;float: left;">/</text>
  76. <view style="clear: both;"></view>
  77. </view>
  78. </view>
  79. </view>
  80. <!--摘要-->
  81. <view style="padding: 0 20upx;margin-top: 20upx;background-color: white;"
  82. v-for="(item,index) in details.informationList">
  83. <!--摘要 >> 标题-->
  84. <view style="height: 102upx;padding: 30upx 0">
  85. <view style="height: 30upx;width: 10upx;margin-top: 6upx;background-color: #6DD400;float: left">
  86. </view>
  87. <view style="height: 100%;float: left;margin-left: 15upx;">
  88. <text style="font-size: 30upx;color: #0B0B11;line-height: 42upx;">{{item.title}}</text>
  89. </view>
  90. <view style="clear: both;"></view>
  91. </view>
  92. <!--摘要 >> 正文-->
  93. <view style="padding: 0 0 30upx 0">
  94. <text v-html="item.content"></text>
  95. </view>
  96. </view>
  97. </view>
  98. <view class="recommend">
  99. <view style="width: 100%;height: 83upx;padding: 0 30upx;">
  100. <text style="font-size: 26upx;font-weight: 600;line-height: 83upx;color: #333333;">
  101. {{$i18n.locale=='zh'?'相关':'related recommendation'}}
  102. </text>
  103. </view>
  104. <!-- 列表 -->
  105. <view style="width: 100%;">
  106. <view style="width: 100%;">
  107. <view style="width: 100%;height: 160upx;margin-bottom: 20upx;padding: 20upx 30upx;"
  108. v-for="(item,index) in recommendList" @click="toDetaile(item.entityId)">
  109. <view
  110. style="width: 690upx;background-color: white;border: 1px solid rgba(151,151,151,0.5);">
  111. <view style="width: 220upx;height: 140upx;float: left;margin: 10upx 20upx 10upx 10upx">
  112. <image
  113. v-if="item.researchResourcesMedia!='null'&&item.researchResourcesMedia!=null&&item.researchResourcesMedia!=''&&item.researchResourcesMedia!=undefined"
  114. :src="'https://www.geidcp.com//api/file/pub/'+item.researchResourcesMedia"
  115. mode="" style="width: 100%;height: 100%;"></image>
  116. <image v-else src="@/static/img/resourceSharing/lavle.png" mode=""
  117. style="width: 100%;height: 100%;"></image>
  118. </view>
  119. <view style="width: 434upx;height: 100%;float: left;position: relative;">
  120. <view style="margin-top: 10upx;">
  121. <text style="font-size: 26upx; color: #0B0B11;" class="twoLine">
  122. {{item.name}}
  123. </text>
  124. </view>
  125. <view style="margin-top: 8upx;">
  126. <text style="font-size: 20upx; color: #666666;" class="twoLine">
  127. {{item.affiliatedUnitName}}
  128. </text>
  129. </view>
  130. <view style="margin-top: 8upx;">
  131. <text style="font-size: 20upx; padding: 0 13upx; border: 1px solid #1777FE;
  132. display: inline-block;border-radius: 32upx;color: #1777FE;">
  133. {{item.researchResourcesType}}
  134. </text>
  135. </view>
  136. </view>
  137. <view style="clear: both;"></view>
  138. </view>
  139. </view>
  140. <view style="margin-top: 30rpx;">
  141. <u-loadmore :status="status" :load-text="loadText" />
  142. </view>
  143. </view>
  144. </view>
  145. </view>
  146. <rf-back-top :scrollTop="scrollTop"></rf-back-top>
  147. </view>
  148. <!-- 8.8.20.112:19001 -->
  149. <share v-show="ifShare == true" :show="ifShare" @closeShare="share"
  150. :title="i18n('ResourceSharing') + '->' + i18n('researchResource')"
  151. :summary="i18n('ResourceSharing')+i18n('researchResource')"
  152. :href="'https://m.geidcp.com/#/pages/resourceSharing/resultDetails?' + 'entityId=' + entityId + '&language=' + $i18n.locale"
  153. :imageUrl="bannerImg && bannerImg[0]?' bannerImg[0]':'https://geidcp.com/api/file/pub/defaultCutPic/shareLogo.png'">
  154. </share>
  155. <!-- #ifdef H5 -->
  156. <rf-back-home></rf-back-home>
  157. <!-- #endif -->
  158. <u-tabbar v-model="current" :list="bottombar" inactive-color="#B3BCCC" active-color="#4E79ED"
  159. @change="bottombarChange"></u-tabbar>
  160. </view>
  161. </template>
  162. <script>
  163. import rfSearchBar from '@/components/rf-search-bar';
  164. import share from '@/components/share'
  165. export default {
  166. data() {
  167. return {
  168. // bar
  169. ifShare: false,
  170. barBackground: {
  171. backgroundImage: 'linear-gradient(270deg, #4BC0E2 0%, #538BE7 100%)',
  172. },
  173. backStyle: {
  174. color: '#fff'
  175. },
  176. //
  177. entityId: '',
  178. details: {},
  179. recommendList: [],
  180. pageNo: 0,
  181. totalPage: 1,
  182. status: 'loading',
  183. isLoading: true,
  184. loadText: {
  185. loading: this.$i18n.locale == 'zh' ? '加载中' : 'Loading',
  186. nomore: this.$i18n.locale == 'zh' ? '没有更多数据了' : 'No more'
  187. },
  188. // 轮播
  189. bannerImg: [],
  190. bannerVideo: [],
  191. bannerList: [],
  192. // 收藏
  193. ifcommon: false,
  194. // 字典
  195. classify1: '',
  196. classify2: '',
  197. // 返回顶部
  198. scrollTop: 0,
  199. // 底部导航栏
  200. current: 2,
  201. bottombar: [{
  202. iconPath: "heart-fill",
  203. selectedIconPath: "heart-fill",
  204. text: '',
  205. isDot: false,
  206. customIcon: false,
  207. },
  208. /*{
  209. iconPath: "share-fill",
  210. selectedIconPath: "share-fill",
  211. text: '',
  212. isDot: false,
  213. customIcon: false,
  214. },*/
  215. {
  216. iconPath: "file-text-fill",
  217. selectedIconPath: "file-text-fill",
  218. text: '',
  219. isDot: false,
  220. customIcon: false,
  221. },
  222. ],
  223. // 用户信息
  224. Storage_data: '',
  225. AuthToken: '',
  226. }
  227. },
  228. components: {
  229. rfSearchBar,
  230. share
  231. },
  232. onLoad: function(options) {
  233. this.entityId = options.entityId;
  234. if (options.language) {
  235. uni.setStorageSync("language", options.language);
  236. this._i18n.locale = options.language;
  237. }
  238. },
  239. onPageScroll(e) {
  240. this.scrollTop = e.scrollTop;
  241. },
  242. onReachBottom() { //页面拉到底部加载
  243. if (this.isLoading) {
  244. this.getDataList();
  245. }
  246. },
  247. async onShow() {
  248. this.bottombarInit()
  249. await this.getDict()
  250. await this.getData();
  251. await this.getDataList()
  252. await this.getMyCollecModel();
  253. },
  254. watch: {
  255. current() {
  256. this.current = 2;
  257. },
  258. ifcommon() {
  259. this.bottombarInit()
  260. },
  261. },
  262. computed: {
  263. token() {
  264. return 'Bearer ' + uni.getStorageSync('Auth-Token');
  265. }
  266. },
  267. methods: {
  268. share() {
  269. this.ifShare = !this.ifShare;
  270. },
  271. async getDict() {
  272. this.AuthToken = uni.getStorageSync('Auth-Token');
  273. if (this.AuthToken) {
  274. this.Storage_data = JSON.parse(uni.getStorageSync('user'));
  275. }
  276. const res = await this.$myRequest({
  277. url: '/sys/sysDicts',
  278. data: {
  279. type: this.$i18n.locale == "zh" ?
  280. 'RESEARCH_RESULT_TYPE_DICT,RESEARCH_RESOURCE_TYPE_DICT' :
  281. 'RESEARCH_RESULT_TYPE_DICT_EN,RESEARCH_RESOURCE_TYPE_DICT_EN',
  282. }
  283. });
  284. if (res.data) {
  285. let both = {
  286. label: this.$t('common.All'),
  287. value: null
  288. };
  289. this.classify1 = res.data[0];
  290. this.classify2 = res.data[1];
  291. this.classify1.unshift(both);
  292. this.classify2.unshift(both);
  293. }
  294. },
  295. async getData() {
  296. const that = this;
  297. let params = {
  298. entityId: this.entityId,
  299. pageNo: '',
  300. pageSize: '',
  301. language: this.$i18n.locale.toUpperCase(),
  302. effective: 'Y',
  303. };
  304. const res = await this.$myRequest({
  305. url: '/resourceshare/baseResearchResourcesViews/gateBaseResearchResourcesView',
  306. data: {
  307. ...params
  308. }
  309. })
  310. if (res.data) {
  311. that.details = res.data.baseResearchResourcesView;
  312. that.details.createDate = that.details.createDate ? this.formatDate(that.details.createDate,
  313. "YYYY-MM-DD") : "";
  314. // 轮播图片
  315. if (that.details.researchResourcesMedia && that.details.researchResourcesMedia != 'null' && that
  316. .details.researchResourcesMedia !=
  317. null) {
  318. that.bannerImg = that.details.researchResourcesMedia.split(',')
  319. }
  320. for (let i = 0; i < that.bannerImg.length; i++) {
  321. that.bannerImg[i] = that.bannerImg[i].replace(' ', '')
  322. that.bannerImg[i] = 'https://m.geidcp.com/api/file/pub/' + that.bannerImg[i]
  323. }
  324. // 轮播视频
  325. if (that.details.researchResourcesMediaVideo) {
  326. that.bannerVideo = that.details.researchResourcesMediaVideo.split(',')
  327. }
  328. for (let i = 0; i < that.bannerVideo.length; i++) {
  329. that.bannerVideo[i] = that.bannerVideo[i].replace(' ', '')
  330. that.bannerVideo[i] = 'https://m.geidcp.com/api/file/pub/' + that.bannerVideo[i]
  331. }
  332. that.bannerList = that.bannerVideo.concat(that.bannerImg)
  333. }
  334. },
  335. async getDataList() {
  336. const that = this;
  337. let params = {
  338. pageNo: '',
  339. pageSize: 10,
  340. language: this.$i18n.locale.toUpperCase(),
  341. status: 0,
  342. descStatus: 0,
  343. effective: 'Y',
  344. };
  345. if (this.pageNo >= this.totalPage) {
  346. this.isLoading = false;
  347. this.status = 'nomore';
  348. } else {
  349. this.isLoading = true;
  350. this.pageNo++;
  351. params.pageNo = this.pageNo;
  352. const res = await this.$myRequest({
  353. url: '/resourceshare/baseResearchResourcesViews/',
  354. data: {
  355. ...params
  356. }
  357. })
  358. if (res.data) {
  359. let data = res.data.baseResearchResourcesViews;
  360. data.forEach((i) => {
  361. i.createDate = i.createDate ? this.formatDate(i.createDate, "YYYY-MM-DD") : "";
  362. if (i.researchResourcesMedia) {
  363. i.researchResourcesMedia = i.researchResourcesMedia.split(',')
  364. i.researchResourcesMedia = i.researchResourcesMedia[0]
  365. }
  366. })
  367. for (let i = 0; i < data.length; i++) {
  368. for (let o = 0; o < that.classify2.length; o++) {
  369. if (data[i].researchResourcesType == that.classify2[o].value) {
  370. data[i].researchResourcesType = that.classify2[o].label;
  371. }
  372. }
  373. }
  374. that.recommendList = [...that.recommendList, ...data]
  375. that.totalPage = res.data.page.totalPage;
  376. if (that.totalPage == 1) {
  377. this.isLoading = false;
  378. this.status = 'nomore';
  379. }
  380. let flag = false;
  381. for (let j = 0; j < that.recommendList.length; j++) {
  382. if (that.recommendList[j].name == that.details.name) {
  383. flag = j;
  384. }
  385. }
  386. if (flag === 0 || flag != false) {
  387. that.recommendList.splice(flag, 1)
  388. }
  389. }
  390. }
  391. },
  392. toDetaile() {
  393. this.$mRouter.push({
  394. route: `/pages/resourceSharing/resourceDetails?entityId=${params.entityId}`,
  395. });
  396. },
  397. //收藏
  398. async getMyCollecModel() {
  399. const that = this;
  400. if (this.AuthToken) {
  401. var users = this.Storage_data;
  402. let MyCollecModel = {
  403. modelType: 'researchResult',
  404. userId: users.userId,
  405. modelEntityId: this.entityId,
  406. }
  407. const res = await this.$myRequest({
  408. url: '/op/basePortalModelFollowInfos/followModelStatusByModelIdAndUserId',
  409. data: {
  410. modelId: this.entityId,
  411. modelType: 'researchResult',
  412. userId: users.id,
  413. }
  414. });
  415. if (res) {
  416. this.ifcommon = res.data;
  417. }
  418. }
  419. },
  420. shareChange() {
  421. console.log('share')
  422. if (this.ifcommon) {
  423. this.saveFollowModelDo("unfollow");
  424. } else {
  425. this.saveFollowModelDo("follow");
  426. }
  427. },
  428. // 关注操作方法
  429. async saveFollowModelDo(followType) {
  430. if (!this.AuthToken) {
  431. // 登录
  432. } else {
  433. var users = this.Storage_data;
  434. let MyCollecModel = {
  435. followType: followType,
  436. modelId: this.entityId,
  437. modelType: 'researchResult',
  438. userId: users.id,
  439. };
  440. const res = await this.$myRequest({
  441. url: '/op/basePortalModelFollowInfos/followModel',
  442. method: 'post',
  443. headers: {
  444. token: this.token
  445. },
  446. data: {
  447. followType: followType,
  448. modelId: this.entityId,
  449. modelType: 'researchResources',
  450. userId: users.id,
  451. }
  452. });
  453. if (res) {
  454. if (followType == 'unfollow') {
  455. if (this.$i18n.locale == 'zh') {
  456. this.$refs.uToast.show({
  457. title: '取消关注',
  458. type: 'success',
  459. })
  460. } else {
  461. this.$refs.uToast.show({
  462. title: 'Cancel concerned',
  463. type: 'success',
  464. })
  465. }
  466. } else {
  467. if (this.$i18n.locale == 'zh') {
  468. this.$refs.uToast.show({
  469. title: '已关注',
  470. type: 'success',
  471. })
  472. } else {
  473. this.$refs.uToast.show({
  474. title: 'Already concerned',
  475. type: 'success',
  476. })
  477. }
  478. }
  479. this.ifcommon = !this.ifcommon;
  480. }
  481. }
  482. },
  483. // 底部bar事件
  484. bottombarInit() {
  485. this.bottombar[0].text = this.$i18n.locale == 'zh' ? (this.ifcommon === true ? "已关注" : "关注") : (this
  486. .ifcommon === true ? "Concerned" : " Follow")
  487. // this.bottombar[1].text = this.i18n('resourceDetailsShare')
  488. this.bottombar[1].text = this.i18n('resourceApplicationResearch')
  489. },
  490. bottombarChange(index) {
  491. if (index == 0) {
  492. // 收藏
  493. if (this.AuthToken) {
  494. this.shareChange()
  495. } else {
  496. if (this.$i18n.locale == 'zh') {
  497. this.$refs.uToast.show({
  498. title: '请登录',
  499. type: 'warning',
  500. url: '/pages/public/login',
  501. params: {
  502. back: 1
  503. }
  504. })
  505. } else {
  506. this.$refs.uToast.show({
  507. title: 'please log in',
  508. type: 'warning',
  509. url: '/pages/public/login',
  510. params: {
  511. back: 1
  512. }
  513. })
  514. }
  515. }
  516. } else if (index == 2) {
  517. // 分享
  518. } else if (index == 1) {
  519. // 申请调研
  520. if (this.AuthToken) {
  521. let params = {
  522. entityId: this.entityId,
  523. name: this.details.name,
  524. flag: true
  525. };
  526. uni.redirectTo({
  527. url: `/pages/resourceSharing/resourceApply?entityId=${params.entityId}&name=${params.name}&flag=${params.flag}`
  528. });
  529. } else {
  530. if (this.$i18n.locale == 'zh') {
  531. this.$refs.uToast.show({
  532. title: '请登录',
  533. type: 'warning',
  534. url: '/pages/public/login',
  535. params: {
  536. back: 1
  537. }
  538. })
  539. } else {
  540. this.$refs.uToast.show({
  541. title: 'please log in',
  542. type: 'warning',
  543. url: '/pages/public/login',
  544. params: {
  545. back: 1
  546. }
  547. })
  548. }
  549. }
  550. }
  551. },
  552. back() {
  553. uni.navigateBack();
  554. },
  555. //中英文切换
  556. i18n(data) {
  557. return this.$t('common.' + data);
  558. },
  559. }
  560. }
  561. </script>
  562. <style lang="scss" scoped>
  563. .rf-category {
  564. background-color: #F3F4F5;
  565. /* #ifdef APP-PLUS */
  566. /*margin-top: calc(20upx + var(--status-bar-height));*/
  567. /* #endif */
  568. .icoStyle {
  569. display: flex;
  570. align-items: center;
  571. .leftArrow {
  572. width: 36upx;
  573. height: 36upx;
  574. position: fixed;
  575. z-index: 99;
  576. /* #ifdef APP-PLUS */
  577. padding-top: calc(20upx + var(--status-bar-height));
  578. /* #endif */
  579. }
  580. }
  581. .content {
  582. background-color: #F3F4F5;
  583. }
  584. .recommend {
  585. background-color: white;
  586. margin-top: 20upx;
  587. padding-bottom: 20upx;
  588. width: 100%;
  589. }
  590. .oneLine {
  591. /* 隐藏溢出元素 */
  592. overflow: hidden;
  593. /* 单行显示 */
  594. white-space: nowrap;
  595. /* 溢出显示省略号 */
  596. text-overflow: ellipsis;
  597. }
  598. .twoLine {
  599. overflow: hidden;
  600. text-overflow: ellipsis;
  601. display: -webkit-box;
  602. -webkit-line-clamp: 2;
  603. -webkit-box-orient: vertical;
  604. }
  605. .threeLine {
  606. overflow: hidden;
  607. text-overflow: ellipsis;
  608. display: -webkit-box;
  609. -webkit-line-clamp: 3;
  610. -webkit-box-orient: vertical;
  611. }
  612. .fourLine {
  613. overflow: hidden;
  614. text-overflow: ellipsis;
  615. display: -webkit-box;
  616. -webkit-line-clamp: 4;
  617. -webkit-box-orient: vertical;
  618. }
  619. }
  620. </style>