index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. <template>
  2. <div class="page-account">
  3. <div class="container" :class="[fullWidth > 768 ? 'containerSamll' : 'containerBig']">
  4. <!--<swiper :options="swiperOption" class="swiperPross" v-if="fullWidth > 768">
  5. <swiper-slide class="swiperPic" v-for="(item, index) in swiperList" :key="index">
  6. <img :src="item.slide" alt="" />
  7. </swiper-slide>
  8. <div class="swiper-pagination" slot="pagination"></div>
  9. </swiper>-->
  10. <div class="index_from page-account-container from-wh">
  11. <div class="page-account-top">
  12. <div class="page-account-top-logo">
  13. <img :src="login_logo" alt="logo" style="width: 100%; height: 74px" />
  14. </div>
  15. </div>
  16. <el-form ref="formInline" :model="formInline" :rules="ruleInline" @keyup.enter="handleSubmit('formInline')">
  17. <el-form-item prop="username">
  18. <el-input
  19. type="text"
  20. v-model="formInline.username"
  21. prefix="ios-contact-outline"
  22. placeholder="请输入用户名"
  23. size="large"
  24. />
  25. </el-form-item>
  26. <el-form-item prop="password">
  27. <el-input
  28. type="password"
  29. v-model="formInline.password"
  30. prefix="ios-lock-outline"
  31. placeholder="请输入密码"
  32. size="large"
  33. show-password
  34. />
  35. </el-form-item>
  36. <!-- <el-form-item prop="code">
  37. <div class="code">
  38. <el-input
  39. type="text"
  40. v-model="formInline.code"
  41. prefix="ios-keypad-outline"
  42. placeholder="请输入验证码"
  43. size="large"
  44. />
  45. <img :src="imgcode" class="pictrue" v-db-click @click="captchas" />
  46. </div>
  47. </el-form-item> -->
  48. <el-form-item class="pt10">
  49. <el-button
  50. type="primary"
  51. :loading="loading"
  52. size="large"
  53. v-db-click
  54. @click="handleSubmit('formInline')"
  55. class="btn"
  56. >登录</el-button
  57. >
  58. </el-form-item>
  59. </el-form>
  60. </div>
  61. </div>
  62. <Verify
  63. @success="success"
  64. captchaType="blockPuzzle"
  65. :imgSize="{ width: '330px', height: '155px' }"
  66. ref="verify"
  67. ></Verify>
  68. <!--<div class="footer">
  69. <div class="pull-right" v-if="copyright">{{ copyright }}</div>
  70. <div class="pull-right" v-else>
  71. Copyright © 2014-2025 <a href="https://www.crmeb.com" target="_blank">{{ version }}</a>
  72. </div>
  73. </div>-->
  74. </div>
  75. </template>
  76. <script>
  77. import { AccountLogin, loginInfoApi } from '@/api/account';
  78. import { getWorkermanUrl } from '@/api/kefu';
  79. import { setCookies } from '@/libs/util';
  80. import '@/assets/js/canvas-nest.min';
  81. import Verify from '@/components/verifition/Verify';
  82. import { PrevLoading } from '@/utils/loading.js';
  83. import { formatFlatteningRoutes, findFirstNonNullChildren } from '@/libs/system';
  84. import { Local } from '@/utils/storage.js';
  85. export default {
  86. components: {
  87. Verify,
  88. },
  89. data() {
  90. return {
  91. fullWidth: document.documentElement.clientWidth,
  92. swiperOption: {
  93. pagination: '.swiper-pagination',
  94. autoplay: true,
  95. },
  96. loading: false,
  97. isShow: false,
  98. imgcode: '',
  99. formInline: {
  100. username: '',
  101. password: '',
  102. },
  103. ruleInline: {
  104. username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
  105. password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
  106. },
  107. login_captcha: 0,
  108. login_logo: '',
  109. swiperList: [],
  110. defaultSwiperList: require('@/assets/images/sw.png'),
  111. key: '',
  112. copyright: '',
  113. version: '',
  114. timer: null,
  115. };
  116. },
  117. created() {
  118. document.onkeydown = (e) => {
  119. if (this.$route.name === 'login' && (e.keyCode === 13 || e.which === 13)) {
  120. this.handleSubmit('formInline');
  121. }
  122. };
  123. window.addEventListener('resize', this.handleResize);
  124. },
  125. mounted() {
  126. this.$nextTick(() => {
  127. this.handleResize();
  128. this.swiperData();
  129. });
  130. },
  131. beforeDestroy() {
  132. window.removeEventListener('resize', this.handleResize);
  133. document.onkeydown = null;
  134. const canvas = document.getElementsByTagName('canvas')[0];
  135. if (canvas) canvas.removeAttribute('class', 'index_bg');
  136. },
  137. methods: {
  138. swiperData() {
  139. loginInfoApi()
  140. .then((res) => {
  141. const data = res.data || {};
  142. document.title = `${data.site_name} - 登录`;
  143. localStorage.setItem('ADMIN_TITLE', data.site_name || '');
  144. this.$store.commit('setAdminTitle', data.site_name);
  145. this.login_logo = data.login_logo || require('@/assets/images/logo.png');
  146. this.swiperList = data.slide && data.slide.length ? data.slide : [{ slide: this.defaultSwiperList }];
  147. this.key = data.key;
  148. this.copyright = data.copyright;
  149. this.version = data.version;
  150. this.login_captcha = data.login_captcha;
  151. })
  152. .catch((err) => {
  153. this.$message.error(err);
  154. this.login_logo = require('@/assets/images/logo.png');
  155. this.swiperList = [{ slide: this.defaultSwiperList }];
  156. });
  157. },
  158. success(params) {
  159. this.closeModel(params);
  160. },
  161. closeModel(params) {
  162. this.isShow = false;
  163. this.loading = true;
  164. AccountLogin({
  165. account: this.formInline.username,
  166. pwd: this.formInline.password,
  167. key: this.key,
  168. captchaType: 'blockPuzzle',
  169. captchaVerification: params ? params.captchaVerification : '',
  170. })
  171. .then(async (res) => {
  172. const data = res.data;
  173. const expires = this.getExpiresTime(data.expires_time);
  174. setCookies('uuid', data.user_info.id, expires);
  175. setCookies('token', data.token, expires);
  176. setCookies('expires_time', data.expires_time, expires);
  177. Local.set('PERMISSIONS', data.site_func);
  178. this.$store.commit('userInfo/uniqueAuth', data.unique_auth);
  179. this.$store.commit('userInfo/userInfo', data.user_info);
  180. this.$store.commit('menus/setopenMenus', []);
  181. this.$store.commit('menus/getmenusNav', data.menus);
  182. this.$store.dispatch('routesList/setRoutesList', data.menus);
  183. const arr = formatFlatteningRoutes(this.$router.options.routes);
  184. this.formatTwoStageRoutes(arr);
  185. this.$store.commit('menus/setOneLvMenus', arr);
  186. const routes = formatFlatteningRoutes(data.menus);
  187. this.$store.commit('menus/setOneLvRoute', routes);
  188. this.$store.commit('userInfo/name', data.user_info.account);
  189. this.$store.commit('userInfo/avatar', data.user_info.head_pic);
  190. this.$store.commit('userInfo/access', data.unique_auth);
  191. this.$store.commit('userInfo/logo', data.logo);
  192. this.$store.commit('userInfo/logoSmall', data.logo_square);
  193. this.$store.commit('userInfo/version', data.version);
  194. this.$store.commit('userInfo/newOrderAudioLink', data.newOrderAudioLink);
  195. this.login_captcha = 0;
  196. try {
  197. if (data.queue === false) {
  198. this.$notify.warning({
  199. title: '温馨提示',
  200. dangerouslyUseHTMLString: true,
  201. message:
  202. '您的【消息队列】未开启,没有开启会导致异步任务无法执行。请尽快执行命令开启!!<a href="https://doc.crmeb.com/single/v54/13667" target="_blank">点击查看开启方法</a>',
  203. duration: 30000,
  204. });
  205. }
  206. if (data.timer === false) {
  207. setTimeout(() => {
  208. this.$notify.warning({
  209. title: '温馨提示',
  210. dangerouslyUseHTMLString: true,
  211. message:
  212. '您的【定时任务】未开启,没有开启会导致自动收货、未支付自动取消订单、订单自动好评、拼团到期退款等任务无法正常执行。请尽快执行命令开启!!<a href="https://doc.crmeb.com/single/v54/13667" target="_blank">点击查看开启方法</a>',
  213. duration: 30000,
  214. });
  215. }, 0);
  216. }
  217. this.checkSocket();
  218. } catch (e) {}
  219. PrevLoading.start();
  220. this.$router.push({
  221. path: data.menus.length ? findFirstNonNullChildren(data.menus).path : this.$routeProStr + '/',
  222. });
  223. })
  224. .catch((res) => {
  225. const data = res || {};
  226. this.$message.error(data.msg || '登录失败');
  227. if (res && res.data) this.login_captcha = res.data.login_captcha;
  228. })
  229. .finally(() => {
  230. setTimeout(() => {
  231. this.loading = false;
  232. }, 1000);
  233. });
  234. },
  235. formatTwoStageRoutes(arr) {
  236. if (!arr.length) return false;
  237. const cacheList = [];
  238. arr.forEach((v) => {
  239. if (v && v.meta && v.meta.keepAlive) {
  240. cacheList.push(v.name);
  241. }
  242. });
  243. if (cacheList.length) {
  244. this.$store.dispatch('keepAliveNames/setCacheKeepAlive', cacheList);
  245. }
  246. },
  247. checkSocket() {
  248. getWorkermanUrl().then((res) => {
  249. const url = res.data.admin;
  250. let isNotice = false;
  251. const socket = new window.WebSocket(url);
  252. socket.onopen = () => {
  253. isNotice = true;
  254. socket.close();
  255. };
  256. socket.onerror = socket.onclose = () => {
  257. if (!isNotice) {
  258. isNotice = true;
  259. this.$notify.warning({
  260. title: '温馨提示',
  261. message:
  262. '您的【长连接】未开启,没有开启会导致系统默认客服无法使用,后台订单通知无法收到。请尽快执行命令开启!!<a href="https://doc.crmeb.com/single/v54/13667" target="_blank">点击查看开启方法</a>',
  263. dangerouslyUseHTMLString: true,
  264. duration: 30000,
  265. });
  266. }
  267. };
  268. });
  269. },
  270. getExpiresTime(expiresTime) {
  271. const nowTimeNum = Math.round(Date.now() / 1000);
  272. const expiresTimeNum = expiresTime - nowTimeNum;
  273. return parseFloat(expiresTimeNum / 60 / 60 / 24);
  274. },
  275. closefail() {
  276. this.$message.error('校验错误');
  277. },
  278. handleResize() {
  279. this.fullWidth = document.documentElement.clientWidth;
  280. const canvas = document.getElementsByTagName('canvas')[0];
  281. if (canvas) {
  282. if (this.fullWidth < 768) {
  283. canvas.removeAttribute('class', 'index_bg');
  284. } else {
  285. canvas.className = 'index_bg';
  286. }
  287. }
  288. },
  289. handleSubmit(name) {
  290. this.$refs[name].validate((valid) => {
  291. if (valid) {
  292. if (this.login_captcha === 1) {
  293. this.$refs.verify.show();
  294. } else {
  295. this.closeModel();
  296. }
  297. }
  298. });
  299. },
  300. },
  301. };
  302. </script>
  303. <style lang="scss" scoped>
  304. .page-account {
  305. display: flex;
  306. width: 100%;
  307. background-image: url('../../../assets/images/bg.jpg');
  308. background-size: cover;
  309. background-position: center;
  310. flex-direction: column;
  311. justify-content: center;
  312. align-items: center;
  313. height: 100vh;
  314. overflow: auto;
  315. }
  316. .page-account .code {
  317. display: flex;
  318. align-items: center;
  319. justify-content: center;
  320. }
  321. .page-account .code .pictrue {
  322. height: 40px;
  323. }
  324. .swiperPross {
  325. border-radius: 12px 0px 0px 12px;
  326. }
  327. .swiperPross,
  328. .swiperPic,
  329. .swiperPic img {
  330. width: 510px;
  331. height: 100%;
  332. }
  333. .swiperPic img {
  334. width: 100%;
  335. height: 100%;
  336. }
  337. .container {
  338. height: 400px !important;
  339. padding: 0 !important;
  340. border-radius: 12px;
  341. z-index: 1;
  342. display: flex;
  343. }
  344. .containerSamll {
  345. /* width: 56% !important; */
  346. background: #fff !important;
  347. }
  348. .containerBig {
  349. width: auto !important;
  350. background: #f7f7f7 !important;
  351. }
  352. .index_from {
  353. padding: 32px 40px 32px 40px;
  354. height: 400px;
  355. box-sizing: border-box;
  356. }
  357. .page-account-top {
  358. padding: 20px 0 24px 0 !important;
  359. box-sizing: border-box !important;
  360. display: flex;
  361. justify-content: center;
  362. }
  363. .page-account-container {
  364. border-radius: 0px 6px 6px 0px;
  365. }
  366. .btn {
  367. width: 100%;
  368. background: linear-gradient(90deg, rgba(25, 180, 241, 1) 0%, rgba(14, 115, 232, 1) 100%) !important;
  369. }
  370. .captchaBox {
  371. width: 310px;
  372. }
  373. input {
  374. display: block;
  375. width: 290px;
  376. line-height: 40px;
  377. margin: 10px 0;
  378. padding: 0 10px;
  379. outline: none;
  380. border: 1px solid #c8cccf;
  381. border-radius: 4px;
  382. color: #6a6f77;
  383. }
  384. #msg {
  385. width: 100%;
  386. line-height: 40px;
  387. font-size: 14px;
  388. text-align: center;
  389. }
  390. a:link,
  391. a:visited,
  392. a:hover,
  393. a:active {
  394. margin-left: 100px;
  395. color: #0366d6;
  396. }
  397. .index_from ::v-deep .ivu-input-large {
  398. font-size: 14px !important;
  399. }
  400. .from-wh {
  401. width: 400px;
  402. }
  403. .pull-right {
  404. float: right !important;
  405. }
  406. ::v-deep .el-button--primary {
  407. border: none;
  408. }
  409. ::v-deep .el-button {
  410. padding: 13px 20px !important;
  411. }
  412. .pull-right {
  413. float: right !important;
  414. color: #666;
  415. }
  416. .pull-right a {
  417. margin-left: 0;
  418. color: #666;
  419. }
  420. .footer {
  421. position: fixed;
  422. bottom: 0;
  423. width: 100%;
  424. left: 0;
  425. margin: 0;
  426. background: rgba(255, 255, 255, 0.8);
  427. border-top: 1px solid #e7eaec;
  428. overflow: hidden;
  429. padding: 10px 20px;
  430. height: 36px;
  431. line-height: 18px;
  432. z-index: 999;
  433. }
  434. </style>