Эх сурвалжийг харах

UI修改完成,基本信息完成

Huanyi 3 өдөр өмнө
parent
commit
d7818853bb
39 өөрчлөгдсөн 1205 нэмэгдсэн , 110 устгасан
  1. 13 2
      apis/auth.js
  2. 16 0
      apis/dict.js
  3. 16 0
      apis/setting.js
  4. 10 0
      locales/index.js
  5. 12 0
      locales/pages-content/my/en_US.js
  6. 13 0
      locales/pages-content/my/info/en_US.js
  7. 13 0
      locales/pages-content/my/info/zh_CN.js
  8. 12 0
      locales/pages-content/my/zh_CN.js
  9. 6 1
      locales/pages/login/en_US.js
  10. 6 1
      locales/pages/login/zh_CN.js
  11. 1 1
      pages-content/home/index.vue
  12. 271 0
      pages-content/my/aggreement/detail.vue
  13. 226 0
      pages-content/my/aggreement/index.vue
  14. 126 77
      pages-content/my/index.vue
  15. 319 0
      pages-content/my/info/index.vue
  16. 1 1
      pages-content/scan/index.vue
  17. 16 0
      pages.json
  18. 7 7
      pages/index.vue
  19. 121 20
      pages/login/login.vue
  20. BIN
      static/pages-content/my/aggrement.png
  21. BIN
      static/pages-content/my/agreement/privacy.png
  22. BIN
      static/pages-content/my/agreement/user.png
  23. BIN
      static/pages-content/my/audit.png
  24. BIN
      static/pages-content/my/file.png
  25. BIN
      static/pages-content/my/info.png
  26. BIN
      static/pages-content/my/language.png
  27. BIN
      static/pages/login/password.png
  28. BIN
      static/pages/login/phone.png
  29. 0 0
      static/pages/tabbar/home-active.png
  30. BIN
      static/pages/tabbar/home.png
  31. BIN
      static/pages/tabbar/mine-active.png
  32. BIN
      static/pages/tabbar/mine.png
  33. BIN
      static/pages/tabbar/scan-active.png
  34. BIN
      static/pages/tabbar/scan.png
  35. BIN
      static/tabbar/home.png
  36. BIN
      static/tabbar/mine-active.png
  37. BIN
      static/tabbar/mine.png
  38. BIN
      static/tabbar/scan-active.png
  39. BIN
      static/tabbar/scan.png

+ 13 - 2
apis/auth.js

@@ -29,13 +29,24 @@ export const getUserInfo = () => {
   })
 }
 
+/**
+ * 获取基本信息
+ * @returns {Promise}
+ */
+export const getBasicInfo = () => {
+  return request({
+    url: '/applet/user/getBasicInfo',
+    method: 'GET'
+  })
+}
+
 /**
  * 退出登录
  * @returns {Promise}
  */
 export const logout = () => {
   return request({
-    url: '/api/logout',
-    method: 'POST'
+    url: '/applet/auth/logout',
+    method: 'DELETE'
   })
 }

+ 16 - 0
apis/dict.js

@@ -0,0 +1,16 @@
+/**
+ * 字典相关 API
+ */
+import request from '@/utils/request'
+
+/**
+ * 根据字典类型获取字典数据
+ * @param {String} dictType - 字典类型
+ * @returns {Promise}
+ */
+export const getDictDataByType = (dictType) => {
+  return request({
+    url: `/system/dict/data/type/${dictType}`,
+    method: 'GET'
+  })
+}

+ 16 - 0
apis/setting.js

@@ -0,0 +1,16 @@
+/**
+ * 设置相关 API
+ */
+import request from '@/utils/request'
+
+/**
+ * 获取协议内容
+ * @param {String} type - 协议类型 ('user' | 'privacy')
+ * @returns {Promise}
+ */
+export const getAgreementContent = (type) => {
+  return request({
+    url: `/setting/applet/${type}`,
+    method: 'GET'
+  })
+}

+ 10 - 0
locales/index.js

@@ -6,6 +6,10 @@ import commonEnUS from './common/en_US'
 import loginZhCN from './pages/login/zh_CN'
 import loginEnUS from './pages/login/en_US'
 
+// 导入pages-content模块
+import myZhCN from './pages-content/my/zh_CN'
+import myEnUS from './pages-content/my/en_US'
+
 // 导入组件模块
 import languageSwitcherZhCN from './components/languageSwitcher/zh_CN'
 import languageSwitcherEnUS from './components/languageSwitcher/en_US'
@@ -15,6 +19,9 @@ export const messages = {
   'zh-CN': {
     common: commonZhCN,
     login: loginZhCN,
+    pagesContent: {
+      my: myZhCN
+    },
     components: {
       languageSwitcher: languageSwitcherZhCN
     }
@@ -22,6 +29,9 @@ export const messages = {
   'en-US': {
     common: commonEnUS,
     login: loginEnUS,
+    pagesContent: {
+      my: myEnUS
+    },
     components: {
       languageSwitcher: languageSwitcherEnUS
     }

+ 12 - 0
locales/pages-content/my/en_US.js

@@ -0,0 +1,12 @@
+import info from './info/en_US'
+
+export default {
+  info,
+  agreement: {
+    title: 'Protocol Description',
+    userAgreement: 'User Agreement',
+    privacyPolicy: 'Privacy Policy',
+    userAgreementDesc: 'View user service agreement',
+    privacyPolicyDesc: 'View privacy agreement'
+  }
+}

+ 13 - 0
locales/pages-content/my/info/en_US.js

@@ -0,0 +1,13 @@
+export default {
+  title: 'Basic Info',
+  nickname: 'Nickname',
+  phoneNumber: 'Phone Number',
+  avatar: 'Avatar',
+  gender: 'Gender',
+  male: 'Male',
+  female: 'Female',
+  unknown: 'Unknown',
+  loading: 'Loading...',
+  loadFailed: 'Load Failed',
+  back: 'Back'
+}

+ 13 - 0
locales/pages-content/my/info/zh_CN.js

@@ -0,0 +1,13 @@
+export default {
+  title: '基本信息',
+  nickname: '昵称',
+  phoneNumber: '手机号',
+  avatar: '头像',
+  gender: '性别',
+  male: '男',
+  female: '女',
+  unknown: '未知',
+  loading: '加载中...',
+  loadFailed: '加载失败',
+  back: '返回'
+}

+ 12 - 0
locales/pages-content/my/zh_CN.js

@@ -0,0 +1,12 @@
+import info from './info/zh_CN'
+
+export default {
+  info,
+  agreement: {
+    title: '协议说明',
+    userAgreement: '用户协议',
+    privacyPolicy: '隐私协议',
+    userAgreementDesc: '查看用户服务协议',
+    privacyPolicyDesc: '查看隐私政策'
+  }
+}

+ 6 - 1
locales/pages/login/en_US.js

@@ -8,5 +8,10 @@ export default {
   phoneError: 'Please enter a valid phone number',
   passwordError: 'Password must be at least 6 characters',
   loginSuccess: 'Login successful',
-  loggingIn: 'Logging in...'
+  loggingIn: 'Logging in...',
+  agreePrefix: 'I have read and agree to',
+  userAgreement: 'User Agreement',
+  and: 'and',
+  privacyPolicy: 'Privacy Agreement',
+  agreementError: 'Please read and agree to the User Agreement and Privacy Agreement'
 }

+ 6 - 1
locales/pages/login/zh_CN.js

@@ -8,5 +8,10 @@ export default {
   phoneError: '请输入正确的手机号',
   passwordError: '密码至少6位',
   loginSuccess: '登录成功',
-  loggingIn: '登录中...'
+  loggingIn: '登录中...',
+  agreePrefix: '我已阅读并同意',
+  userAgreement: '《用户协议》',
+  and: '和',
+  privacyPolicy: '《隐私协议》',
+  agreementError: '请先阅读并同意用户协议和隐私协议'
 }

+ 1 - 1
pages-content/home/index.vue

@@ -38,7 +38,7 @@ onMounted(() => {
   min-height: 100vh;
   display: flex;
   flex-direction: column;
-  background: linear-gradient(180deg, #fff5f5 0%, #f8f9fa 100%);
+  background: linear-gradient(180deg, #E3F2FD 0%, #F0F7FF 100%);
   
   // 自定义头部
   .custom-header {

+ 271 - 0
pages-content/my/aggreement/detail.vue

@@ -0,0 +1,271 @@
+<template>
+  <view class="agreement-detail-page">
+    <!-- 自定义头部 -->
+    <view class="custom-header" :style="{ paddingTop: statusBarHeight + 'px' }">
+      <view class="header-content">
+        <view class="back-btn" @click="handleBack">
+          <text class="back-icon">‹</text>
+        </view>
+        <text class="header-title">{{ pageTitle }}</text>
+        <view class="placeholder"></view>
+      </view>
+    </view>
+    
+    <!-- 页面内容 -->
+    <view class="page-body" :style="{ paddingTop: headerHeight + 'px' }">
+      <view class="content-wrapper">
+        <scroll-view scroll-y class="content-scroll">
+          <rich-text :nodes="agreementContent" class="agreement-content"></rich-text>
+        </scroll-view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { getAgreementContent } from '@/apis/setting'
+
+const { t, locale } = useI18n()
+
+// 状态栏高度
+const statusBarHeight = ref(0)
+
+// 头部总高度(状态栏 + 导航栏)
+const headerHeight = computed(() => {
+  return statusBarHeight.value + 44 // 44px是导航栏高度
+})
+
+// 协议类型
+const agreementType = ref('')
+
+// 协议内容
+const agreementContent = ref('')
+
+// 加载状态
+const loading = ref(false)
+
+// 页面标题
+const pageTitle = computed(() => {
+  return agreementType.value === 'user' 
+    ? t('pagesContent.my.agreement.userAgreement')
+    : t('pagesContent.my.agreement.privacyPolicy')
+})
+
+
+onMounted(async () => {
+  // 获取系统信息
+  const systemInfo = uni.getSystemInfoSync()
+  statusBarHeight.value = systemInfo.statusBarHeight || 0
+  
+  // 获取页面参数
+  const pages = getCurrentPages()
+  const currentPage = pages[pages.length - 1]
+  agreementType.value = currentPage.options.type || 'user'
+  
+  console.log('协议详情页面已加载, 类型:', agreementType.value)
+  
+  // 获取协议内容
+  await fetchAgreementContent()
+})
+
+// 返回上一页
+const handleBack = () => {
+  const pages = getCurrentPages()
+  if (pages.length > 1) {
+    uni.navigateBack()
+  } else {
+    uni.reLaunch({
+      url: '/pages/index'
+    })
+  }
+}
+
+// 获取协议内容
+const fetchAgreementContent = async () => {
+  try {
+    loading.value = true
+    
+    // 显示加载提示
+    uni.showLoading({
+      title: t('common.message.loading'),
+      mask: true
+    })
+    
+    // 调用API获取内容
+    const response = await getAgreementContent(agreementType.value)
+    
+    if (response && response.code === 200 && response.data && response.data.content) {
+      // 解析JSON字符串
+      try {
+        const contentObj = JSON.parse(response.data.content)
+        
+        // 根据当前语言环境选择对应的内容
+        const currentLocale = locale.value // 'zh-CN' 或 'en-US'
+        const localeKey = currentLocale.replace('-', '_') // 转换为 'zh_CN' 或 'en_US'
+        
+        // 优先使用当前语言,如果不存在则使用中文,再不存在则使用任意可用语言
+        agreementContent.value = contentObj[localeKey] || 
+                                 contentObj['zh_CN'] || 
+                                 contentObj['en_US'] || 
+                                 Object.values(contentObj)[0] || 
+                                 ''
+        
+        console.log('已加载协议内容,语言:', localeKey)
+      } catch (parseError) {
+        console.error('解析协议内容JSON失败:', parseError)
+        // 如果解析失败,尝试直接使用原始内容
+        agreementContent.value = response.data.content
+      }
+    } else {
+      throw new Error(response?.msg || '获取失败')
+    }
+  } catch (error) {
+    console.error('获取协议内容失败:', error)
+    
+    uni.showToast({
+      title: error.message || '获取协议内容失败',
+      icon: 'none',
+      duration: 2000
+    })
+    
+    // 失败时显示默认内容
+    agreementContent.value = getDefaultContent()
+  } finally {
+    loading.value = false
+    uni.hideLoading()
+  }
+}
+
+// 获取默认内容(API失败时的后备方案)
+const getDefaultContent = () => {
+  if (agreementType.value === 'user') {
+    return `
+      <div style="padding: 20px; line-height: 1.8; color: #333;">
+        <h2 style="text-align: center; color: #667eea; margin-bottom: 20px;">用户服务协议</h2>
+        <p>协议内容加载失败,请稍后重试。</p>
+      </div>
+    `
+  } else {
+    return `
+      <div style="padding: 20px; line-height: 1.8; color: #333;">
+        <h2 style="text-align: center; color: #667eea; margin-bottom: 20px;">隐私政策</h2>
+        <p>协议内容加载失败,请稍后重试。</p>
+      </div>
+    `
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.agreement-detail-page {
+  width: 100%;
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background: linear-gradient(180deg, #f0f4ff 0%, #f8f9fa 100%);
+  
+  // 自定义头部
+  .custom-header {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    background-color: #ffffff;
+    border-bottom: 1rpx solid #e5e5e5;
+    z-index: 100;
+    
+    .header-content {
+      height: 88rpx;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 32rpx;
+      
+      .back-btn {
+        width: 60rpx;
+        height: 60rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        
+        .back-icon {
+          font-size: 56rpx;
+          color: #333333;
+          font-weight: 300;
+        }
+      }
+      
+      .header-title {
+        flex: 1;
+        text-align: center;
+        font-size: 32rpx;
+        font-weight: 500;
+        color: #000000;
+      }
+      
+      .placeholder {
+        width: 60rpx;
+      }
+    }
+  }
+  
+  // 页面内容
+  .page-body {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    
+    .content-wrapper {
+      flex: 1;
+      margin: 40rpx;
+      background-color: #ffffff;
+      border-radius: 20rpx;
+      box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
+      overflow: hidden;
+      
+      .content-scroll {
+        width: 100%;
+        height: 100%;
+        
+        .agreement-content {
+          padding: 40rpx;
+          
+          ::v-deep h2 {
+            font-size: 36rpx;
+            font-weight: bold;
+            margin-bottom: 30rpx;
+          }
+          
+          ::v-deep h3 {
+            font-size: 30rpx;
+            font-weight: 600;
+            margin: 30rpx 0 20rpx;
+          }
+          
+          ::v-deep p {
+            font-size: 28rpx;
+            line-height: 1.8;
+            margin-bottom: 20rpx;
+            text-align: justify;
+          }
+          
+          ::v-deep ul {
+            padding-left: 40rpx;
+            margin-bottom: 20rpx;
+            
+            li {
+              font-size: 28rpx;
+              line-height: 1.8;
+              margin-bottom: 10rpx;
+              list-style-type: disc;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 226 - 0
pages-content/my/aggreement/index.vue

@@ -0,0 +1,226 @@
+<template>
+  <view class="agreement-page">
+    <!-- 自定义头部 -->
+    <view class="custom-header" :style="{ paddingTop: statusBarHeight + 'px' }">
+      <view class="header-content">
+        <view class="back-btn" @click="handleBack">
+          <text class="back-icon">‹</text>
+        </view>
+        <text class="header-title">{{ t('pagesContent.my.agreement.title') }}</text>
+        <view class="placeholder"></view>
+      </view>
+    </view>
+    
+    <!-- 页面内容 -->
+    <view class="page-body" :style="{ paddingTop: headerHeight + 'px' }">
+      <!-- 协议列表 -->
+      <view class="agreement-list">
+        <!-- 用户协议 -->
+        <view class="list-item" @click="handleUserAgreement">
+          <view class="item-content">
+            <view class="item-icon-wrapper">
+              <image class="item-icon" src="/static/pages-content/my/agreement/user.png" mode="aspectFit" />
+            </view>
+            <view class="item-info">
+              <text class="item-title">{{ t('pagesContent.my.agreement.userAgreement') }}</text>
+              <text class="item-desc">{{ t('pagesContent.my.agreement.userAgreementDesc') }}</text>
+            </view>
+          </view>
+          <text class="item-arrow">›</text>
+        </view>
+        
+        <!-- 隐私协议 -->
+        <view class="list-item" @click="handlePrivacyPolicy">
+          <view class="item-content">
+            <view class="item-icon-wrapper">
+              <image class="item-icon" src="/static/pages-content/my/agreement/privacy.png" mode="aspectFit" />
+            </view>
+            <view class="item-info">
+              <text class="item-title">{{ t('pagesContent.my.agreement.privacyPolicy') }}</text>
+              <text class="item-desc">{{ t('pagesContent.my.agreement.privacyPolicyDesc') }}</text>
+            </view>
+          </view>
+          <text class="item-arrow">›</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+
+// 状态栏高度
+const statusBarHeight = ref(0)
+
+// 头部总高度(状态栏 + 导航栏 + 额外间距)
+const headerHeight = computed(() => {
+  return statusBarHeight.value + 44 + 10 // 44px是导航栏高度,100px是额外间距
+})
+
+onMounted(() => {
+  // 获取系统信息
+  const systemInfo = uni.getSystemInfoSync()
+  statusBarHeight.value = systemInfo.statusBarHeight || 0
+  
+  console.log('协议说明页面已加载')
+})
+
+// 返回上一页
+const handleBack = () => {
+  const pages = getCurrentPages()
+  if (pages.length > 1) {
+    uni.navigateBack()
+  } else {
+    uni.reLaunch({
+      url: '/pages/index'
+    })
+  }
+}
+
+// 用户协议
+const handleUserAgreement = () => {
+  // TODO: 跳转到用户协议详情页
+  uni.navigateTo({
+    url: '/pages-content/my/aggreement/detail?type=user'
+  })
+}
+
+// 隐私协议
+const handlePrivacyPolicy = () => {
+  // TODO: 跳转到隐私协议详情页
+  uni.navigateTo({
+    url: '/pages-content/my/aggreement/detail?type=privacy'
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+.agreement-page {
+  width: 100%;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background: linear-gradient(180deg, #f0f4ff 0%, #f8f9fa 100%);
+  
+  // 自定义头部
+  .custom-header {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    background-color: #ffffff;
+    border-bottom: 1rpx solid #e5e5e5;
+    z-index: 100;
+    
+    .header-content {
+      height: 88rpx;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 32rpx;
+      
+      .back-btn {
+        width: 60rpx;
+        height: 60rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        
+        .back-icon {
+          font-size: 56rpx;
+          color: #333333;
+          font-weight: 300;
+        }
+      }
+      
+      .header-title {
+        flex: 1;
+        text-align: center;
+        font-size: 32rpx;
+        font-weight: 500;
+        color: #000000;
+      }
+      
+      .placeholder {
+        width: 60rpx;
+      }
+    }
+  }
+  
+  // 页面内容
+  .page-body {
+    flex: 1;
+    padding: 40rpx;
+    
+    // 协议列表
+    .agreement-list {
+      .list-item {
+        background-color: #ffffff;
+        border-radius: 20rpx;
+        padding: 40rpx;
+        margin-bottom: 24rpx;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
+        transition: all 0.3s ease;
+        position: relative;
+        overflow: hidden;
+        
+        &:active {
+          transform: translateY(-4rpx);
+          box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
+        }
+        
+        .item-content {
+          flex: 1;
+          display: flex;
+          align-items: center;
+          
+          .item-icon-wrapper {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin-right: 32rpx;
+            
+            .item-icon {
+              width: 48rpx;
+              height: 48rpx;
+            }
+          }
+          
+          .item-info {
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+            
+            .item-title {
+              font-size: 32rpx;
+              color: #333333;
+              font-weight: 600;
+              margin-bottom: 8rpx;
+            }
+            
+            .item-desc {
+              font-size: 24rpx;
+              color: #999999;
+              line-height: 1.5;
+            }
+          }
+        }
+        
+        .item-arrow {
+          font-size: 48rpx;
+          color: #d0d0d0;
+          font-weight: 300;
+          margin-left: 20rpx;
+        }
+      }
+    }
+  }
+}
+</style>

+ 126 - 77
pages-content/my/index.vue

@@ -1,5 +1,9 @@
 <template>
-  <view class="my-page">
+  <!-- 基本信息组件 -->
+  <BasicInfo v-if="showBasicInfo" @back="handleBackToMain" />
+  
+  <!-- 我的主页 -->
+  <view v-else class="my-page">
     <!-- 自定义头部 -->
     <view class="custom-header" :style="{ paddingTop: statusBarHeight + 'px' }">
       <view class="header-content">
@@ -26,7 +30,7 @@
         <!-- 基本信息 -->
         <view class="list-item" @click="handleBasicInfo">
           <view class="item-left">
-            <text class="item-icon">👤</text>
+            <image class="item-icon" src="/static/pages-content/my/info.png" mode="aspectFit" />
             <text class="item-label">{{ t('common.mine.basicInfo') }}</text>
           </view>
           <text class="item-arrow">›</text>
@@ -35,7 +39,7 @@
         <!-- 文件管理 -->
         <view class="list-item" @click="handleFileManage">
           <view class="item-left">
-            <text class="item-icon">📁</text>
+            <image class="item-icon" src="/static/pages-content/my/file.png" mode="aspectFit" />
             <text class="item-label">{{ t('common.mine.fileManage') }}</text>
           </view>
           <text class="item-arrow">›</text>
@@ -44,7 +48,7 @@
         <!-- 审核管理 -->
         <view class="list-item" @click="handleAuditManage">
           <view class="item-left">
-            <text class="item-icon">📋</text>
+            <image class="item-icon" src="/static/pages-content/my/audit.png" mode="aspectFit" />
             <text class="item-label">{{ t('common.mine.auditManage') }}</text>
           </view>
           <text class="item-arrow">›</text>
@@ -53,23 +57,20 @@
         <!-- 语言切换 -->
         <view class="list-item">
           <view class="item-left">
-            <text class="item-icon">🌐</text>
+            <image class="item-icon" src="/static/pages-content/my/language.png" mode="aspectFit" />
             <text class="item-label">{{ t('common.mine.languageSwitch') }}</text>
           </view>
-          <view class="item-right">
-            <text class="language-text">{{ currentLanguage }}</text>
-            <switch 
-              :checked="isEnglish" 
-              @change="handleLanguageChange"
-              color="#007aff"
-            />
+          <view class="language-switcher" @click="handleLanguageChange">
+            <text class="language-text" :class="{ active: locale === 'zh-CN' }">中</text>
+            <text class="language-separator">|</text>
+            <text class="language-text" :class="{ active: locale === 'en-US' }">EN</text>
           </view>
         </view>
         
         <!-- 协议说明 -->
         <view class="list-item" @click="handleProtocol">
           <view class="item-left">
-            <text class="item-icon">📄</text>
+            <image class="item-icon" src="/static/pages-content/my/aggrement.png" mode="aspectFit" />
             <text class="item-label">{{ t('common.mine.protocol') }}</text>
           </view>
           <text class="item-arrow">›</text>
@@ -85,16 +86,20 @@
 </template>
 
 <script setup>
+import BasicInfo from './info/index.vue'
 import { ref, computed, onMounted } from 'vue'
 import { useI18n } from 'vue-i18n'
 import { useUserStore } from '@/store/index'
 import { useLocaleStore } from '@/store/locale'
-import { getUserInfo as getUserInfoAPI } from '@/apis/auth'
+import { getUserInfo as getUserInfoAPI, logout as logoutAPI } from '@/apis/auth'
 
 const { t, locale } = useI18n()
 const userStore = useUserStore()
 const localeStore = useLocaleStore()
 
+// 控制显示哪个组件
+const showBasicInfo = ref(false)
+
 // 状态栏高度
 const statusBarHeight = ref(0)
 
@@ -107,11 +112,6 @@ const userInfo = ref({
 // 加载状态
 const loading = ref(false)
 
-// 当前语言显示
-const currentLanguage = computed(() => {
-  return locale.value === 'zh-CN' ? t('common.language.zh') : t('common.language.en')
-})
-
 // 显示的昵称(带加载状态)
 const displayNickname = computed(() => {
   if (loading.value) {
@@ -120,11 +120,6 @@ const displayNickname = computed(() => {
   return userInfo.value.nickname || t('common.mine.defaultNickname')
 })
 
-// 是否英文
-const isEnglish = computed(() => {
-  return locale.value === 'en-US'
-})
-
 onMounted(() => {
   // 获取系统信息
   const systemInfo = uni.getSystemInfoSync()
@@ -189,11 +184,12 @@ const fetchUserInfo = async () => {
 
 // 基本信息
 const handleBasicInfo = () => {
-  uni.showToast({
-    title: '基本信息',
-    icon: 'none'
-  })
-  // TODO: 跳转到基本信息页面
+  showBasicInfo.value = true
+}
+
+// 从基本信息返回主页
+const handleBackToMain = () => {
+  showBasicInfo.value = false
 }
 
 // 文件管理
@@ -215,31 +211,24 @@ const handleAuditManage = () => {
 }
 
 // 语言切换
-const handleLanguageChange = (e) => {
-  const isChecked = e.detail.value
-  const newLocale = isChecked ? 'en-US' : 'zh-CN'
-  
-  const success = localeStore.setLocale(newLocale)
+const handleLanguageChange = () => {
+  localeStore.toggleLocale()
   
-  if (success) {
-    // 延迟一下让语言切换生效后再显示提示
-    setTimeout(() => {
-      uni.showToast({
-        title: t('common.language.switchSuccess'),
-        icon: 'success',
-        duration: 1500
-      })
-    }, 100)
-  }
+  // 延迟一下让语言切换生效后再显示提示
+  setTimeout(() => {
+    uni.showToast({
+      title: t('common.language.switchSuccess'),
+      icon: 'success',
+      duration: 1500
+    })
+  }, 100)
 }
 
 // 协议说明
 const handleProtocol = () => {
-  uni.showToast({
-    title: '协议说明',
-    icon: 'none'
+  uni.navigateTo({
+    url: '/pages-content/my/aggreement/index'
   })
-  // TODO: 跳转到协议说明页面
 }
 
 // 退出登录
@@ -249,24 +238,56 @@ const handleLogout = () => {
     content: t('common.mine.logoutConfirm'),
     confirmText: t('common.button.confirm'),
     cancelText: t('common.button.cancel'),
-    success: (res) => {
+    success: async (res) => {
       if (res.confirm) {
-        // 清除本地token和用户信息缓存
-        userStore.logout()
-        
-        // 显示退出成功提示
-        uni.showToast({
-          title: t('common.mine.logoutSuccess'),
-          icon: 'success',
-          duration: 1500
-        })
-        
-        // 延迟跳转到登录页
-        setTimeout(() => {
-          uni.reLaunch({
-            url: '/pages/login/login'
+        try {
+          // 显示加载状态
+          uni.showLoading({
+            title: t('common.message.loading'),
+            mask: true
           })
-        }, 1500)
+          
+          // 调用后端API
+          await logoutAPI()
+          
+          // API调用成功后,清除本地token和用户信息缓存
+          userStore.logout()
+          
+          uni.hideLoading()
+          
+          // 显示退出成功提示
+          uni.showToast({
+            title: t('common.mine.logoutSuccess'),
+            icon: 'success',
+            duration: 1500
+          })
+          
+          // 延迟跳转到登录页
+          setTimeout(() => {
+            uni.reLaunch({
+              url: '/pages/login/login'
+            })
+          }, 1500)
+          
+        } catch (error) {
+          uni.hideLoading()
+          console.error('退出登录失败:', error)
+          
+          // 即使 API失败,也清除本地数据并跳转
+          userStore.logout()
+          
+          uni.showToast({
+            title: t('common.mine.logoutSuccess'),
+            icon: 'success',
+            duration: 1500
+          })
+          
+          setTimeout(() => {
+            uni.reLaunch({
+              url: '/pages/login/login'
+            })
+          }, 1500)
+        }
       }
     }
   })
@@ -279,7 +300,7 @@ const handleLogout = () => {
   min-height: 100vh;
   display: flex;
   flex-direction: column;
-  background: linear-gradient(180deg, #f0f4ff 0%, #f8f9fa 100%);
+  background: linear-gradient(180deg, #f8fcff 0%, #ffffff 100%);
   
   // 自定义头部
   .custom-header {
@@ -313,14 +334,14 @@ const handleLogout = () => {
     
     // 用户名片区域
     .user-card {
-      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      background: linear-gradient(135deg, #6ec7f5 0%, #4eb8f0 100%);
       border-radius: 24rpx;
       padding: 80rpx 40rpx 60rpx;
       display: flex;
       flex-direction: column;
       align-items: center;
       margin-bottom: 40rpx;
-      box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
+      box-shadow: 0 8rpx 24rpx rgba(110, 199, 245, 0.25);
       position: relative;
       overflow: hidden;
       
@@ -385,7 +406,7 @@ const handleLogout = () => {
       border-radius: 20rpx;
       overflow: hidden;
       margin-bottom: 40rpx;
-      box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
+      box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
       
       .list-item {
         display: flex;
@@ -401,8 +422,7 @@ const handleLogout = () => {
         }
         
         &:active {
-          background-color: #f8f9ff;
-          transform: scale(0.98);
+          background-color: #f0faff;
         }
         
         // 左侧渐变条
@@ -414,7 +434,7 @@ const handleLogout = () => {
           transform: translateY(-50%);
           width: 6rpx;
           height: 60%;
-          background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
+          background: linear-gradient(180deg, #6ec7f5 0%, #4eb8f0 100%);
           border-radius: 0 6rpx 6rpx 0;
           opacity: 0;
           transition: opacity 0.3s;
@@ -429,9 +449,9 @@ const handleLogout = () => {
           align-items: center;
           
           .item-icon {
-            font-size: 44rpx;
+            width: 44rpx;
+            height: 44rpx;
             margin-right: 28rpx;
-            filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.1));
           }
           
           .item-label {
@@ -441,15 +461,38 @@ const handleLogout = () => {
           }
         }
         
-        .item-right {
+        .language-switcher {
           display: flex;
           align-items: center;
-          gap: 20rpx;
+          background: rgba(110, 199, 245, 0.08);
+          backdrop-filter: blur(10rpx);
+          border-radius: 30rpx;
+          padding: 8rpx 20rpx;
+          cursor: pointer;
+          transition: all 0.3s;
+          gap: 8rpx;
+          
+          &:active {
+            transform: scale(0.95);
+            background: rgba(110, 199, 245, 0.15);
+          }
           
           .language-text {
-            font-size: 26rpx;
-            color: #667eea;
+            font-size: 24rpx;
+            color: #999;
             font-weight: 500;
+            transition: all 0.3s;
+            
+            &.active {
+              color: #6ec7f5;
+              font-weight: 700;
+            }
+          }
+          
+          .language-separator {
+            font-size: 24rpx;
+            color: #ccc;
+            font-weight: 300;
           }
         }
         
@@ -468,6 +511,7 @@ const handleLogout = () => {
       .logout-btn {
         width: 100%;
         height: 96rpx;
+        line-height: 96rpx;
         background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
         border-radius: 20rpx;
         border: none;
@@ -476,11 +520,16 @@ const handleLogout = () => {
         font-weight: 600;
         box-shadow: 0 6rpx 20rpx rgba(255, 107, 107, 0.3);
         letter-spacing: 2rpx;
+        padding: 0;
         
         &:active {
           opacity: 0.9;
           transform: scale(0.98);
         }
+        
+        &::after {
+          border: none;
+        }
       }
     }
   }

+ 319 - 0
pages-content/my/info/index.vue

@@ -0,0 +1,319 @@
+<template>
+  <view class="basic-info-page">
+    <!-- 自定义头部 -->
+    <view class="custom-header" :style="{ paddingTop: statusBarHeight + 'px' }">
+      <view class="header-content">
+        <view class="back-btn" @click="handleBack">
+          <text class="back-icon">‹</text>
+        </view>
+        <text class="header-title">{{ t('pagesContent.my.info.title') }}</text>
+        <view class="placeholder"></view>
+      </view>
+    </view>
+    
+    <!-- 页面内容 -->
+    <view class="page-body">
+      <!-- 加载状态 -->
+      <view v-if="loading" class="loading-state">
+        <text class="loading-text">{{ t('pagesContent.my.info.loading') }}</text>
+      </view>
+      
+      <!-- 信息列表 -->
+      <view v-else class="info-list">
+        <!-- 头像 -->
+        <view class="info-item avatar-item">
+          <text class="item-label">{{ t('pagesContent.my.info.avatar') }}</text>
+          <image 
+            class="avatar-image" 
+            :src="basicInfo.avatar || '/static/default-avatar.svg'" 
+            mode="aspectFill"
+          />
+        </view>
+        
+        <!-- 昵称 -->
+        <view class="info-item">
+          <text class="item-label">{{ t('pagesContent.my.info.nickname') }}</text>
+          <text class="item-value">{{ basicInfo.nickname || '-' }}</text>
+        </view>
+        
+        <!-- 手机号 -->
+        <view class="info-item">
+          <text class="item-label">{{ t('pagesContent.my.info.phoneNumber') }}</text>
+          <text class="item-value">{{ basicInfo.phoneNumber || '-' }}</text>
+        </view>
+        
+        <!-- 性别 -->
+        <view class="info-item">
+          <text class="item-label">{{ t('pagesContent.my.info.gender') }}</text>
+          <view class="gender-value">
+            <text class="item-value" :class="genderClass">{{ genderDisplay }}</text>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { getBasicInfo } from '@/apis/auth'
+import { getDictDataByType } from '@/apis/dict'
+
+const { t, locale } = useI18n()
+
+// 定义事件
+const emit = defineEmits(['back'])
+
+// 状态栏高度
+const statusBarHeight = ref(0)
+
+// 基本信息
+const basicInfo = ref({
+  nickname: '',
+  phoneNumber: '',
+  avatar: '',
+  gender: ''
+})
+
+// 加载状态
+const loading = ref(false)
+
+// 性别字典数据
+const genderDictList = ref([])
+
+// 性别显示
+const genderDisplay = computed(() => {
+  if (basicInfo.value.gender === null || basicInfo.value.gender === undefined || basicInfo.value.gender === '') return '-'
+  
+  // 从字典数据中查找匹配的项,使用dictValue匹配gender
+  const genderItem = genderDictList.value.find(item => 
+    String(item.dictValue) === String(basicInfo.value.gender)
+  )
+  
+  if (!genderItem) return '-'
+  
+  // 解析dictLabel的JSON字符串
+  try {
+    const labelObj = JSON.parse(genderItem.dictLabel)
+    // 将locale格式从 zh-CN 转换为 zh_CN
+    const localeKey = locale.value.replace('-', '_')
+    // 根据当前语言返回对应的标签
+    return labelObj[localeKey] || labelObj['zh_CN'] || genderItem.dictLabel
+  } catch (error) {
+    // 如果解析失败,直接返回dictLabel
+    return genderItem.dictLabel
+  }
+})
+
+// 性别样式类
+const genderClass = computed(() => {
+  if (basicInfo.value.gender === null || basicInfo.value.gender === undefined || basicInfo.value.gender === '') return ''
+  
+  const genderItem = genderDictList.value.find(item => 
+    String(item.dictValue) === String(basicInfo.value.gender)
+  )
+  
+  return genderItem?.listClass || ''
+})
+
+onMounted(() => {
+  // 获取系统信息
+  const systemInfo = uni.getSystemInfoSync()
+  statusBarHeight.value = systemInfo.statusBarHeight || 0
+  
+  // 获取字典数据
+  fetchGenderDict()
+  
+  // 获取基本信息
+  fetchBasicInfo()
+})
+
+// 获取性别字典数据
+const fetchGenderDict = async () => {
+  try {
+    const response = await getDictDataByType('sys_user_sex')
+    
+    if (response && response.data) {
+      genderDictList.value = response.data
+    }
+  } catch (error) {
+    console.error('获取性别字典失败:', error)
+  }
+}
+
+// 获取基本信息
+const fetchBasicInfo = async () => {
+  try {
+    loading.value = true
+    
+    const response = await getBasicInfo()
+    
+    if (response && response.data) {
+      basicInfo.value = response.data
+    }
+  } catch (error) {
+    console.error('获取基本信息失败:', error)
+    
+    uni.showToast({
+      title: t('pagesContent.my.info.loadFailed'),
+      icon: 'none',
+      duration: 2000
+    })
+  } finally {
+    loading.value = false
+  }
+}
+
+// 返回
+const handleBack = () => {
+  emit('back')
+}
+</script>
+
+<style lang="scss" scoped>
+.basic-info-page {
+  width: 100%;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background: linear-gradient(180deg, #f8fcff 0%, #ffffff 100%);
+  
+  // 自定义头部
+  .custom-header {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    background-color: #ffffff;
+    border-bottom: 1rpx solid #e5e5e5;
+    z-index: 100;
+    
+    .header-content {
+      height: 88rpx;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 32rpx;
+      
+      .back-btn {
+        width: 60rpx;
+        height: 60rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        
+        .back-icon {
+          font-size: 56rpx;
+          color: #333333;
+          font-weight: 300;
+        }
+      }
+      
+      .header-title {
+        flex: 1;
+        text-align: center;
+        font-size: 32rpx;
+        font-weight: 500;
+        color: #000000;
+      }
+      
+      .placeholder {
+        width: 60rpx;
+      }
+    }
+  }
+  
+  // 页面内容
+  .page-body {
+    flex: 1;
+    margin-top: 88rpx;
+    padding: 40rpx;
+    
+    // 加载状态
+    .loading-state {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 120rpx 0;
+      
+      .loading-text {
+        font-size: 28rpx;
+        color: #999999;
+      }
+    }
+    
+    // 信息列表
+    .info-list {
+      margin-top: 100rpx;
+      background-color: #ffffff;
+      border-radius: 20rpx;
+      overflow: hidden;
+      box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
+      
+      .info-item {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 36rpx 40rpx;
+        border-bottom: 1rpx solid #f0f0f0;
+        
+        &:last-child {
+          border-bottom: none;
+        }
+        
+        &.avatar-item {
+          .avatar-image {
+            width: 100rpx;
+            height: 100rpx;
+            border-radius: 50rpx;
+            border: 4rpx solid #6ec7f5;
+          }
+        }
+        
+        .item-label {
+          font-size: 30rpx;
+          color: #666666;
+          font-weight: 500;
+        }
+        
+        .gender-value {
+          display: flex;
+          align-items: center;
+        }
+        
+        .item-value {
+          font-size: 30rpx;
+          color: #333333;
+          font-weight: 500;
+          
+          // 字典listClass样式
+          &.default {
+            color: #909399;
+          }
+          
+          &.primary {
+            color: #6ec7f5;
+          }
+          
+          &.success {
+            color: #67c23a;
+          }
+          
+          &.info {
+            color: #909399;
+          }
+          
+          &.warning {
+            color: #e6a23c;
+          }
+          
+          &.danger {
+            color: #f56c6c;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 1 - 1
pages-content/scan/index.vue

@@ -38,7 +38,7 @@ onMounted(() => {
   min-height: 100vh;
   display: flex;
   flex-direction: column;
-  background: linear-gradient(180deg, #e0f7fa 0%, #f8f9fa 100%);
+  background: linear-gradient(180deg, #E3F2FD 0%, #F0F7FF 100%);
   
   // 自定义头部
   .custom-header {

+ 16 - 0
pages.json

@@ -14,6 +14,22 @@
         "navigationStyle": "custom",
         "enablePullDownRefresh": false
       }
+    },
+    {
+      "path": "pages-content/my/aggreement/index",
+      "style": {
+        "navigationBarTitleText": "协议说明",
+        "navigationStyle": "custom",
+        "enablePullDownRefresh": false
+      }
+    },
+    {
+      "path": "pages-content/my/aggreement/detail",
+      "style": {
+        "navigationBarTitleText": "协议详情",
+        "navigationStyle": "custom",
+        "enablePullDownRefresh": false
+      }
     }
   ],
   "globalStyle": {

+ 7 - 7
pages/index.vue

@@ -49,20 +49,20 @@ const tabList = computed(() => [
   {
     name: 'home',
     label: t('common.page.home'),
-    icon: '/static/tabbar/home.png',
-    activeIcon: '/static/tabbar/home-active.png'
+    icon: '/static/pages/tabbar/home.png',
+    activeIcon: '/static/pages/tabbar/home-active.png'
   },
   {
     name: 'scan',
     label: t('common.page.scan'),
-    icon: '/static/tabbar/scan.png',
-    activeIcon: '/static/tabbar/scan-active.png'
+    icon: '/static/pages/tabbar/scan.png',
+    activeIcon: '/static/pages/tabbar/scan-active.png'
   },
   {
     name: 'mine',
     label: t('common.page.mine'),
-    icon: '/static/tabbar/mine.png',
-    activeIcon: '/static/tabbar/mine-active.png'
+    icon: '/static/pages/tabbar/mine.png',
+    activeIcon: '/static/pages/tabbar/mine-active.png'
   }
 ])
 
@@ -130,7 +130,7 @@ const switchTab = (name) => {
       }
       
       .tab-text {
-        color: #007aff;
+        color: #2E7CF6;
         font-weight: 500;
       }
     }

+ 121 - 20
pages/login/login.vue

@@ -2,8 +2,9 @@
   <view class="login-container">
     <!-- 语言切换按钮 -->
     <view class="language-switcher" @click="switchLanguage">
-      <text class="language-icon">🌐</text>
-      <text class="language-text">{{ currentLanguageName }}</text>
+      <text class="language-text" :class="{ active: locale === 'zh-CN' }">中</text>
+      <text class="language-separator">|</text>
+      <text class="language-text" :class="{ active: locale === 'en-US' }">EN</text>
     </view>
     
     <view class="login-box">
@@ -15,7 +16,7 @@
       <view class="form-section">
         <view class="form-item">
           <view class="input-wrapper">
-            <text class="input-icon">📱</text>
+            <image class="input-icon" src="/static/pages/login/phone.png" mode="aspectFit" />
             <input
               v-model="phone"
               type="text"
@@ -29,7 +30,7 @@
         
         <view class="form-item">
           <view class="input-wrapper">
-            <text class="input-icon">🔒</text>
+            <image class="input-icon" src="/static/pages/login/password.png" mode="aspectFit" />
             <input
               v-model="password"
               :password="!showPassword"
@@ -45,6 +46,25 @@
           </view>
         </view>
         
+        <!-- 协议勾选 -->
+        <view class="agreement-section">
+          <checkbox-group @change="handleAgreementChange">
+            <label class="agreement-label">
+              <checkbox 
+                :checked="agreedToTerms" 
+                class="agreement-checkbox"
+                color="#2E7CF6"
+              />
+              <view class="agreement-text">
+                <text class="prefix">{{ $t('login.agreePrefix') }}</text>
+                <text class="link" @click.stop="viewUserAgreement">{{ $t('login.userAgreement') }}</text>
+                <text class="prefix">{{ $t('login.and') }}</text>
+                <text class="link" @click.stop="viewPrivacyPolicy">{{ $t('login.privacyPolicy') }}</text>
+              </view>
+            </label>
+          </checkbox-group>
+        </view>
+        
         <button 
           class="login-btn"
           @click="handleLogin"
@@ -72,6 +92,7 @@ const phone = ref('')
 const password = ref('')
 const showPassword = ref(false)
 const isLanguageSwitching = ref(false)
+const agreedToTerms = ref(false)
 
 // 当前语言名称
 const currentLanguageName = computed(() => {
@@ -135,6 +156,25 @@ const togglePassword = () => {
   showPassword.value = !showPassword.value
 }
 
+// 协议勾选变化
+const handleAgreementChange = (e) => {
+  agreedToTerms.value = e.detail.value.length > 0
+}
+
+// 查看用户协议
+const viewUserAgreement = () => {
+  uni.navigateTo({
+    url: '/pages-content/my/aggreement/detail?type=user'
+  })
+}
+
+// 查看隐私协议
+const viewPrivacyPolicy = () => {
+  uni.navigateTo({
+    url: '/pages-content/my/aggreement/detail?type=privacy'
+  })
+}
+
 // 登录处理
 const handleLogin = async () => {
   // 验证手机号
@@ -157,6 +197,16 @@ const handleLogin = async () => {
     return
   }
   
+  // 验证是否同意协议
+  if (!agreedToTerms.value) {
+    uni.showToast({
+      title: t('login.agreementError'),
+      icon: 'none',
+      duration: 2000
+    })
+    return
+  }
+  
   try {
     uni.showLoading({
       title: t('login.loggingIn'),
@@ -207,7 +257,7 @@ const handleLogin = async () => {
 <style lang="scss" scoped>
 .login-container {
   min-height: 100vh;
-  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  background: linear-gradient(135deg, #2E7CF6 0%, #1A5FC7 100%);
   display: flex;
   flex-direction: column;
   align-items: center;
@@ -223,17 +273,18 @@ const handleLogin = async () => {
   margin-right: 40rpx;
   display: flex;
   align-items: center;
-  background: rgba(255, 255, 255, 0.25);
+  background: rgba(255, 255, 255, 0.95);
   backdrop-filter: blur(10rpx);
-  border-radius: 40rpx;
-  padding: 16rpx 28rpx;
+  border-radius: 30rpx;
+  padding: 12rpx 24rpx;
   cursor: pointer;
   transition: all 0.3s;
   box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
+  gap: 8rpx;
   
   &:active {
     transform: scale(0.95);
-    background: rgba(255, 255, 255, 0.35);
+    background: rgba(255, 255, 255, 1);
   }
 }
 
@@ -246,15 +297,22 @@ const handleLogin = async () => {
   box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.15);
 }
 
-.language-icon {
-  font-size: 36rpx;
-  margin-right: 8rpx;
-}
-
 .language-text {
-  font-size: 28rpx;
-  color: #ffffff;
+  font-size: 26rpx;
+  color: #999;
   font-weight: 500;
+  transition: all 0.3s;
+  
+  &.active {
+    color: #2E7CF6;
+    font-weight: 700;
+  }
+}
+
+.language-separator {
+  font-size: 26rpx;
+  color: #ccc;
+  font-weight: 300;
 }
 
 .logo-section {
@@ -296,14 +354,16 @@ const handleLogin = async () => {
   
   &:focus-within {
     background: #fff;
-    border-color: #667eea;
-    box-shadow: 0 0 0 4rpx rgba(102, 126, 234, 0.1);
+    border-color: #2E7CF6;
+    box-shadow: 0 0 0 4rpx rgba(46, 124, 246, 0.1);
   }
 }
 
 .input-icon {
-  font-size: 40rpx;
+  width: 40rpx;
+  height: 40rpx;
   margin-right: 16rpx;
+  opacity: 0.6;
 }
 
 .input-field {
@@ -325,7 +385,7 @@ const handleLogin = async () => {
 .login-btn {
   width: 100%;
   height: 96rpx;
-  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  background: linear-gradient(135deg, #2E7CF6 0%, #1A5FC7 100%);
   color: #ffffff;
   font-size: 32rpx;
   font-weight: bold;
@@ -348,4 +408,45 @@ const handleLogin = async () => {
     transform: none;
   }
 }
+
+.agreement-section {
+  margin-top: 24rpx;
+  margin-bottom: 8rpx;
+}
+
+.agreement-label {
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+}
+
+.agreement-checkbox {
+  margin-right: 12rpx;
+  transform: scale(0.7);
+}
+
+.agreement-text {
+  flex: 1;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  line-height: 1.6;
+  
+  .prefix {
+    font-size: 24rpx;
+    color: #666;
+    margin-right: 4rpx;
+  }
+  
+  .link {
+    font-size: 24rpx;
+    color: #2E7CF6;
+    text-decoration: none;
+    margin: 0 4rpx;
+    
+    &:active {
+      opacity: 0.7;
+    }
+  }
+}
 </style>

BIN
static/pages-content/my/aggrement.png


BIN
static/pages-content/my/agreement/privacy.png


BIN
static/pages-content/my/agreement/user.png


BIN
static/pages-content/my/audit.png


BIN
static/pages-content/my/file.png


BIN
static/pages-content/my/info.png


BIN
static/pages-content/my/language.png


BIN
static/pages/login/password.png


BIN
static/pages/login/phone.png


+ 0 - 0
static/tabbar/home-active.png → static/pages/tabbar/home-active.png


BIN
static/pages/tabbar/home.png


BIN
static/pages/tabbar/mine-active.png


BIN
static/pages/tabbar/mine.png


BIN
static/pages/tabbar/scan-active.png


BIN
static/pages/tabbar/scan.png


BIN
static/tabbar/home.png


BIN
static/tabbar/mine-active.png


BIN
static/tabbar/mine.png


BIN
static/tabbar/scan-active.png


BIN
static/tabbar/scan.png