A8000
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.

247 lines
5.7 KiB

4 months ago
  1. <script setup lang="ts">
  2. import { ref, onMounted, onUnmounted } from 'vue'
  3. import { getUserList } from '@/services/Index/user-manage'
  4. import type { User } from '@/types/Index'
  5. import { createWebSocket, DeviceContextStateMessage } from '@/websocket/socket.ts'
  6. import { getServerInfo } from '@/utils/getServerInfo.ts'
  7. import router from '@/router/router.ts'
  8. import { isBoardParamInited, login } from '@/services'
  9. import circleUrl from '@/assets/avatar.png'
  10. const stateUrl = getServerInfo('/api/v1/app/ws/state')
  11. const wsState = createWebSocket(stateUrl.wsUrl)
  12. const handleDeviceContextState = (data: DeviceContextStateMessage['data']) => {
  13. if (data.loginFlag) {
  14. sessionStorage.setItem('token', JSON.stringify(data.loginUser))
  15. router.push('/index')
  16. }
  17. }
  18. //用户列表
  19. const userList = ref<User[]>([])
  20. //获取用户列表
  21. const getUserListData = async () => {
  22. const res = await getUserList()
  23. userList.value = res.data
  24. }
  25. onMounted(() => {
  26. wsState.subscribe<DeviceContextStateMessage>(
  27. 'DeviceContext',
  28. handleDeviceContextState,
  29. )
  30. wsState.connect()
  31. getUserListData()
  32. })
  33. onUnmounted(() => {
  34. wsState.unsubscribe<DeviceContextStateMessage>(
  35. 'DeviceContext',
  36. handleDeviceContextState,
  37. )
  38. })
  39. const activeUser = ref<User>()
  40. const password = ref('')
  41. const inputPin = (pin: string) => {
  42. if (password.value.length >= 4) {
  43. return
  44. }
  45. if (password.value.length < 4) {
  46. password.value += pin
  47. }
  48. if (password.value.length === 4) {
  49. // 输入完成,执行登录操作
  50. submitPin()
  51. }
  52. }
  53. const errorMsg = ref('')
  54. const submitPin = async () => {
  55. if (!activeUser.value?.id) {
  56. errorMsg.value = '请选择用户'
  57. return
  58. }
  59. if (password.value.length !== 4) {
  60. errorMsg.value = '请输入密码'
  61. return
  62. }
  63. // let resData = await isBoardParamInited()
  64. // if (!resData.data) {
  65. // //设备正在初始化
  66. // errorMsg.value = '设备正在初始化,请稍候重试'
  67. // return
  68. // }
  69. const params = {
  70. id: activeUser.value?.id,
  71. password: password.value,
  72. }
  73. const res = await login(params)
  74. if (res.success) {
  75. sessionStorage.setItem('token', JSON.stringify(res.data))
  76. await router.push('/index')
  77. } else {
  78. errorMsg.value = res.info
  79. // password.value = ''
  80. }
  81. }
  82. </script>
  83. <template>
  84. <div class="login-box">
  85. <p class="login-title">登录</p>
  86. <div class="user-box">
  87. <p class="title">选择用户</p>
  88. <div class="user-list">
  89. <div class="user-info" v-for="user in userList" :key="user.id" @click="activeUser = user" :class="{ 'user-info-active': activeUser?.id === user.id }">
  90. <el-avatar :size="60" :src="circleUrl" />
  91. <span class="name">{{user.account}}</span>
  92. <el-icon v-show="activeUser?.id === user.id" color="#fff"><Select /></el-icon>
  93. </div>
  94. </div>
  95. </div>
  96. <div class="password-box">
  97. <p class="title">请输入4位PIN码</p>
  98. <div class="password-list">
  99. <div v-for="i in 4" :key="i" class="password-item" :class="{'password-item-fill': i <= password.length}"></div>
  100. </div>
  101. <div class="pin-keypad">
  102. <div
  103. v-for="n in 9"
  104. :key="n"
  105. class="key"
  106. @click="inputPin(n.toString())"
  107. >
  108. {{ n }}
  109. </div>
  110. <div class="key" @click="password = '';errorMsg = ''">重输</div>
  111. <div class="key" @click="inputPin('0')">0</div>
  112. <div class="key" @click="submitPin">确定</div>
  113. </div>
  114. <div class="error-box">{{errorMsg}}</div>
  115. </div>
  116. </div>
  117. </template>
  118. <style lang="less" scoped>
  119. @keyframes iconAnim {
  120. 0% { transform: scale(0.8); opacity: 0; }
  121. 100% { transform: scale(1); opacity: 1; }
  122. }
  123. .login-title {
  124. position: absolute;
  125. top: 10%;
  126. font-size: 50px;
  127. font-weight: bold;
  128. }
  129. .login-box {
  130. width: 100%;
  131. height: 100%;
  132. background: #A0CEF2;
  133. display: flex;
  134. align-items: center;
  135. justify-content: center;
  136. position: relative;
  137. .user-box {
  138. width: 35%;
  139. height: 40%;
  140. margin-right: 20px;
  141. background: #fff;
  142. border-radius: 30px;
  143. padding: 60px 30px;
  144. display: flex;
  145. flex-direction: column;
  146. .user-list {
  147. width: 100%;
  148. flex: 1;
  149. overflow: auto;
  150. .user-info {
  151. display: flex;
  152. align-items: center;
  153. padding: 20px;
  154. border-radius: 20px;
  155. .name {
  156. margin-left: 30px;
  157. font-size: 25px;
  158. font-weight: bold;
  159. }
  160. .el-icon {
  161. animation: iconAnim 0.3s ease forwards;
  162. font-size: 30px;
  163. margin-left: auto;
  164. }
  165. }
  166. .user-info-active {
  167. background: #3E8ED1;
  168. color: #fff;
  169. }
  170. }
  171. }
  172. .password-box {
  173. margin-left: 20px;
  174. width: 35%;
  175. height: 40%;
  176. background: #fff;
  177. border-radius: 30px;
  178. padding: 60px 30px;
  179. display: flex;
  180. flex-direction: column;
  181. }
  182. }
  183. .title {
  184. font-size: 30px;
  185. font-weight: bold;
  186. }
  187. .password-list {
  188. display: flex;
  189. justify-content: center;
  190. padding: 20px 0;
  191. .password-item {
  192. width: 30px;
  193. height: 30px;
  194. border-radius: 50%;
  195. background: #CDCFD4;
  196. margin: 0 10px;
  197. }
  198. .password-item-fill {
  199. background: #3E8ED1;
  200. }
  201. }
  202. .pin-keypad {
  203. display: grid;
  204. grid-template-columns: repeat(3, 1fr);
  205. gap: 10px;
  206. margin-top: 30px;
  207. .key {
  208. width: 100px;
  209. height: 80px;
  210. background-color: #fff;
  211. border-radius: 15px;
  212. display: flex;
  213. align-items: center;
  214. justify-content: center;
  215. font-size: 32px;
  216. color: #333;
  217. cursor: pointer;
  218. transition: all 0.3s;
  219. border: 1px solid #E7EDF1;
  220. box-shadow: 0 3px 8px #E7EDF1;
  221. &:active {
  222. transform: scale(0.95);
  223. }
  224. }
  225. }
  226. .error-box {
  227. flex: 1;
  228. display: flex;
  229. justify-content: center;
  230. align-items: center;
  231. font-size: 20px;
  232. color: #ff0000;
  233. }
  234. </style>