Login.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <!-- b y 7 5 2 7 1 8 9 2 0 -->
  2. <template>
  3. <div class="main">
  4. <a-form :form="form" class="user-layout-login" ref="formLogin" id="formLogin">
  5. <a-form-item>
  6. <a-input
  7. size="large"
  8. v-decorator="['loginName', { initialValue: '', rules: validatorRules.loginName.rules }]"
  9. type="text"
  10. placeholder="请输入用户名"
  11. >
  12. <a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }" />
  13. </a-input>
  14. </a-form-item>
  15. <a-form-item>
  16. <a-input-password
  17. v-decorator="['password', { initialValue: '', rules: validatorRules.password.rules }]"
  18. size="large"
  19. type="password"
  20. autocomplete="false"
  21. placeholder="请输入密码"
  22. >
  23. <a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }" />
  24. </a-input-password>
  25. </a-form-item>
  26. <a-row :gutter="0">
  27. <a-col :span="14">
  28. <a-form-item>
  29. <a-input
  30. v-decorator="['inputCode', { initialValue: '', rules: validatorRules.inputCode.rules }]"
  31. size="large"
  32. type="text"
  33. default-value=""
  34. placeholder="请输入验证码"
  35. >
  36. <a-icon slot="prefix" type="smile" :style="{ color: 'rgba(0,0,0,.25)' }" />
  37. </a-input>
  38. </a-form-item>
  39. </a-col>
  40. <a-col :span="10" style="text-align: right">
  41. <img v-if="requestCodeSuccess" style="margin-top: 2px" :src="randCodeImage" @click="handleChangeCheckCode" />
  42. <img v-else style="margin-top: 2px" src="../../assets/checkcode.png" @click="handleChangeCheckCode" />
  43. </a-col>
  44. </a-row>
  45. <a-form-item>
  46. <a-checkbox :checked="checked" @change="handleChange">记住密码</a-checkbox>
  47. </a-form-item>
  48. <a-form-item style="margin-top: 16px">
  49. <a-button
  50. size="large"
  51. type="primary"
  52. htmlType="submit"
  53. class="login-button"
  54. :loading="loginBtn"
  55. @click.stop.prevent="handleSubmit"
  56. :disabled="loginBtn"
  57. >登 录
  58. </a-button>
  59. </a-form-item>
  60. <div class="login-copyright" v-if="device === 'mobile'">
  61. <a-row>
  62. <a-col>
  63. © 2015-2030 Powered By
  64. <a style="color: #00458a" :href="systemUrl" target="_blank">官方网站</a>
  65. </a-col>
  66. </a-row>
  67. </div>
  68. </a-form>
  69. </div>
  70. </template>
  71. <!-- BY cao_yu_li -->
  72. <script>
  73. import md5 from 'md5'
  74. import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
  75. import { mapActions } from 'vuex'
  76. import { timeFix } from '@/utils/util'
  77. import Vue from 'vue'
  78. import { getPlatformConfigByKey } from '@/api/api'
  79. import { ACCESS_TOKEN, ENCRYPTED_STRING } from '@/store/mutation-types'
  80. import { getAction } from '@/api/manage'
  81. import { getEncryptedString } from '@/utils/encryption/aesEncrypt'
  82. import { mixinDevice } from '@/utils/mixin.js'
  83. export default {
  84. components: {
  85. TwoStepCaptcha,
  86. },
  87. mixins: [mixinDevice],
  88. data() {
  89. return {
  90. customActiveKey: 'tab1',
  91. systemTitle: window.SYS_TITLE,
  92. systemUrl: window.SYS_URL,
  93. loginBtn: false,
  94. // login type: 0 email, 1 username, 2 telephone
  95. loginType: 0,
  96. requiredTwoStepCaptcha: false,
  97. stepCaptchaVisible: false,
  98. form: this.$form.createForm(this),
  99. encryptedString: {
  100. key: '',
  101. iv: '',
  102. },
  103. state: {
  104. time: 60,
  105. smsSendBtn: false,
  106. },
  107. validatorRules: {
  108. loginName: { rules: [{ required: true, message: '请输入用户名!' }, { validator: this.handleLoginName }] },
  109. password: { rules: [{ required: true, message: '请输入密码!', validator: 'click' }] },
  110. inputCode: { rules: [{ required: true, message: '请输入验证码!', validator: 'click' }] },
  111. },
  112. verifiedCode: '',
  113. inputCodeContent: '', //20200510 cfm: 为方便测试,不输入验证码可 ""-->"xxxx"
  114. inputCodeNull: true,
  115. departList: [],
  116. departVisible: false,
  117. departSelected: '',
  118. currentUsername: '',
  119. validate_status: '',
  120. currdatetime: '',
  121. uuid: '',
  122. randCodeImage: '',
  123. registerFlag: '',
  124. requestCodeSuccess: false,
  125. checked: false,
  126. }
  127. },
  128. created() {
  129. this.loadInfo()
  130. this.checkScreen()
  131. this.currdatetime = new Date().getTime()
  132. Vue.ls.remove(ACCESS_TOKEN)
  133. this.getRouterData()
  134. this.getRegisterFlag()
  135. this.handleChangeCheckCode()
  136. },
  137. methods: {
  138. ...mapActions(['Login', 'Logout']),
  139. // handler
  140. loadInfo() {
  141. //从缓存中获取登录名和密码
  142. this.$nextTick(() => {
  143. if (Vue.ls.get('cache_loginName') && Vue.ls.get('cache_password')) {
  144. this.form.setFieldsValue({ loginName: Vue.ls.get('cache_loginName') })
  145. this.form.setFieldsValue({ password: Vue.ls.get('cache_password') })
  146. this.checked = true
  147. }
  148. })
  149. //从注册页面跳转过来,给登录名进行赋值
  150. if (this.$route.params.loginName) {
  151. this.$nextTick(() => {
  152. //先清空缓存
  153. Vue.ls.remove('cache_loginName')
  154. Vue.ls.remove('cache_password')
  155. this.form.setFieldsValue({ loginName: this.$route.params.loginName })
  156. this.form.setFieldsValue({ password: '' })
  157. this.checked = false
  158. })
  159. }
  160. },
  161. handleLoginName(rule, value, callback) {
  162. const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/
  163. if (regex.test(value)) {
  164. this.loginType = 0
  165. } else {
  166. this.loginType = 1
  167. }
  168. callback()
  169. },
  170. //切换勾选
  171. handleChange(e) {
  172. this.checked = e.target.checked
  173. },
  174. handleChangeCheckCode() {
  175. getAction('/user/randomImage')
  176. .then((res) => {
  177. if (res.code == 200) {
  178. this.uuid = res.data.uuid
  179. this.randCodeImage = res.data.base64
  180. this.requestCodeSuccess = true
  181. } else {
  182. this.$message.error(res.data)
  183. this.requestCodeSuccess = false
  184. }
  185. })
  186. .catch(() => {
  187. this.requestCodeSuccess = false
  188. })
  189. },
  190. handleSubmit() {
  191. let that = this
  192. let loginParams = {}
  193. that.loginBtn = true
  194. // 使用账户密码登陆
  195. if (that.customActiveKey === 'tab1') {
  196. that.form.validateFields(['loginName', 'password', 'inputCode'], { force: true }, (err, values) => {
  197. if (!err) {
  198. loginParams.loginName = values.loginName
  199. loginParams.password = md5(values.password)
  200. loginParams.code = values.inputCode
  201. loginParams.uuid = that.uuid
  202. if (that.checked) {
  203. //勾选的时候进行缓存
  204. Vue.ls.set('cache_loginName', values.loginName)
  205. Vue.ls.set('cache_password', values.password)
  206. } else {
  207. //没勾选的时候清缓存
  208. Vue.ls.remove('cache_loginName')
  209. Vue.ls.remove('cache_password')
  210. }
  211. that
  212. .Login(loginParams)
  213. .then((res) => {
  214. this.departConfirm(res, loginParams.loginName)
  215. })
  216. .catch((err) => {
  217. that.requestFailed(err)
  218. })
  219. } else {
  220. that.loginBtn = false
  221. }
  222. })
  223. }
  224. },
  225. loginSuccess(res) {
  226. let that = this
  227. this.$router.push({ path: '/dashboard/analysis' })
  228. this.$notification.success({
  229. message: '欢迎',
  230. description: `${timeFix()},欢迎回来`,
  231. })
  232. if (res.data.pwdSimple) {
  233. setTimeout(function () {
  234. that.$notification.warning({
  235. message: '友情提醒',
  236. description: '密码过于简单,请尽快修改',
  237. })
  238. }, 3000)
  239. }
  240. if (res.data && res.data.user) {
  241. if (res.data.user.loginName === 'admin') {
  242. let desc =
  243. 'admin只是平台运维用户,真正的管理员是租户(测试账号为jsh),admin不能编辑任何业务数据,只能配置平台菜单和创建租户'
  244. this.$message.info(desc, 30)
  245. } else {
  246. getPlatformConfigByKey({ platformKey: 'bill_excel_url' }).then((res) => {
  247. if (res && res.code === 200) {
  248. if (res.data.platformValue) {
  249. Vue.ls.set('isShowExcel', true)
  250. } else {
  251. Vue.ls.set('isShowExcel', false)
  252. }
  253. }
  254. })
  255. getAction('/user/infoWithTenant', {}).then((res) => {
  256. if (res && res.code === 200) {
  257. let currentTime = new Date() //新建一个日期对象,默认现在的时间
  258. let expireTime = new Date(res.data.expireTime) //设置过去的一个时间点,"yyyy-MM-dd HH:mm:ss"格式化日期
  259. let type = res.data.type //租户类型,0免费租户,1付费租户
  260. let difftime = expireTime - currentTime //计算时间差
  261. let tipInfo = '您好,服务即将到期,请及时续费!'
  262. //0免费租户-如果距离到期还剩5天就进行提示续费
  263. if (type === '0' && difftime < 86400000 * 5) {
  264. this.$message.warning(tipInfo, 8)
  265. }
  266. //1付费租户-如果距离到期还剩15天就进行提示续费
  267. if (type === '1' && difftime < 86400000 * 15) {
  268. this.$message.warning(tipInfo, 8)
  269. }
  270. }
  271. })
  272. }
  273. }
  274. this.initMPropertyShort()
  275. },
  276. cmsFailed(err) {
  277. this.$notification['error']({
  278. message: '登录失败',
  279. description: err,
  280. duration: 4,
  281. })
  282. },
  283. requestFailed(err) {
  284. this.$notification['error']({
  285. message: '登录失败',
  286. description:
  287. ((err.response || {}).data || {}).message || err.message || err.data.message || '请求出现错误,请稍后再试',
  288. duration: 4,
  289. })
  290. //验证码刷新
  291. this.form.setFieldsValue({ inputCode: '' })
  292. this.handleChangeCheckCode()
  293. this.loginBtn = false
  294. },
  295. generateCode(value) {
  296. this.verifiedCode = value.toLowerCase()
  297. },
  298. departConfirm(res, loginName) {
  299. if (res.code === 200) {
  300. let err = {}
  301. if (res.data.msgTip === 'user can login') {
  302. this.loginSuccess(res)
  303. } else if (res.data.msgTip === 'user is not exist') {
  304. err.message = '用户不存在'
  305. this.requestFailed(err)
  306. this.Logout()
  307. } else if (res.data.msgTip === 'user password error') {
  308. err.message = '用户密码不正确'
  309. this.requestFailed(err)
  310. this.Logout()
  311. } else if (res.data.msgTip === 'user is black') {
  312. err.message = '用户被禁用'
  313. this.requestFailed(err)
  314. this.Logout()
  315. } else if (res.data.msgTip === 'tenant is black') {
  316. if (loginName === 'jsh') {
  317. err.message = 'jsh用户已停用,请注册租户进行体验!'
  318. } else {
  319. err.message = '用户所属的租户被禁用'
  320. }
  321. this.requestFailed(err)
  322. this.Logout()
  323. } else if (res.data.msgTip === 'tenant is expire') {
  324. err.message = '试用期已结束,请联系客服续费'
  325. this.requestFailed(err)
  326. this.Logout()
  327. } else if (res.data.msgTip === 'access service error') {
  328. err.message = '查询服务异常'
  329. this.requestFailed(err)
  330. this.Logout()
  331. }
  332. } else {
  333. this.requestFailed(res)
  334. this.Logout()
  335. }
  336. },
  337. getRouterData() {
  338. this.$nextTick(() => {
  339. if (this.$route.params.username) {
  340. this.form.setFieldsValue({
  341. username: this.$route.params.username,
  342. })
  343. }
  344. })
  345. },
  346. getRegisterFlag() {
  347. getAction('/platformConfig/getPlatform/registerFlag').then((res) => {
  348. this.registerFlag = res + ''
  349. })
  350. },
  351. //获取密码加密规则
  352. getEncrypte() {
  353. var encryptedString = Vue.ls.get(ENCRYPTED_STRING)
  354. if (encryptedString == null) {
  355. getEncryptedString().then((data) => {
  356. this.encryptedString = data
  357. })
  358. } else {
  359. this.encryptedString = encryptedString
  360. }
  361. },
  362. //加载商品属性
  363. initMPropertyShort() {
  364. getAction('/materialProperty/getAllList').then((res) => {
  365. if (res && res.code === 200) {
  366. if (res.data) {
  367. let thisRows = res.data //属性列表
  368. Vue.ls.set('materialPropertyList', thisRows, 7 * 24 * 60 * 60 * 1000)
  369. }
  370. }
  371. })
  372. },
  373. checkScreen() {
  374. let percentage = '100%'
  375. let basicWidth = 1920
  376. const currentWidth = window.screen.width
  377. const currentHeight = window.screen.height
  378. //浏览器的当前比例
  379. const currentRatio = window.devicePixelRatio.toFixed(2)
  380. //浏览器需要调整的比例
  381. let needRatio = 1
  382. let ratio = currentWidth / basicWidth
  383. if (ratio > 0.5 && ratio < 0.67) {
  384. percentage = '50%'
  385. needRatio = 0.5
  386. }
  387. if (ratio >= 0.67 && ratio < 0.75) {
  388. percentage = '67%'
  389. needRatio = 0.67
  390. } else if (ratio >= 0.75 && ratio < 0.8) {
  391. percentage = '75%'
  392. needRatio = 0.75
  393. } else if (ratio >= 0.8 && ratio < 0.9) {
  394. percentage = '80%'
  395. needRatio = 0.8
  396. } else if (ratio >= 1.1 && ratio < 1.25) {
  397. percentage = '110%'
  398. needRatio = 1.1
  399. } else if (ratio >= 1.25 && ratio < 1.5) {
  400. percentage = '125%'
  401. needRatio = 1.25
  402. } else if (ratio >= 1.5 && ratio < 1.75) {
  403. percentage = '150%'
  404. needRatio = 1.5
  405. }
  406. //console.log(currentRatio)
  407. //console.log(needRatio)
  408. if (currentRatio - 0 !== needRatio) {
  409. this.openNotificationWithIcon('warning', currentWidth, currentHeight, percentage)
  410. }
  411. },
  412. openNotificationWithIcon(type, currentWidth, currentHeight, percentage) {
  413. this.$notification[type]({
  414. message: '浏览器的缩放比例调整提示',
  415. description:
  416. '检测到您显示器的分辨率为:' +
  417. currentWidth +
  418. '*' +
  419. currentHeight +
  420. ' ,为了获得更好的操作体验,建议您将浏览器的缩放比例调整至' +
  421. percentage,
  422. duration: 10,
  423. })
  424. },
  425. },
  426. }
  427. </script>
  428. <style lang="less" scoped>
  429. .user-layout-login {
  430. label {
  431. font-size: 14px;
  432. }
  433. .ant-form-item {
  434. margin-bottom: 16px;
  435. }
  436. .getCaptcha {
  437. display: block;
  438. width: 100%;
  439. height: 40px;
  440. }
  441. .forge-password {
  442. font-size: 14px;
  443. font-weight: bolder;
  444. }
  445. button.login-button {
  446. padding: 0 15px;
  447. font-size: 16px;
  448. height: 40px;
  449. width: 100%;
  450. }
  451. .user-login-other {
  452. text-align: left;
  453. margin-top: 24px;
  454. line-height: 22px;
  455. .item-icon {
  456. font-size: 24px;
  457. color: rgba(0, 0, 0, 0.2);
  458. margin-left: 16px;
  459. vertical-align: middle;
  460. cursor: pointer;
  461. transition: color 0.3s;
  462. &:hover {
  463. color: #1890ff;
  464. }
  465. }
  466. .register {
  467. float: right;
  468. }
  469. }
  470. .weixin {
  471. padding-left: 10px;
  472. color: red;
  473. cursor: pointer;
  474. }
  475. }
  476. </style>
  477. <style>
  478. .valid-error .ant-select-selection__placeholder {
  479. color: #f5222d;
  480. }
  481. .login-copyright {
  482. text-align: center;
  483. margin-top: 20px;
  484. }
  485. .login-copyright,
  486. .login-copyright a {
  487. color: #666;
  488. }
  489. </style>