Browse Source

feat: 实现微信登录demo

Burt 2 years ago
parent
commit
26068635dc

+ 2 - 0
manifest.config.ts

@@ -3,9 +3,11 @@ import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
 import path from 'node:path'
 import { loadEnv } from 'vite'
 
+// 获取环境变量的范例
 const env = loadEnv(process.env.NODE_ENV!, path.resolve(process.cwd(), 'env'))
 // console.log(env)
 const { VITE_APP_TITLE, VITE_UNI_APPID, VITE_WX_APPID } = env
+
 export default defineManifestConfig({
   name: VITE_APP_TITLE,
   appid: VITE_UNI_APPID,

+ 37 - 20
pages.config.ts

@@ -1,4 +1,5 @@
 import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
+import { isWxProd } from './vite.config'
 
 export default defineUniPages({
   globalStyle: {
@@ -24,26 +25,42 @@ export default defineUniPages({
     fontSize: '10px',
     iconWidth: '24px',
     spacing: '3px',
-    list: [
-      {
-        iconPath: 'static/tabbar/home.png',
-        selectedIconPath: 'static/tabbar/homeHL.png',
-        pagePath: 'pages/index/index',
-        text: '首页',
-      },
-      {
-        iconPath: 'static/tabbar/example.png',
-        selectedIconPath: 'static/tabbar/exampleHL.png',
-        pagePath: 'pages/demo/index',
-        text: '示例',
-      },
-      {
-        iconPath: 'static/tabbar/personal.png',
-        selectedIconPath: 'static/tabbar/personalHL.png',
-        pagePath: 'pages/my/index',
-        text: '我的',
-      },
-    ],
+    list: isWxProd
+      ? [
+          {
+            iconPath: 'static/tabbar/home.png',
+            selectedIconPath: 'static/tabbar/homeHL.png',
+            pagePath: 'pages/index/index',
+            text: '首页',
+          },
+          {
+            iconPath: 'static/tabbar/personal.png',
+            selectedIconPath: 'static/tabbar/personalHL.png',
+            pagePath: 'pages/my/index',
+            text: '我的',
+          },
+        ]
+      : [
+          {
+            iconPath: 'static/tabbar/home.png',
+            selectedIconPath: 'static/tabbar/homeHL.png',
+            pagePath: 'pages/index/index',
+            text: '首页',
+          },
+          // 生产环境要注释掉demo,所以分开来写
+          {
+            iconPath: 'static/tabbar/example.png',
+            selectedIconPath: 'static/tabbar/exampleHL.png',
+            pagePath: 'pages/demo/index',
+            text: '示例',
+          },
+          {
+            iconPath: 'static/tabbar/personal.png',
+            selectedIconPath: 'static/tabbar/personalHL.png',
+            pagePath: 'pages/my/index',
+            text: '我的',
+          },
+        ],
   },
   // 你也可以定义 pages 字段,它具有最高的优先级。
   pages: [],

+ 7 - 0
src/components/fly-login/README.md

@@ -0,0 +1,7 @@
+# fly-login
+
+点击“点击显示微信头像”按钮后,出现的半屏登录弹窗,可以在任意页面引入。
+
+仿“掘金小册”小程序。
+
+![掘金小册登录](screenshot.png)

BIN
src/components/fly-login/defaultAvatar.png


+ 116 - 0
src/components/fly-login/fly-login.vue

@@ -0,0 +1,116 @@
+<template>
+  <view class="fly-login" v-if="show">
+    <view class="fly-login-mask" />
+    <view class="fly-login-content px-4">
+      <view class="font-bold h-16 leading-16">获取您的昵称、头像</view>
+      <view
+        class="rounded-full bg-light-600 w-6 h-6 text-center absolute top-4 right-4"
+        @click="onClose"
+      >
+        <view class="i-carbon-close text-gray-700" />
+      </view>
+
+      <view
+        class="flex items-center h-16 leading-16 border-b-gray-400 border-b-solid border-[1rpx]"
+      >
+        <text class="mr-4 flex-shrink-0">头像</text>
+        <button
+          class="bg-transparent flex items-center after:b-none w-full h-12 leading-12"
+          open-type="chooseAvatar"
+          @chooseavatar="onChooseAvatar"
+        >
+          <image class="w-8 h-8 rounded-full" :src="avatarUrl"></image>
+          <text class="ml-auto i-carbon-chevron-right"></text>
+        </button>
+      </view>
+
+      <view
+        class="flex items-center h-16 leading-16 border-b-gray-400 border-b-solid border-1 mt-4"
+      >
+        <text class="mr-4 flex-shrink-0">昵称</text>
+        <input type="nickname" placeholder="请输入昵称" @change="onChange" />
+      </view>
+
+      <button
+        size="default"
+        type="default"
+        style="color: #fff; background-color: #1aad19; border-color: #1aad19"
+        class="text-center leading-12 w-40 my-4"
+        @click="onSubmit"
+      >
+        确定
+      </button>
+    </view>
+  </view>
+</template>
+<script lang="ts" setup>
+import { useUserStore } from '@/store'
+import defaultAvatarUrl from './defaultAvatar.png'
+
+const userStore = useUserStore()
+
+const show = ref(true)
+const avatarUrl = ref(defaultAvatarUrl)
+const nickname = ref('')
+
+const onClose = () => {
+  show.value = false
+}
+
+const onChooseAvatar = (e) => {
+  const { avatarUrl: url } = e.detail
+  avatarUrl.value = url
+  // 这里就要上传,加快速度,提升体验(用户多次选择头像就多次上传吧,总有取舍)
+  console.log(url)
+}
+
+const onChange = (e) => {
+  const { value } = e.detail
+  nickname.value = value
+}
+
+const onSubmit = () => {
+  // 1、上传刚刚的图片,并返回网络地址
+  // 2、把用户信息存起来
+  if (avatarUrl.value === defaultAvatarUrl) {
+    uni.showToast({
+      title: '请选择头像',
+      icon: 'none',
+    })
+    return
+  }
+  if (!nickname.value) {
+    uni.showToast({
+      title: '请填写昵称',
+      icon: 'none',
+    })
+    return
+  }
+
+  console.log('保存用户信息')
+  userStore.setUserInfo({ nickname: nickname.value, avatar: avatarUrl.value })
+}
+</script>
+
+<style lang="scss" scoped>
+.fly-login {
+  position: fixed;
+  inset: 0;
+
+  .fly-login-mask {
+    position: fixed;
+    inset: 0;
+    background-color: rgb(0 0 0 / 30%);
+  }
+
+  .fly-login-content {
+    position: fixed;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    background-color: #fff;
+    border-top-left-radius: 16px;
+    border-top-right-radius: 16px;
+  }
+}
+</style>

BIN
src/components/fly-login/screenshot.png


+ 1 - 1
src/pages.json

@@ -56,7 +56,7 @@
       "type": "page"
     },
     {
-      "path": "pages/login/login",
+      "path": "pages/login/index",
       "type": "page",
       "style": {
         "navigationBarTitleText": "登录"

+ 40 - 0
src/pages/login/index.vue

@@ -0,0 +1,40 @@
+<route lang="json5">
+{
+  style: { navigationBarTitleText: '登录' },
+}
+</route>
+
+<template>
+  <view class="p-4">
+    <view class="flex items-center leading-6" v-if="hasLogin">
+      <image class="w-8 h-8 rounded-full" :src="userStore.userInfo?.avatar"></image>
+      <view class="ml-2">{{ userStore.userInfo?.nickname }}</view>
+    </view>
+    <view class="flex items-center leading-6" v-else @click="show = true">
+      <fly-login v-if="show" />
+      <view class="i-carbon-user-avatar"></view>
+      <view class="ml-2">点击显示微信头像</view>
+    </view>
+    <fly-content :line="10" />
+    <button v-if="hasLogin" class="mt-2" @click="logout">退出登录</button>
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { useUserStore } from '@/store'
+
+const show = ref(false)
+const userStore = useUserStore()
+const hasLogin = computed(() => userStore.userInfo?.nickname)
+const logout = () => {
+  uni.showModal({
+    title: '确认退出当前账号?',
+    success: (res) => {
+      if (res.confirm) {
+        show.value = false
+        userStore.clearUserInfo()
+      }
+    },
+  })
+}
+</script>

+ 0 - 9
src/pages/login/login.vue

@@ -1,9 +0,0 @@
-<route lang="json5">
-{
-  style: { navigationBarTitleText: '登录' },
-}
-</route>
-
-<template>
-  <view>登录</view>
-</template>

+ 8 - 0
src/pages/my/index.vue

@@ -5,4 +5,12 @@
 </route>
 <template>
   <view>我的</view>
+  <view @click="goLoginPage">去登录</view>
 </template>
+
+<script lang="ts" setup>
+const goLoginPage = () => {
+  uni.navigateTo({ url: '/pages/login/index' })
+}
+// 用户登录,获取openId
+</script>

+ 2 - 2
src/typings.d.ts

@@ -1,6 +1,6 @@
 export type UserInfo = {
-  username: string
-  token: string
+  nickname: string
+  avatar: string
 }
 export type UserItem = {
   username: string

+ 1 - 1
uni-pages.d.ts

@@ -6,7 +6,7 @@
 interface NavigateToOptions {
   url: "pages/index/index" |
        "pages/demo/index" |
-       "pages/login/login" |
+       "pages/login/index" |
        "pages/my/index" |
        "pages/demo/demo/clock" |
        "pages/demo/demo/component-auto-import" |

+ 10 - 2
vite.config.ts

@@ -24,9 +24,13 @@ import AutoImport from 'unplugin-auto-import/vite'
 import viteCompression from 'vite-plugin-compression'
 import ViteRestart from 'vite-plugin-restart'
 import { visualizer } from 'rollup-plugin-visualizer'
-// TODO: 很多用户无法安装这个插件所以先注释掉了,如果你可以安装成功,那就可以放开这个注释,以及下面的配置
+// TODO: 很多用户无法安装这个插件所以先注释掉了,如果你可以安装成功,那就可以放开这个注释,以及下面的viteImagemin配置
+// 另外,小程序有主包2M的限制,所以一般图片会放到图片服务器(不放本地),那这个插件就没用,所以在开发h5的时候,使用本地图片才用得到,既然如此那就不装吧
 // import viteImagemin from 'vite-plugin-imagemin'
 
+export const isWxProd =
+  process.env.UNI_PLATFORM === 'mp-weixin' && process.env.NODE_ENV === 'production'
+
 // https://vitejs.dev/config/
 export default ({ command, mode }) => {
   console.log(mode === process.env.NODE_ENV)
@@ -43,11 +47,15 @@ export default ({ command, mode }) => {
   const env = loadEnv(mode, path.resolve(process.cwd(), 'env'))
   // console.log(env)
   console.log(process.env.UNI_PLATFORM) // 得到 mp-weixin, h5 等
+
   return defineConfig({
     envDir: './env', // 自定义env目录
     plugins: [
       UniPages({
-        exclude: ['**/components/**/**.*'],
+        // TODO: 生产环境小程序要过滤掉demo(demo里面很多图片,超过2M的包大小)
+        exclude: isWxProd
+          ? ['**/components/**/**.*', '**/demo/**/**.*']
+          : ['**/components/**/**.*'],
       }),
       UniLayouts(),
       UniPlatform(),