Преглед на файлове

feat(qr): 添加二维码生成功能

- 引入 weapp-qrcode 库用于生成二维码
- 新增二维码页面,展示用户身份码
- 在患者首页添加跳转至二维码页面的功能
- 集成用户信息读取与二维码数据生成逻辑
- 添加二维码样式设计与用户界面优化
- 增加类型声明文件以支持 weapp-qrcode 模块
- 更新 package.json 和 package-lock.json 依赖配置
mcbaiyun преди 1 месец
родител
ревизия
22e4bfe064
променени са 6 файла, в които са добавени 253 реда и са изтрити 3 реда
  1. 21 1
      package-lock.json
  2. 2 1
      package.json
  3. 6 0
      src/pages.json
  4. 1 1
      src/pages/patient/index/index.vue
  5. 207 0
      src/pages/public/profile/qr/index.vue
  6. 16 0
      src/shims-uni.d.ts

+ 21 - 1
package-lock.json

@@ -25,8 +25,10 @@
         "@dcloudio/uni-mp-xhs": "3.0.0-4070620250821001",
         "@dcloudio/uni-quickapp-webview": "3.0.0-4070620250821001",
         "@dcloudio/uni-ui": "^1.5.11",
+        "@qiun/ucharts": "^2.5.0-20230101",
         "vue": "3.4.21",
-        "vue-i18n": "9.14.5"
+        "vue-i18n": "9.14.5",
+        "weapp-qrcode": "^1.0.0"
       },
       "devDependencies": {
         "@dcloudio/types": "3.4.21",
@@ -3765,6 +3767,11 @@
         "url": "https://opencollective.com/parcel"
       }
     },
+    "node_modules/@qiun/ucharts": {
+      "version": "2.5.0-20230101",
+      "resolved": "https://registry.npmmirror.com/@qiun/ucharts/-/ucharts-2.5.0-20230101.tgz",
+      "integrity": "sha512-C7ccBgfPuGF6dxTRuMW0NPPMSCf1k/kh3I9zkRVBc5PaivudX/rPL+jd2Wty6gn5ya5L3Ob+YmYe09V5xw66Cw=="
+    },
     "node_modules/@rollup/pluginutils": {
       "version": "5.3.0",
       "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
@@ -6088,6 +6095,11 @@
       "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==",
       "license": "MIT"
     },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+    },
     "node_modules/fast-glob": {
       "version": "3.3.3",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -10706,6 +10718,14 @@
         "makeerror": "1.0.12"
       }
     },
+    "node_modules/weapp-qrcode": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/weapp-qrcode/-/weapp-qrcode-1.0.0.tgz",
+      "integrity": "sha512-4sa3W0rGDVJ9QaeZpAKlAuUxVyjhDwiUqHyGK/jJMsRMXnhb4yO8qWU/pZruMo+iT5J6CraS67lDMFb1VY+RaA==",
+      "dependencies": {
+        "extend": "^3.0.2"
+      }
+    },
     "node_modules/webidl-conversions": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",

+ 2 - 1
package.json

@@ -54,7 +54,8 @@
     "@dcloudio/uni-ui": "^1.5.11",
     "@qiun/ucharts": "^2.5.0-20230101",
     "vue": "3.4.21",
-    "vue-i18n": "9.14.5"
+    "vue-i18n": "9.14.5",
+    "weapp-qrcode": "^1.0.0"
   },
   "devDependencies": {
     "@dcloudio/types": "3.4.21",

+ 6 - 0
src/pages.json

@@ -137,6 +137,12 @@
 			"style": {
 				"navigationBarTitleText": "家属基本信息"
 			}
+		},
+		{
+			"path": "pages/public/profile/qr/index",
+			"style": {
+				"navigationBarTitleText": "二维码"
+			}
 		}
 	],
 	"globalStyle": {

+ 1 - 1
src/pages/patient/index/index.vue

@@ -220,7 +220,7 @@ function onItemClick(type: string) {
 }
 
 function onQrClick() {
-  uni.showToast({ title: '二维码功能开发中', icon: 'none' })
+  uni.navigateTo({ url: '/pages/public/profile/qr/index' })
 }</script>
 
 <style>

+ 207 - 0
src/pages/public/profile/qr/index.vue

@@ -0,0 +1,207 @@
+<template>
+  <CustomNav title="二维码" leftType="back" :opacity="0" />
+  <view class="page-container">
+    <view class="content">
+      <view class="qr-card">
+        <view class="user-info">
+          <view class="title-row">
+            <text class="card-title">个人身份码</text>
+            <view class="role-badge">{{ getRoleText(user.role) }}</view>
+          </view>
+          <text class="user-name">{{ user.nickname || '未知用户' }}</text>
+          <text class="qr-desc">扫码查看我的健康档案</text>
+        </view>
+        <view class="qr-container">
+          <canvas
+            canvas-id="qrcode"
+            class="qr-canvas"
+            v-if="qrData"
+          ></canvas>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+import { onShow } from '@dcloudio/uni-app'
+import CustomNav from '@/components/custom-nav.vue'
+// @ts-ignore
+import drawQrcode from 'weapp-qrcode'
+
+const user = ref<{ nickname?: string; role?: string | number; openid?: string; wx_openid?: string }>({})
+const qrData = ref('')
+
+const getRoleText = (role: string | number | undefined) => {
+  const roleMap: { [key: number]: string } = {
+    2: '医生',
+    3: '患者',
+    4: '患者家属'
+  }
+  return roleMap[Number(role)] || '未知身份'
+}
+
+const loadUser = () => {
+  try {
+    const u = uni.getStorageSync('user_info')
+    console.log('Loaded user_info from storage:', u)
+    if (u) {
+      user.value = u
+      console.log('User data set:', user.value)
+    }
+  } catch (e) {
+    console.error('Error loading user info:', e)
+  }
+}
+
+const generateQRCode = () => {
+  console.log('Generating QR code, user data:', user.value)
+  if (!user.value.wx_openid || !user.value.role) {
+    console.log('Missing wx_openid or role:', { wx_openid: user.value.wx_openid, role: user.value.role })
+    uni.showToast({ title: '用户信息不完整', icon: 'none' })
+    return
+  }
+  const data = JSON.stringify({
+    openid: user.value.wx_openid,
+    role: user.value.role
+  })
+  console.log('QR data:', data)
+  qrData.value = data
+  // 使用 weapp-qrcode 生成二维码
+  drawQrcode({
+    width: 200,
+    height: 200,
+    canvasId: 'qrcode',
+    text: data
+  })
+}
+
+onShow(() => {
+  loadUser()
+  generateQRCode()
+})
+</script>
+
+<style>
+.page-container {
+  min-height: 100vh;
+  padding-top: calc(var(--status-bar-height) + 44px);
+  box-sizing: border-box;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+}
+
+.content {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.qr-card {
+  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+  border-radius: 24rpx;
+  padding: 50rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  max-width: 550rpx;
+  width: 85%;
+  position: relative;
+  overflow: hidden;
+}
+
+.qr-card::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  height: 8rpx;
+  background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
+  border-radius: 24rpx 24rpx 0 0;
+}
+
+.user-info {
+  text-align: center;
+  margin-bottom: 50rpx;
+  position: relative;
+}
+
+.title-row {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin-bottom: 20rpx;
+}
+
+.card-title {
+  flex: 1;
+  text-align: center;
+  font-size: 36rpx;
+  font-weight: bold;
+  color: #2c3e50;
+  text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1);
+}
+
+.role-badge {
+  margin-left: auto;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  color: white;
+  padding: 6rpx 12rpx;
+  border-radius: 16rpx;
+  font-size: 20rpx;
+  font-weight: 500;
+  box-shadow: 0 2rpx 8rpx rgba(102, 126, 234, 0.3);
+}
+
+.user-info::after {
+  content: '';
+  position: absolute;
+  bottom: -20rpx;
+  left: 50%;
+  transform: translateX(-50%);
+  width: 80rpx;
+  height: 4rpx;
+  background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
+  border-radius: 2rpx;
+}
+
+.user-name {
+  font-size: 48rpx;
+  font-weight: bold;
+  color: #2c3e50;
+  display: block;
+  margin-bottom: 12rpx;
+  text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1);
+}
+
+.qr-desc {
+  font-size: 28rpx;
+  color: #95a5a6;
+  font-weight: 400;
+  display: block;
+  margin-top: 10rpx;
+}
+
+.qr-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 30rpx;
+  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+  border-radius: 16rpx;
+  border: 2rpx solid #dee2e6;
+  box-shadow: inset 0 2rpx 4rpx rgba(0, 0, 0, 0.05);
+}
+
+.qr-canvas {
+  width: 400rpx;
+  height: 400rpx;
+  border-radius: 8rpx;
+}
+</style>

+ 16 - 0
src/shims-uni.d.ts

@@ -8,3 +8,19 @@ declare module '@vue/runtime-core' {
 
   }
 }
+
+declare module 'weapp-qrcode' {
+  interface DrawQrcodeOptions {
+    width: number;
+    height: number;
+    canvasId: string;
+    text: string;
+    background?: string;
+    foreground?: string;
+    pdground?: string;
+    correctLevel?: number;
+  }
+  export default function drawQrcode(options: DrawQrcodeOptions): void;
+}
+
+declare module 'weapp-qrcode';