header.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <template>
  2. <header id="header">
  3. <div class="wrap">
  4. <div class="left-part">
  5. <img v-if="user.isQingQue" class="logo" src="http://gm.nkfzs.com/favicon.ico" alt="游戏中心"/>
  6. <img v-else class="logo" :src="getAssetsFile('default_logo.png')" alt="游戏中心"/>
  7. {{ user.projectName }}
  8. </div>
  9. <div class="menu">
  10. <el-menu
  11. :default-active="route.path"
  12. class="el-menu-wrapper"
  13. :ellipsis="false"
  14. router
  15. mode="horizontal"
  16. >
  17. <el-menu-item index="/home">首页</el-menu-item>
  18. <el-menu-item index="/cate">分类</el-menu-item>
  19. <el-menu-item index="/my_game">我的游戏</el-menu-item>
  20. </el-menu>
  21. </div>
  22. <div class="search-wrap">
  23. <el-autocomplete
  24. v-model="search.params.keywords"
  25. :fetch-suggestions="searchChange"
  26. placeholder="请输入游戏名称"
  27. :prefix-icon="Search"
  28. clearable
  29. @select="handleSelect"
  30. @keyup.enter="handleKeyEnter"
  31. maxlength="30"
  32. />
  33. </div>
  34. <div class="mob-search" v-if="route.path === '/home' && platform.mobile">
  35. <van-search shape='round' @click="router.push({ path: '/search' })" />
  36. </div>
  37. <div class="mini-menu" v-if="platform.mobile && route.path !== '/home' ">
  38. <img v-if="route.path !== '/search'" :src="getAssetsFile('search_circle.png')" alt="" @click="router.push({ path: '/search' })" />
  39. <i class="verticle" />
  40. <img :src="getAssetsFile('menu.png')" alt="" @click="show=true"/>
  41. </div>
  42. <div class="user">
  43. <span v-if="!user.isLogin" class="el-dropdown-link">
  44. <img :src="getAssetsFile('gamer.png')" @click="loginFormVisible = true" />
  45. </span>
  46. <el-dropdown v-else trigger="click" >
  47. <span class="el-dropdown-link">
  48. <img :src="getAssetsFile('gamer.png')" />
  49. </span>
  50. <template #dropdown>
  51. <el-dropdown-menu>
  52. <el-dropdown-item class="clearfix" @click="toSetting">
  53. 账号设置
  54. </el-dropdown-item>
  55. <el-dropdown-item class="clearfix" @click="logOut">
  56. 退出登录
  57. </el-dropdown-item>
  58. </el-dropdown-menu>
  59. </template>
  60. </el-dropdown>
  61. </div>
  62. </div>
  63. </header>
  64. <div class="header-hidden"/>
  65. <el-dialog
  66. v-model="loginFormVisible"
  67. :center="true"
  68. title="登录"
  69. width="30%"
  70. >
  71. <div class="login-box-header">
  72. <el-form
  73. ref="ruleFormRef"
  74. :rules="rules"
  75. class="login_from"
  76. label-position="left"
  77. :model="loginForm"
  78. style="max-width: 400px"
  79. @submit.prevent="submitForm(ruleFormRef)">
  80. <el-tabs v-model="login_typ" style="width: 50%;" @tab-change="resetForm(ruleFormRef)">
  81. <el-tab-pane label="密码登录" name="pwd" />
  82. <el-tab-pane label="验证码登录" name="sms" />
  83. </el-tabs>
  84. <template v-if="login_typ === 'pwd'">
  85. <el-form-item label="" prop="account">
  86. <el-input v-model="loginForm.account" type="text" placeholder="请输入账号" clearable :prefix-icon="User" />
  87. </el-form-item>
  88. <el-form-item label="" prop="password">
  89. <el-input
  90. v-model="loginForm.password"
  91. type="password"
  92. show-password
  93. placeholder="请输入密码"
  94. autocomplete="off"
  95. :prefix-icon="Lock" />
  96. <el-input
  97. v-model="loginForm.password"
  98. type="hidden"
  99. autocomplete="off"
  100. id="md5_password"
  101. show-password
  102. placeholder="请输入密码"
  103. :prefix-icon="Lock" />
  104. </el-form-item>
  105. </template>
  106. <template v-else-if="login_typ === 'sms'">
  107. <el-form-item label="" prop="account">
  108. <el-input v-model="loginForm.account" type="text" placeholder="请输入手机号" clearable :prefix-icon="Cellphone" />
  109. </el-form-item>
  110. <el-form-item label="" prop="smsCaptcha">
  111. <el-input v-model="loginForm.smsCaptcha" type="number" placeholder="请输入验证码" :prefix-icon="Lock">
  112. <template #append>
  113. <div>
  114. <el-button @click="getCaptcha" color="#ed8c0f" style="width: 100px;" :disabled ="!can_send">{{smsMessage}}</el-button>
  115. </div>
  116. </template>
  117. </el-input>
  118. </el-form-item>
  119. </template>
  120. <!-- <el-form-item style="margin-top:-10px;margin-bottom:-5px;">
  121. <el-checkbox v-model="checked" style="color:#a0a0a0;margin-top:-10px;">记住我</el-checkbox>
  122. </el-form-item> -->
  123. <div class="btn">
  124. <el-button
  125. round
  126. class="submit_btn"
  127. block
  128. type="primary"
  129. size="large"
  130. native-type="submit">登&emsp;录</el-button>
  131. </div>
  132. </el-form>
  133. </div>
  134. </el-dialog>
  135. <van-popup
  136. v-model:show="show"
  137. position="top"
  138. :style="{ width: '100%', height: 'auto' }"
  139. >
  140. <van-cell
  141. class="menu_title"
  142. v-for="(item, index) in user.menuPath"
  143. :key="index"
  144. :title="item.text"
  145. @click="toPath(item.url)"/>
  146. <van-cell v-if="user.isLogin" title="账号设置" @click="toSetting"/>
  147. <van-cell v-if="user.isLogin" title="退出登录" @click="logOut"/>
  148. </van-popup>
  149. </template>
  150. <script setup lang="ts">
  151. import { ref, reactive, onMounted, watch, computed } from 'vue'
  152. import { useRouter, useRoute } from 'vue-router'
  153. import { useStore } from '@/store'
  154. import Message from '@/utils/Message'
  155. import local from '@/utils/local'
  156. import { getAssetsFile } from '@/utils/imgResolve'
  157. import { Lock, User, Cellphone, Search } from '@element-plus/icons-vue'
  158. import type { FormInstance, FormRules } from 'element-plus'
  159. import { getUserLogin, captchaLogin, getCaptchaHttp, getGameList } from '@/api/index'
  160. // const img:any = inject('img')
  161. const router = useRouter()
  162. const route = useRoute()
  163. const { user } = useStore()
  164. const { search } = useStore()
  165. // showSuccessToast('成功文案')
  166. // showFailToast('失败文案')
  167. const show = ref(false)
  168. const game_name = ref(local.get('searchText') || '')
  169. // 绑定el-autocomplete组件
  170. const oriMenu = reactive([
  171. { text: '首页', value: 1, url: '/home' },
  172. { text: '分类', value: 2, url: '/cate' },
  173. { text: '我的游戏', value: 3, url: '/my_game' }
  174. ])
  175. const currentIndex = ref(local.get('headerPath') || '') // 取保存的title
  176. console.log('currentIndex', currentIndex.value)
  177. if (route.query.account) {
  178. currentIndex.value = '我的'
  179. console.log('1234567', currentIndex.value)
  180. }
  181. // 顶部导航栏
  182. onMounted(() => {
  183. const token = sessionStorage.getItem('token') || ''
  184. if (token) {
  185. user.isLogin = true
  186. user.getUserProfile()
  187. }
  188. })
  189. // 退出登录
  190. const logOut = () => {
  191. // console.log(22);
  192. show.value = false
  193. user.menuPath = oriMenu
  194. sessionStorage.removeItem('token')
  195. user.isLogin = false
  196. router.push({ path: '/login' })
  197. }
  198. const toSetting = () => {
  199. show.value = false
  200. router.push('/settings')
  201. }
  202. interface LinkItem {
  203. id: string
  204. value: string
  205. }
  206. const links: any = ref<LinkItem[]>([])
  207. const searchChange = async(str: string, cb: (arg: any) => void) => {
  208. currentIndex.value = ''
  209. local.remove('headerPath')
  210. if (str && str.length > 0) {
  211. game_name.value = str
  212. try {
  213. var params = reactive({
  214. type: 0,
  215. tag_id: 0,
  216. page: 1,
  217. pagesize: 10,
  218. keywords: str
  219. })
  220. var { data } = await getGameList(params)
  221. links.value = data.data.lists
  222. links.value = links.value.map((item:any, index:any) => {
  223. return {
  224. id: `${index}`,
  225. value: `${item.screen_name}`
  226. }
  227. })
  228. links.value = links.value.filter((item:any) => {
  229. return item.value.indexOf(str) > -1
  230. })
  231. cb(links.value)
  232. } catch (error: any) {
  233. // links.value.push({
  234. // id: '-1',
  235. // value: '暂无匹配数据'
  236. // })
  237. // cb(links.value)
  238. console.log(error)
  239. Message.error(error.data.msg)
  240. }
  241. } else {
  242. cb([])
  243. }
  244. }
  245. const handleSelect = (item: LinkItem) => {
  246. if (item) {
  247. if (route.path !== '/search') {
  248. router.push({ path: '/search' })
  249. }
  250. search.onSearch()
  251. local.set('searchText', game_name.value)
  252. }
  253. }
  254. // 手动回车跳转搜索页
  255. const handleKeyEnter = () => {
  256. if (route.path !== '/search') {
  257. router.push({ path: '/search' })
  258. }
  259. search.onSearch()
  260. local.set('searchText', game_name.value)
  261. }
  262. watch(currentIndex, () => { }, { deep: true, immediate: true })
  263. const platform = computed(() => {
  264. var u = navigator.userAgent
  265. return {
  266. mobile: !!u.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端
  267. ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // ios终端
  268. android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, // android终端
  269. iPhone: u.indexOf('iPhone') > -1, // 是否为iPhone或者QQHD浏览器
  270. iPad: u.indexOf('iPad') > -1, // 是否iPad
  271. webApp: u.indexOf('Safari') === -1, // 是否web应该程序,没有头部与底部
  272. weixin: u.indexOf('MicroMessenger') > -1 // 是否微信
  273. }
  274. })
  275. const toPath = (url: any) => {
  276. show.value = false
  277. router.push({ path: url })
  278. }
  279. declare let md5: any
  280. const loginFormVisible = ref<boolean>(false)
  281. const rules = reactive<FormRules>({
  282. account: [
  283. { required: true, message: '请输入账号', trigger: 'blur' },
  284. { min: 3, max: 255, message: '请正确输入账号', trigger: 'blur' }
  285. ],
  286. password: [
  287. { required: true, message: '请输入密码', trigger: 'blur' },
  288. { min: 6, max: 20, message: '请输入6-20位密码', trigger: 'blur,change' }
  289. ],
  290. smsCaptcha: [
  291. { required: true, message: '请输入验证码', trigger: 'blur' }
  292. ]
  293. })
  294. interface login {
  295. account: string,
  296. password: string,
  297. timestamp: number | string,
  298. smsCaptcha:string | undefined
  299. }
  300. const loginForm = reactive<login>({
  301. account: '',
  302. password: '',
  303. timestamp: parseInt(`${Date.now() / 1000}`),
  304. smsCaptcha: undefined
  305. })
  306. const ruleFormRef = ref<FormInstance>()
  307. const login_typ = ref<'pwd'| 'sms'>('sms')
  308. const resetForm = (formEl: FormInstance | undefined) => {
  309. if (!formEl) return
  310. formEl.resetFields()
  311. }
  312. const can_send = ref<boolean>(true)
  313. const smsMessage = ref<'获取验证码' | number>('获取验证码')
  314. const VALID = 60
  315. const settimes = () => {
  316. var interval:any
  317. const setTimeFn = () => {
  318. (smsMessage.value as number)--
  319. if (smsMessage.value as number < 0 || can_send.value === true) {
  320. clearInterval(interval)
  321. can_send.value = true
  322. smsMessage.value = '获取验证码'
  323. }
  324. }
  325. interval = setInterval(function() {
  326. setTimeFn()
  327. }, 1000)
  328. }
  329. const getCaptcha = async() => {
  330. if (loginForm.account.length !== 11) {
  331. return Message.error('请输入有效手机号!')
  332. }
  333. await getCaptchaHttp({ mobile: loginForm.account }).then((res) => {
  334. console.log(res)
  335. can_send.value = false
  336. smsMessage.value = VALID
  337. settimes()
  338. sessionStorage.setItem('LOGIN_CAPTCHA', Math.round(new Date().getTime() / 1000).toString())
  339. Message.success('验证码已发送,请注意查收')
  340. }).catch((error) => {
  341. console.log(error)
  342. Message.error(error.data.msg)
  343. })
  344. }
  345. const submitForm = async(formEl: FormInstance | undefined) => {
  346. if (!formEl) return
  347. await formEl.validate(async(valid, fields) => {
  348. if (valid) {
  349. if (login_typ.value === 'pwd') {
  350. // 登录密码加密
  351. let md5Pwd: any = document.querySelector('#md5_password')
  352. // eslint-disable-next-line no-undef
  353. md5Pwd = md5(md5(md5(loginForm.password)) + loginForm.timestamp)
  354. const params = {
  355. account: loginForm.account,
  356. password: md5Pwd,
  357. timestamp: loginForm.timestamp
  358. }
  359. getUserLogin(params).then(async(res) => {
  360. // console.log('res---->', res);
  361. if (res.data.code === 200) {
  362. Message.success('登录成功')
  363. // 保存用户信息 和 token
  364. sessionStorage.setItem('token', res.data.data.token)
  365. sessionStorage.setItem('account', loginForm.account)
  366. localStorage.setItem('from', '1')
  367. user.isLogin = true
  368. await user.getUserProfile()
  369. // 跳转我的游戏页面
  370. setTimeout(() => {
  371. router.push({ path: '/my_game', query: { account: loginForm.account }})
  372. local.set('headerPath', '我的')
  373. }, 1000)
  374. }
  375. }).catch(err => {
  376. // console.log('err===>', err);
  377. Message.error(err.data.msg)
  378. })
  379. } else if (login_typ.value === 'sms') {
  380. const params = {
  381. mobile: loginForm.account,
  382. captcha: loginForm.smsCaptcha
  383. }
  384. console.log(params)
  385. await captchaLogin(params).then(async(res) => {
  386. console.log(res)
  387. if (res.data.code === 200) {
  388. Message.success('登录成功')
  389. sessionStorage.setItem('token', res.data.data.token)
  390. sessionStorage.setItem('account', loginForm.account)
  391. localStorage.setItem('from', '2')
  392. user.isLogin = true
  393. await user.getUserProfile()
  394. setTimeout(() => {
  395. router.push({ path: '/my_game', query: { account: loginForm.account }})
  396. local.set('headerPath', '我的')
  397. }, 1000)
  398. }
  399. }).catch((error) => {
  400. console.log(error)
  401. Message.error(error.data.msg)
  402. })
  403. }
  404. } else {
  405. console.log('登录失败', fields)
  406. }
  407. })
  408. }
  409. </script>
  410. <style lang="scss" scoped>
  411. #header{
  412. width: 100%;
  413. max-width: 100vw;
  414. background-color: #fff;
  415. position: fixed;
  416. z-index: 1000;
  417. .wrap{
  418. margin: 0 auto;
  419. height: 100%;
  420. display: flex;
  421. justify-content: space-between;
  422. align-items: center;
  423. box-sizing: border-box;
  424. .left-part{
  425. display: flex;
  426. align-items: center;
  427. font-family: 'zihunchaoji';
  428. white-space: nowrap;
  429. }
  430. .menu{
  431. padding: 0 5rem 0 1rem;
  432. .el-menu--horizontal.el-menu{
  433. border: 0;
  434. >.el-menu-item.is-active{
  435. border: 0;
  436. }
  437. }
  438. .el-menu-item{
  439. border: 0;
  440. }
  441. }
  442. .search-wrap{
  443. width: 8.5rem;
  444. :deep(.el-input__wrapper){
  445. border-radius: 1rem;
  446. }
  447. }
  448. .user{
  449. cursor: pointer;
  450. .el-dropdown-link{
  451. overflow: hidden;
  452. width: 2rem;
  453. height: 2rem;
  454. border-radius: 50%;
  455. >img{
  456. width: 2rem;
  457. height: 2rem;
  458. display: block;
  459. }
  460. }
  461. }
  462. .mini-menu{
  463. display: flex;
  464. align-items: center;
  465. >img{
  466. width: 25px;
  467. height: 25px;
  468. }
  469. .verticle{
  470. width: 1px;
  471. height: 30px;
  472. background-color: #ddd;
  473. margin: 0 10px;
  474. }
  475. }
  476. }
  477. }
  478. .header-hidden{
  479. height: 3rem;
  480. }
  481. .login-box-header{
  482. padding: 1rem;
  483. .submit_btn{
  484. width: 100%;
  485. display: block;
  486. }
  487. }
  488. </style>