index - 副本.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. <template>
  2. <view class="logo-v">
  3. <view class="login-bg">
  4. <image src="../../static/image/login-bg.jpg" mode="widthFix"></image>
  5. <view class="logoImg">
  6. <u-image :src="appIcon" mode="widthFix" :border-radius="20" width="160" height="160">
  7. <template #error>
  8. <u-image :src="logoImg" mode="widthFix" width="160" height="160">
  9. </u-image>
  10. </template>
  11. </u-image>
  12. </view>
  13. <view class="login-version">
  14. <view class="login-version-text">{{sysConfigInfo.sysVersion || define.sysVersion}}</view>
  15. </view>
  16. </view>
  17. <view class="logo-hd u-flex-col">
  18. <view class="loginSwitch u-flex-col">
  19. <view class="loginInputBox u-flex-col" v-show="!isSso && !ssoLoading">
  20. <u-form :model="formData" :rules="rules" ref="dataForm" :errorType="['toast']" label-position="left"
  21. label-width="150" label-align="left">
  22. <u-form-item prop="account" :borderBottom="false">
  23. <u-input input-align='left' v-model="formData.account" placeholder="请输入帐号" @focus="onFocus"
  24. @blur="onBlur" border border-color="#F0F1F3" placeholder-style="#9D9D9D">
  25. </u-input>
  26. </u-form-item>
  27. <u-form-item prop="password" :border-bottom="false">
  28. <u-input input-align='left' v-model="formData.password" type="password" placeholder="请输入密码"
  29. border border-color="#F0F1F3" placeholder-style="#9D9D9D">
  30. </u-input>
  31. </u-form-item>
  32. <u-form-item prop="code" required v-if="needCode">
  33. <view class="u-flex code-box">
  34. <u-input v-model="formData.code" placeholder="验证码" input-align='left'></u-input>
  35. <view class="code-img-box">
  36. <u-image :showLoading="true" :src="baseURL+imgUrl" width="130px" height="38px"
  37. @click="changeCode">
  38. </u-image>
  39. </view>
  40. </view>
  41. </u-form-item>
  42. </u-form>
  43. <view class="remember-wrap">
  44. <u-checkbox v-model="remember"><span class="remember-text">记住账号密码</span></u-checkbox>
  45. </view>
  46. <view class="loginBtnBox">
  47. <u-button @click="login" type="primary" :loading="loading">{{ loading ? "登录中...":"登录"}}
  48. </u-button>
  49. </view>
  50. <template v-if="socialsList.length">
  51. <u-divider margin-top='40' margin-bottom='40' half-width='100%'>其他登录方式</u-divider>
  52. <view class="other-list">
  53. <block v-for="(item,i) in socialsList" :key="i">
  54. <!--#ifdef MP-WEIXIN -->
  55. <view class="other-item" v-if="item.enname==='wechat_open'" :title="item.name"
  56. @click="wechatLogin()"><text :class="item.icon" />
  57. </view>
  58. <!-- #endif -->
  59. <!-- #ifndef MP-WEIXIN -->
  60. <!--#ifdef APP-PLUS-->
  61. <view class="other-item" v-if="item.enname==='qq'" :title="item.name"
  62. @click="qqOtherlogin()"><text :class="item.icon" />
  63. </view>
  64. <!-- #endif -->
  65. <!-- #ifdef H5 || MP-WEIXIN -->
  66. <view class="other-item" v-if="item.enname==='qq'" :title="item.name"
  67. @click="otherslogin(item.enname,item.renderUrl)"><text :class="item.icon" />
  68. </view>
  69. <!-- #endif -->
  70. <view class="other-item" v-else :title="item.name"
  71. @click="otherslogin(item.enname,item.renderUrl)"><text :class="item.icon" />
  72. </view>
  73. <!-- #endif -->
  74. </block>
  75. </view>
  76. </template>
  77. </view>
  78. <view class="sso-login-btn" v-show="isSso && !ssoLoading">
  79. <u-button @click="ssoLogin" type="primary" :loading="loading">{{ loading ? "登录中...":"登录"}}
  80. </u-button>
  81. </view>
  82. </view>
  83. </view>
  84. <view class="copyright">{{copyright}}</view>
  85. <u-popup v-model="show" mode="left" width="90%" height="100%">
  86. <view class="mian">
  87. <view class='top'>
  88. <view class='img-box'>
  89. <image class="img" src="/static/image/tabbar/contactsHL.png" mode="widthFix"></image>
  90. </view>
  91. <view class='title'>
  92. 请选择登录账号
  93. </view>
  94. </view>
  95. <view v-for="(item,i) in tenantUserInfo" :key="i">
  96. <view class='info' @click="socailsLogin(item)">
  97. <view class='user-name'>
  98. {{item.socialName}}
  99. </view>
  100. <view class='user-tenancy'>
  101. 租户名称: {{item.tenantName}}
  102. </view>
  103. <view class='user-tenancy'>
  104. 租户id:{{item.tenantId}}
  105. </view>
  106. <view class='user-tenancy'>
  107. 账号:{{item.accountName}}
  108. </view>
  109. </view>
  110. </view>
  111. </view>
  112. </u-popup>
  113. </view>
  114. </template>
  115. <script>
  116. import {
  117. login,
  118. getConfig,
  119. getCallback,
  120. otherlogin,
  121. getLoginConfig,
  122. getSocialsUserList,
  123. socialsLogin,
  124. getTicket
  125. } from '@/api/common.js'
  126. import md5Libs from "@/uni_modules/vk-uview-ui/libs/function/md5";
  127. import resources from '@/libs/resources'
  128. import {
  129. useUserStore
  130. } from '@/store/modules/user'
  131. import logoImg from '@/static/logo.png'
  132. export default {
  133. data() {
  134. return {
  135. remember: false,
  136. logoImg,
  137. imgUrl: '',
  138. loading: false,
  139. formData: {
  140. account: "",
  141. password: "",
  142. code: "",
  143. origin: 'password'
  144. },
  145. needCode: false,
  146. codeLength: 4,
  147. isCode: false,
  148. rules: {
  149. account: [{
  150. required: true,
  151. message: '请输入账号',
  152. trigger: 'blur',
  153. }],
  154. password: [{
  155. required: true,
  156. message: '请输入密码',
  157. trigger: 'blur',
  158. }],
  159. },
  160. sysConfigInfo: {},
  161. appIcon: '',
  162. sysName: '',
  163. copyright: '',
  164. isCopyright: true,
  165. socialsList: [],
  166. show: false,
  167. tenantUserInfo: [],
  168. ssoLoading: true,
  169. isSso: false,
  170. ssoTicket: '',
  171. ssoUrl: '',
  172. preUrl: '',
  173. ticketParams: "",
  174. loginCode: '',
  175. }
  176. },
  177. watch: {
  178. remember: {
  179. handler(val) {
  180. let model = uni.getStorageSync('rememberAccount')
  181. if (!model) model = {
  182. account: '',
  183. password: ''
  184. }
  185. model.remember = val
  186. uni.setStorageSync('rememberAccount', model)
  187. },
  188. deep: true,
  189. }
  190. },
  191. computed: {
  192. baseURL() {
  193. return this.define.baseURL
  194. },
  195. },
  196. onReady() {
  197. this.$refs.dataForm.setRules(this.rules);
  198. },
  199. onLoad(options) {
  200. if (options?.JNPF_TICKET) {
  201. this.ssoTicket = options.JNPF_TICKET
  202. uni.navigateTo({
  203. url: `/pages/login/otherLogin?ssoTicket=${this.ssoTicket}`
  204. })
  205. }
  206. this.ssoTicket = uni.getStorageSync('ssoTicket')
  207. this.sysConfigInfo = uni.getStorageSync('sysConfigInfo')
  208. this.appIcon = !!this.sysConfigInfo.appIcon ? this.baseURL + this.sysConfigInfo.appIcon :
  209. logoImg
  210. this.sysName = !!this.sysConfigInfo.companyName ? this.sysConfigInfo.sysName :
  211. 'USKY快速开发平台'
  212. this.copyright = !!this.sysConfigInfo.copyright ? this.sysConfigInfo.copyright :
  213. this.define.copyright
  214. let needCode = uni.getStorageSync('app_loginNeedCode')
  215. this.isCode = needCode
  216. this.changeCode()
  217. this.getLoginConfig()
  218. this.formData.password = '';
  219. if (options.data) {
  220. this.tenantUserInfo = JSON.parse(options.data)
  221. if (this.tenantUserInfo) this.show = true
  222. }
  223. this.initAccount()
  224. },
  225. methods: {
  226. initAccount() {
  227. let model = uni.getStorageSync('rememberAccount')
  228. if (model && model.remember) {
  229. if (model.account) {
  230. this.formData.account = this.jnpf.aesEncryption.decrypt(model.account)
  231. }
  232. if (model.password) {
  233. this.formData.password = this.jnpf.aesEncryption.decrypt(model.password)
  234. }
  235. }
  236. this.remember = model.remember
  237. },
  238. rememberAccount() {
  239. // 是否记住密码
  240. if (this.remember) {
  241. let model = {};
  242. model.remember = true
  243. model.account = this.jnpf.aesEncryption.encrypt(this.formData.account)
  244. model.password = this.jnpf.aesEncryption.encrypt(this.formData.password)
  245. uni.setStorageSync('rememberAccount', model)
  246. }
  247. },
  248. loginHandel() {
  249. uni.showLoading({
  250. title: '登录中'
  251. })
  252. userStore.getCurrentUser().then((res) => {
  253. uni.hideLoading()
  254. uni.reLaunch({
  255. url: '/pages/index/index'
  256. });
  257. }).catch(() => {
  258. uni.hideLoading()
  259. uni.reLaunch({
  260. url: '/pages/login/index'
  261. });
  262. })
  263. },
  264. loginToken(res) {
  265. const userStore = useUserStore()
  266. userStore.setToken(res.data.value)
  267. if (res.data.status != 2) {
  268. // 登录成功
  269. if (res.data.status == 1) return this.loginHandel()
  270. if (res.data.status == 6) {
  271. this.tenantUserInfo = JSON.parse(res.data.value)
  272. if (this.tenantUserInfo.length == 1) {
  273. this.loginHandel()
  274. } else {
  275. this.show = true
  276. }
  277. } else {
  278. this.show = false
  279. this.ssoUrl = ''
  280. uni.showToast({
  281. title: res.data.value || '操作超时,请重新点击登录',
  282. icon: 'none'
  283. })
  284. }
  285. }
  286. },
  287. wechatLogin() {
  288. // #ifdef MP-WEIXIN
  289. getTicket().then(res => {
  290. this.ssoTicket = res.data
  291. uni.login({
  292. provider: 'weixin',
  293. success: (loginRes) => {
  294. this.loginCode = loginRes.code
  295. }
  296. })
  297. uni.getUserProfile({
  298. desc: '获取你的昵称、头像、地区及性别',
  299. success: (info) => {
  300. let qurey = {
  301. encryptedData: info.encryptedData,
  302. iv: info.iv,
  303. signature: info.signature,
  304. code: this.loginCode,
  305. jnpf_ticket: this.ssoTicket,
  306. socialName: info.userInfo.nickName,
  307. source: 'wechat_applets'
  308. }
  309. socialsLogin(qurey).then(res => {
  310. this.loginToken(res)
  311. })
  312. }
  313. })
  314. })
  315. // #endif
  316. // #ifdef APP-PLUS
  317. getTicket().then(res => {
  318. this.ssoTicket = res.data
  319. uni.login({
  320. provider: 'weixin',
  321. success: (loginRes) => {
  322. // 登录成功
  323. uni.getUserProfile({
  324. provider: 'weixin',
  325. success: (info) => {
  326. let data = {
  327. source: 'wechat_open',
  328. uuid: info.userInfo.unionId,
  329. socialName: info.userInfo.nickName,
  330. jnpf_ticket: this.ssoTicket
  331. };
  332. socialsLogin(data).then(res => {
  333. this.loginToken(res)
  334. })
  335. },
  336. fail: function(err) {
  337. // 登录授权失败
  338. // err.code是错误码
  339. }
  340. })
  341. }
  342. });
  343. })
  344. // #endif
  345. },
  346. qqOtherlogin() {
  347. getTicket().then(res => {
  348. this.ssoTicket = res.data
  349. uni.login({
  350. provider: 'qq',
  351. success: (loginRes) => {
  352. // 登录成功
  353. uni.getUserInfo({
  354. provider: 'qq',
  355. success: (info) => {
  356. let data = {
  357. source: 'qq',
  358. jnpf_ticket: this.ssoTicket,
  359. socialName: info.userInfo.nickName,
  360. uuid: info.userInfonickName.unionid,
  361. };
  362. socialsLogin(data).then(res => {
  363. this.loginToken(res)
  364. }).catch((err) => {})
  365. // 获取用户信息成功, info.authResult保存用户信息
  366. }
  367. })
  368. }
  369. });
  370. })
  371. },
  372. socailsLogin(item) {
  373. const userStore = useUserStore()
  374. item.tenantLogin = true
  375. socialsLogin(item).then(res => {
  376. if (res.code == 200) {
  377. uni.showLoading({
  378. title: '登录中'
  379. })
  380. userStore.setToken(res.data.token)
  381. userStore.getCurrentUser().then((res) => {
  382. uni.hideLoading()
  383. uni.switchTab({
  384. url: '/pages/index/index'
  385. });
  386. this.show = false
  387. }).catch(() => {
  388. uni.hideLoading()
  389. uni.switchTab({
  390. url: '/pages/login/index'
  391. });
  392. })
  393. }
  394. }).catch(() => {
  395. uni.hideLoading()
  396. uni.switchTab({
  397. url: '/pages/login/index'
  398. });
  399. })
  400. },
  401. otherslogin(key, url) {
  402. if (key === 'wechat_open') {
  403. this.wechatLogin();
  404. } else {
  405. getTicket().then(res => {
  406. this.ssoTicket = res.data
  407. url = url.replace('JNPF_TICKET', this.ssoTicket)
  408. uni.setStorageSync('ssoUrl', url)
  409. uni.navigateTo({
  410. url: `/pages/login/otherLogin?ssoTicket=` + this.ssoTicket
  411. })
  412. }).catch(() => {})
  413. }
  414. },
  415. onFocus(e) {
  416. this.getCodeConfig(e)
  417. },
  418. onBlur(e) {
  419. this.getCodeConfig(e)
  420. },
  421. // 获取登陆配置
  422. getLoginConfig() {
  423. getLoginConfig().then(res => {
  424. this.isSso = res.data.redirect
  425. this.preUrl = res.data.url
  426. this.ticketParams = res.data.ticketParams
  427. let socialsList = res.data.socialsList || []
  428. this.socialsList = socialsList.filter(o => o.latest && o.enname !=
  429. 'github' && o
  430. .enname !=
  431. 'wechat_enterprise')
  432. this.ssoLoading = false
  433. }).catch(() => {
  434. this.isSso = false
  435. this.ssoLoading = false
  436. })
  437. },
  438. getCodeConfig(val) {
  439. if (!val) return
  440. getConfig(val).then(res => {
  441. this.needCode = !!res.data.enableVerificationCode
  442. if (this.needCode) {
  443. this.codeLength = res.data.verificationCodeNumber || 4
  444. this.changeCode()
  445. }
  446. })
  447. },
  448. changeCode() {
  449. let timestamp = Math.random()
  450. this.timestamp = timestamp
  451. this.imgUrl = `/api/oauth/ImageCode/${this.codeLength || 4}/${timestamp}`
  452. },
  453. login() {
  454. const userStore = useUserStore()
  455. this.$refs.dataForm.validate(valid => {
  456. if (valid) {
  457. this.loading = true
  458. const password = md5Libs.md5(this.formData.password);
  459. const encryptPassword = this.jnpf.aesEncryption.encrypt(password);
  460. let query = {
  461. account: this.formData.account,
  462. password: encryptPassword,
  463. timestamp: this.timestamp,
  464. code: this.formData.code,
  465. origin: this.formData.origin,
  466. jnpf_ticket: this.ssoTicket,
  467. grant_type: 'password',
  468. }
  469. // #ifdef APP-PLUS
  470. const clientId = plus.push.getClientInfo().clientid;
  471. query.clientId = clientId
  472. /* unipush2.0 */
  473. // query.Client_Id = uni.getStorageSync('cid')
  474. // #endif
  475. login(query).then(res => {
  476. let token = res.data.token
  477. userStore.setToken(token)
  478. this.rememberAccount()
  479. userStore.getCurrentUser().then(res => {
  480. this.loading = false
  481. uni.switchTab({
  482. url: '/pages/index/index'
  483. });
  484. }).catch(() => {
  485. this.loading = false
  486. })
  487. }).catch((err) => {
  488. this.formData.code = ''
  489. this.changeCode()
  490. this.loading = false
  491. })
  492. }
  493. });
  494. },
  495. ssoLogin() {
  496. getTicket().then(res => {
  497. this.ssoTicket = res.data
  498. this.ssoUrl = this.preUrl + '?' + this.ticketParams + '=' + this.ssoTicket
  499. uni.setStorageSync('ssoUrl', this.ssoUrl)
  500. uni.navigateTo({
  501. url: `/pages/login/otherLogin?ssoTicket=${this.ssoTicket}`
  502. })
  503. })
  504. }
  505. }
  506. }
  507. </script>
  508. <style lang="scss">
  509. page {
  510. width: 100%;
  511. min-height: 100vh;
  512. }
  513. .remember-wrap {
  514. margin-top: 8px;
  515. & .remember-text {
  516. color: #9A9A9A;
  517. font-size: 13px;
  518. }
  519. }
  520. .logo-v {
  521. height: 100vh;
  522. display: flex;
  523. flex-direction: column;
  524. .login-version {
  525. position: fixed;
  526. right: 0px;
  527. top: 0px;
  528. width: 120rpx;
  529. height: 120rpx;
  530. background: url('../../static/image/login_version.png') no-repeat center;
  531. background-size: 100%;
  532. .login-version-text {
  533. width: 120rpx;
  534. height: 120rpx;
  535. line-height: 70rpx;
  536. text-align: center;
  537. color: #fff;
  538. font-size: 28rpx;
  539. transform: rotate(45deg);
  540. }
  541. }
  542. .login-bg {
  543. height: 726rpx;
  544. position: relative;
  545. image {
  546. width: 100%;
  547. height: 100%;
  548. }
  549. .logoImg {
  550. width: 160rpx;
  551. height: 160rpx;
  552. margin: 0 auto;
  553. position: absolute;
  554. /* #ifdef APP-PLUS */
  555. bottom: -90rpx;
  556. /* #endif */
  557. /* #ifndef APP-PLUS */
  558. bottom: 0;
  559. /* #endif */
  560. left: 0;
  561. right: 0;
  562. top: 270rpx;
  563. .image {
  564. width: 100%;
  565. height: 100%;
  566. border-radius: 20%;
  567. }
  568. }
  569. }
  570. .logo-hd {
  571. width: 100%;
  572. margin-top: -240rpx;
  573. .introduce {
  574. justify-content: center;
  575. align-items: center;
  576. .text-one {
  577. height: 70rpx;
  578. font-weight: 700;
  579. }
  580. .text-two {
  581. color: #999999;
  582. }
  583. }
  584. .loginSwitch {
  585. margin-top: 40rpx;
  586. justify-content: center;
  587. align-items: center;
  588. .tabs {
  589. color: #999999;
  590. position: relative;
  591. &::after {
  592. content: "";
  593. width: 64rpx;
  594. height: 4rpx;
  595. background-color: #356efe;
  596. margin-top: 15rpx;
  597. position: absolute;
  598. left: 0;
  599. bottom: -15rpx;
  600. display: block;
  601. border-radius: 50rpx;
  602. }
  603. &.active2 {
  604. &::after {
  605. left: 70%;
  606. }
  607. }
  608. .tab {
  609. width: 50%;
  610. height: 80upx;
  611. text-align: center;
  612. color: #AEAFB5;
  613. font-size: 32upx;
  614. &.active {
  615. color: #3281ff;
  616. }
  617. }
  618. }
  619. .loginInputBox {
  620. width: 100%;
  621. /* #ifdef APP-PLUS */
  622. margin-top: 120rpx;
  623. /* #endif */
  624. /* #ifndef APP-PLUS */
  625. margin-top: 80rpx;
  626. /* #endif */
  627. padding: 0 64rpx;
  628. .code-box {
  629. width: 100%;
  630. .code-img-box {
  631. flex: 0.1;
  632. }
  633. }
  634. .loginBtnBox {
  635. margin-top: 156rpx;
  636. }
  637. .u-form-item {
  638. padding: 12rpx 0;
  639. }
  640. }
  641. }
  642. }
  643. .copyright {
  644. width: 100%;
  645. height: 32rpx;
  646. position: fixed;
  647. bottom: 102rpx;
  648. left: 50%;
  649. right: 0;
  650. text-align: center;
  651. color: #A2A7BE;
  652. font-size: 12px;
  653. font-family: PingFang SC;
  654. transform: translateX(-50%);
  655. }
  656. .sso-login-btn {
  657. width: 100%;
  658. padding: 0 64rpx;
  659. /* #ifdef APP-PLUS */
  660. margin-top: 404rpx;
  661. /* #endif */
  662. /* #ifndef APP-PLUS */
  663. margin-top: 364rpx;
  664. /* #endif */
  665. }
  666. }
  667. .other-list {
  668. display: flex;
  669. align-items: center;
  670. justify-content: space-around;
  671. .other-item {
  672. width: 30px;
  673. height: 30px;
  674. line-height: 30px;
  675. text-align: center;
  676. cursor: pointer;
  677. border-radius: 50%;
  678. text {
  679. font-size: 20px;
  680. color: #a0acb7;
  681. }
  682. }
  683. }
  684. .mian {
  685. background: url('/static/image/tenancy.png');
  686. height: 100%;
  687. .top {
  688. .img_box {
  689. margin-top: 20rpx;
  690. margin-left: 30%;
  691. .img {
  692. height: 50rpx;
  693. width: 50rpx;
  694. text-align: center;
  695. }
  696. }
  697. }
  698. }
  699. .title {
  700. margin-top: -55rpx;
  701. margin-left: 260rpx;
  702. font-size: 32rpx;
  703. }
  704. .info {
  705. margin: auto auto;
  706. width: 96%;
  707. height: 300rpx;
  708. background-color: #fff;
  709. margin-bottom: 20rpx;
  710. border-radius: 10rpx;
  711. border-left: 10rpx solid #9DC8FA;
  712. overflow: hidden;
  713. text-overflow: ellipsis;
  714. white-space: nowrap;
  715. }
  716. .user-name {
  717. font-weight: bold;
  718. font-size: 32rpx;
  719. margin-left: 20rpx;
  720. margin-bottom: 20rpx;
  721. margin-top: 30rpx;
  722. width: 100%;
  723. height: 60rpx;
  724. overflow: hidden;
  725. text-overflow: ellipsis;
  726. white-space: nowrap;
  727. }
  728. .user-tenancy {
  729. font-size: 28rpx;
  730. margin-left: 20rpx;
  731. margin-bottom: 20rpx;
  732. width: 100%;
  733. overflow: hidden;
  734. text-overflow: ellipsis;
  735. white-space: nowrap;
  736. }
  737. </style>