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.

337 lines
9.9 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
4 days ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
4 days ago
1 year ago
6 months ago
1 year ago
1 year ago
4 days ago
1 year ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. <template>
  2. <div class="pda-login">
  3. <div class="login-card">
  4. <!-- 语言切换 -->
  5. <div class="lang-switch">
  6. <el-dropdown trigger="click">
  7. <span class="lang-text">
  8. {{ $t('home.locale') }}<i class="el-icon-arrow-down el-icon--right"></i>
  9. </span>
  10. <el-dropdown-menu slot="dropdown">
  11. <el-dropdown-item
  12. v-for="lang in languageList"
  13. :key="lang.languageCode"
  14. @click.native="switch_the_language(lang.languageCode)"
  15. >
  16. {{ lang.languageName }}
  17. </el-dropdown-item>
  18. </el-dropdown-menu>
  19. </el-dropdown>
  20. </div>
  21. <div class="logo-area">
  22. <svg viewBox="0 0 100 80" width="90" height="72">
  23. <!-- 立体货架左侧 -->
  24. <polygon points="20,30 20,70 50,80 50,40" fill="#2B2523"/>
  25. <!-- 立体货架右侧 -->
  26. <polygon points="50,40 50,80 90,70 90,30" fill="#23201E"/>
  27. <!-- 立体货架顶面 -->
  28. <polygon points="20,30 50,20 90,30 50,40" fill="#3A3532"/>
  29. <!-- 横向货架层 -->
  30. <polyline points="20,40 50,48" stroke="#fff" stroke-width="2" opacity="0.7"/>
  31. <polyline points="20,50 50,56" stroke="#fff" stroke-width="2" opacity="0.7"/>
  32. <polyline points="20,60 50,64" stroke="#fff" stroke-width="2" opacity="0.7"/>
  33. <!-- 横向货架层 -->
  34. <polyline points="90,40 50,48" stroke="#fff" stroke-width="2" opacity="0.7"/>
  35. <polyline points="90,50 50,56" stroke="#fff" stroke-width="2" opacity="0.7"/>
  36. <polyline points="90,60 50,64" stroke="#fff" stroke-width="2" opacity="0.7"/>
  37. <!-- 纵向货架分隔线 -->
  38. <polyline points="35,25 35,75" stroke="#fff" stroke-width="2" opacity="0.5"/>
  39. <polyline points="65,25 65,75" stroke="#fff" stroke-width="2" opacity="0.5"/>
  40. <!-- #17B3A3色系货箱在货架格子里 -->
  41. <rect x="53" y="44" width="8" height="6" fill="#17B3A3" rx="1" />
  42. <rect x="73" y="54" width="8" height="6" fill="#17B3A3" rx="1" />
  43. <rect x="33" y="62" width="8" height="6" fill="#17B3A3" rx="1" />
  44. </svg>
  45. <h1>{{ $t('home.pdaName') }}</h1>
  46. </div>
  47. <el-form class="login-form" @keyup.enter.native="dataFormSubmit()">
  48. <!-- 用户名 -->
  49. <div class="input-group" :class="{focus: usernameFocus}">
  50. <input placeholder="Username"
  51. type="text"
  52. v-model="dataForm.userName"
  53. @focus="usernameFocus=true"
  54. @blur="handleUsernameBlur"
  55. required />
  56. </div>
  57. <!-- 密码 -->
  58. <div class="input-group" :class="{focus: passwordFocus}">
  59. <input placeholder="Password"
  60. :type="showPassword ? 'text' : 'password'"
  61. v-model="dataForm.password"
  62. @focus="passwordFocus=true"
  63. @blur="passwordFocus=false"
  64. required />
  65. </div>
  66. <!-- 工厂下拉框site -->
  67. <el-select
  68. v-if="siteList.length"
  69. v-model="selectedSite"
  70. placeholder="请选择工厂"
  71. style="width: 100%; margin-bottom: 1.5rem"
  72. :popper-append-to-body="false">
  73. <el-option
  74. v-for="item in siteList"
  75. :key="item.siteCode"
  76. :label="item.siteName"
  77. :value="item.siteCode" />
  78. </el-select>
  79. <!-- 登录按钮 -->
  80. <button type="submit" class="login-btn" :disabled="loading" @click.prevent="dataFormSubmit">
  81. <span v-if="!loading">Login</span>
  82. <span v-else class="loading-dots"></span>
  83. </button>
  84. </el-form>
  85. </div>
  86. </div>
  87. </template>
  88. <script>
  89. import {searchSysLanguage} from "@/api/sysLanguage.js"
  90. import {getUserAuthorizedSites, getUserAuthorizedSubSiteCodes} from "@/api/factory/accessSite.js"
  91. export default {
  92. data() {
  93. return {
  94. dataForm: {
  95. userName: '',
  96. password: '',
  97. uuid: ''
  98. },
  99. usernameFocus: false,
  100. passwordFocus: false,
  101. showPassword: false,
  102. loading: false,
  103. siteList: [],
  104. languageList: [],
  105. selectedSite: ''
  106. };
  107. },
  108. created() {
  109. this.getLanguageList();
  110. },
  111. methods: {
  112. // 获取多语言列表
  113. getLanguageList() {
  114. searchSysLanguage({languageCode: this.$i18n.locale}).then(({data}) => {
  115. this.languageList = data.rows
  116. })
  117. },
  118. switch_the_language(val) {
  119. localStorage.setItem('locale', val)
  120. this.$i18n.locale = val; // 修改当前语言
  121. location.reload()
  122. },
  123. handleUsernameBlur() {
  124. // 用户名输入完成后,获取该用户授权的工厂列表
  125. if (this.dataForm.userName && this.dataForm.userName.trim()) {
  126. this.getUserSiteList(this.dataForm.userName);
  127. } else {
  128. this.siteList = [];
  129. this.selectedSite = '';
  130. }
  131. this.usernameFocus = false;
  132. },
  133. // 根据用户名获取授权的工厂列表
  134. getUserSiteList(userName) {
  135. if (!userName || !userName.trim()) {
  136. this.siteList = [];
  137. this.selectedSite = '';
  138. return;
  139. }
  140. this.siteLoading = true;
  141. getUserAuthorizedSites({ userName: userName.trim() }).then(({data}) => {
  142. this.siteLoading = false;
  143. if (data && data.code === 0) {
  144. this.siteList = data.data || data.list || [];
  145. // 清空之前选择的工厂
  146. this.selectedSite = '';
  147. // 如果只有一个工厂,自动选中
  148. if (this.siteList.length === 1) {
  149. this.selectedSite = this.siteList[0].siteCode;
  150. }
  151. } else {
  152. this.siteList = [];
  153. this.selectedSite = '';
  154. this.$message.error(data.msg || '获取工厂列表失败');
  155. }
  156. }).catch(error => {
  157. this.siteLoading = false;
  158. this.siteList = [];
  159. this.selectedSite = '';
  160. console.error('获取工厂列表失败:', error);
  161. this.$message.error('获取工厂列表失败');
  162. })
  163. },
  164. // 登录后缓存用户子site编码集合,供后续页面按需使用
  165. cacheUserAuthorizedSubSiteCodes(userName, site) {
  166. if (!userName || !userName.trim() || !site) {
  167. this.$store.commit('user/updateSubSiteCodeList', [])
  168. localStorage.setItem('userSubSiteCodeList', JSON.stringify([]))
  169. return Promise.resolve([])
  170. }
  171. return getUserAuthorizedSubSiteCodes({ userName: userName.trim(), site: site }).then(({data}) => {
  172. const sourceList = (data && data.code === 0) ? (data.data || []) : []
  173. const subSiteCodeList = Array.from(new Set((sourceList || []).filter(item => !!item)))
  174. this.$store.commit('user/updateSubSiteCodeList', subSiteCodeList)
  175. localStorage.setItem('userSubSiteCodeList', JSON.stringify(subSiteCodeList))
  176. return subSiteCodeList
  177. }).catch(error => {
  178. console.error('缓存子site编码失败:', error)
  179. this.$store.commit('user/updateSubSiteCodeList', [])
  180. localStorage.setItem('userSubSiteCodeList', JSON.stringify([]))
  181. return []
  182. })
  183. },
  184. dataFormSubmit() {
  185. if (!this.selectedSite) {
  186. this.$message.error('请选择工厂');
  187. return;
  188. }
  189. this.loading = true;
  190. this.$http({
  191. url: this.$http.adornUrl('/sys/login'),
  192. method: 'post',
  193. data: this.$http.adornData({
  194. username: this.dataForm.userName,
  195. password: this.dataForm.password,
  196. uuid: this.dataForm.uuid,
  197. site: this.selectedSite
  198. })
  199. }).then(({ data }) => {
  200. this.loading = false;
  201. if (data && data.code === 0) {
  202. // 设置Cookie过期时间为8小时,与后端token同步 - rqrq
  203. this.$cookie.set('token', data.token, { expires: '8h' });
  204. localStorage.setItem('userName', this.dataForm.userName);
  205. localStorage.setItem('site', this.selectedSite);
  206. this.$store.commit('user/updateSite', this.selectedSite);
  207. this.cacheUserAuthorizedSubSiteCodes(this.dataForm.userName, this.selectedSite).finally(() => {
  208. this.$router.replace({ name: 'home' });
  209. })
  210. } else {
  211. this.$alert(data.msg, '错误', {
  212. confirmButtonText: '确定'
  213. });
  214. }
  215. });
  216. }
  217. }
  218. }
  219. </script>
  220. <style scoped>
  221. .pda-login {
  222. height: 100vh;
  223. display: flex;
  224. justify-content: center;
  225. align-items: center;
  226. background: #f5f5f5; /* 修改为与 index.vue 一致的背景色 */
  227. font-family: 'Segoe UI', sans-serif;
  228. }
  229. .login-card {
  230. position: relative;
  231. width: 90%;
  232. max-width: 400px;
  233. padding: 2rem;
  234. background: white;
  235. border-radius: 16px;
  236. box-shadow: 0 6px 24px rgba(0, 0, 0, 0.1);
  237. }
  238. .lang-switch {
  239. position: absolute;
  240. top: 18px;
  241. right: 20px;
  242. }
  243. .lang-text {
  244. cursor: pointer;
  245. font-size: 14px;
  246. color: #17B3A3;
  247. }
  248. .logo-area {
  249. text-align: center;
  250. margin-bottom: 1.5rem;
  251. }
  252. .logo-area svg {
  253. width: 64px;
  254. height: 64px;
  255. filter: drop-shadow(0 2px 4px rgba(23, 179, 163, 0.3)); /* 修改阴影颜色 */
  256. }
  257. h1 {
  258. color: #333;
  259. font-size: 1.5rem;
  260. margin-top: 1rem;
  261. }
  262. .input-group {
  263. position: relative;
  264. margin-bottom: 1.2rem;
  265. border-bottom: 1px solid #ccc;
  266. transition: all 0.3s ease;
  267. }
  268. .input-group.focus {
  269. border-color: #17B3A3;
  270. }
  271. .input-group input {
  272. width: 100%;
  273. padding: 10px 0;
  274. border: none;
  275. outline: none;
  276. font-size: 15px;
  277. }
  278. .input-group.site-select {
  279. border: none;
  280. padding-top: 8px;
  281. }
  282. .pwd-toggle {
  283. position: absolute;
  284. right: 0;
  285. top: 50%;
  286. transform: translateY(-50%);
  287. background: none;
  288. border: none;
  289. color: #17B3A3;
  290. font-size: 12px;
  291. cursor: pointer;
  292. }
  293. .login-btn {
  294. width: 100%;
  295. padding: 12px;
  296. background: #17B3A3;
  297. color: white;
  298. border: none;
  299. border-radius: 8px;
  300. font-size: 16px;
  301. margin-top: 1.2rem;
  302. transition: all 0.3s;
  303. }
  304. .loading-dots {
  305. letter-spacing: 2px;
  306. animation: blink 1.2s infinite;
  307. }
  308. @keyframes blink {
  309. 0%, 100% { opacity: 0.2 }
  310. 50% { opacity: 1 }
  311. }
  312. </style>