Quellcode durchsuchen

新增网络请求等接口封装 && 轮播图优化

maude vor 9 Stunden
Ursprung
Commit
d66b6bc24b
48 geänderte Dateien mit 2379 neuen und 85 gelöschten Zeilen
  1. 34 0
      config/index.js
  2. 144 0
      examples/request-example.js
  3. 6 1
      jsconfig.json
  4. 128 0
      node_modules/.vue-global-types/vue_3.5_0.d.ts
  5. 178 67
      pages/discover/discover.vue
  6. 125 14
      types/global.d.ts
  7. 3 3
      unpackage/dist/cache/.vite/deps/_metadata.json
  8. 11 0
      unpackage/dist/dev/.nvue/app.css.js
  9. 2 0
      unpackage/dist/dev/.nvue/app.js
  10. 15 0
      unpackage/dist/dev/app-plus/__uniappautomator.js
  11. 31 0
      unpackage/dist/dev/app-plus/__uniappchooselocation.js
  12. BIN
      unpackage/dist/dev/app-plus/__uniapperror.png
  13. 31 0
      unpackage/dist/dev/app-plus/__uniappopenlocation.js
  14. 31 0
      unpackage/dist/dev/app-plus/__uniapppicker.js
  15. 6 0
      unpackage/dist/dev/app-plus/__uniappquill.js
  16. 0 0
      unpackage/dist/dev/app-plus/__uniappquillimageresize.js
  17. 31 0
      unpackage/dist/dev/app-plus/__uniappscan.js
  18. BIN
      unpackage/dist/dev/app-plus/__uniappsuccess.png
  19. 24 0
      unpackage/dist/dev/app-plus/__uniappview.html
  20. 11 0
      unpackage/dist/dev/app-plus/app-config-service.js
  21. 1 0
      unpackage/dist/dev/app-plus/app-config.js
  22. 710 0
      unpackage/dist/dev/app-plus/app-service.js
  23. 0 0
      unpackage/dist/dev/app-plus/app.css
  24. 168 0
      unpackage/dist/dev/app-plus/manifest.json
  25. 47 0
      unpackage/dist/dev/app-plus/pages/codebase/codebase.css
  26. 223 0
      unpackage/dist/dev/app-plus/pages/discover/discover.css
  27. 47 0
      unpackage/dist/dev/app-plus/pages/editor/editor.css
  28. 47 0
      unpackage/dist/dev/app-plus/pages/profile/profile.css
  29. 47 0
      unpackage/dist/dev/app-plus/pages/study/study.css
  30. BIN
      unpackage/dist/dev/app-plus/static/icons/question.png
  31. BIN
      unpackage/dist/dev/app-plus/static/icons/study.png
  32. BIN
      unpackage/dist/dev/app-plus/static/icons/tutorial.png
  33. BIN
      unpackage/dist/dev/app-plus/static/icons/video.png
  34. BIN
      unpackage/dist/dev/app-plus/static/icons/vip.png
  35. BIN
      unpackage/dist/dev/app-plus/static/tabbar/codebase_normal.png
  36. BIN
      unpackage/dist/dev/app-plus/static/tabbar/codebase_selected.png
  37. BIN
      unpackage/dist/dev/app-plus/static/tabbar/discover_normal.png
  38. BIN
      unpackage/dist/dev/app-plus/static/tabbar/discover_selected.png
  39. BIN
      unpackage/dist/dev/app-plus/static/tabbar/editor_normal.png
  40. BIN
      unpackage/dist/dev/app-plus/static/tabbar/editor_selected.png
  41. BIN
      unpackage/dist/dev/app-plus/static/tabbar/profile_normal.png
  42. BIN
      unpackage/dist/dev/app-plus/static/tabbar/profile_selected.png
  43. BIN
      unpackage/dist/dev/app-plus/static/tabbar/study_normal.png
  44. BIN
      unpackage/dist/dev/app-plus/static/tabbar/study_selected.png
  45. 0 0
      unpackage/dist/dev/app-plus/uni-app-view.umd.js
  46. 0 0
      unpackage/dist/dev/cache/.app-plus/tsc/app-android/.tsbuildInfo
  47. 54 0
      utils/api.js
  48. 224 0
      utils/request.js

+ 34 - 0
config/index.js

@@ -0,0 +1,34 @@
+/**
+ * 全局配置文件
+ */
+
+// 开发环境
+const DEV_CONFIG = {
+  BASE_URL: 'https://www.w3cschool.cn',
+  APP_ID: 'python_harmony_dev',
+  APP_TYPE: 'development',
+  CHANNEL: 'dev_channel'
+};
+
+// 生产环境
+const PROD_CONFIG = {
+  BASE_URL: 'https://www.w3cschool.cn',
+  APP_ID: 'python_harmony_prod',
+  APP_TYPE: 'production',
+  CHANNEL: 'official'
+};
+
+// 根据环境选择配置
+const ENV = process.env.NODE_ENV;
+const CONFIG = ENV === 'development' ? DEV_CONFIG : PROD_CONFIG;
+
+export default {
+  ...CONFIG,
+  // 超时时间
+  TIMEOUT: 30000,
+  // 版本号
+  VERSION: '1.0.0',
+  // 其他全局配置
+  TOKEN_KEY: 'auth_token',
+  USER_INFO_KEY: 'userInfo'
+};

+ 144 - 0
examples/request-example.js

@@ -0,0 +1,144 @@
+/**
+ * 请求方法使用示例
+ */
+import { userApi, contentApi, promiseApi } from '@/utils/api';
+
+// 示例1:使用回调方式调用登录接口
+export function loginExample() {
+  // 准备登录数据
+  const loginData = {
+    username: 'test',
+    password: '123456'
+  };
+  
+  // 调用登录接口
+  userApi.login(loginData, (res, err) => {
+    if (err) {
+      console.error('登录失败', err);
+      uni.showToast({
+        title: '登录失败',
+        icon: 'none'
+      });
+      return;
+    }
+    
+    if (res.data && res.data.code === 0) {
+      console.log('登录成功', res.data);
+      // 保存用户信息
+      uni.setStorageSync('userInfo', JSON.stringify(res.data.data));
+      uni.showToast({
+        title: '登录成功',
+        icon: 'success'
+      });
+    } else {
+      console.error('登录失败', res.data.msg);
+      uni.showToast({
+        title: res.data.msg || '登录失败',
+        icon: 'none'
+      });
+    }
+  });
+}
+
+// 示例2:使用Promise方式调用登录接口
+export async function loginPromiseExample() {
+  try {
+    // 准备登录数据
+    const loginData = {
+      username: 'test',
+      password: '123456'
+    };
+    
+    // 调用登录接口
+    const res = await promiseApi.login(loginData);
+    
+    if (res.data && res.data.code === 0) {
+      console.log('登录成功', res.data);
+      // 保存用户信息
+      uni.setStorageSync('userInfo', JSON.stringify(res.data.data));
+      uni.showToast({
+        title: '登录成功',
+        icon: 'success'
+      });
+      return res.data.data;
+    } else {
+      console.error('登录失败', res.data.msg);
+      uni.showToast({
+        title: res.data.msg || '登录失败',
+        icon: 'none'
+      });
+      return null;
+    }
+  } catch (error) {
+    console.error('登录请求异常', error);
+    uni.showToast({
+      title: '网络异常,请稍后重试',
+      icon: 'none'
+    });
+    return null;
+  }
+}
+
+// 示例3:在页面中使用
+// 在Vue组件中使用示例
+/*
+export default {
+  data() {
+    return {
+      username: '',
+      password: ''
+    };
+  },
+  methods: {
+    // 回调方式登录
+    handleLogin() {
+      const loginData = {
+        username: this.username,
+        password: this.password
+      };
+      
+      userApi.login(loginData, (res, err) => {
+        // 处理登录结果
+        if (err) {
+          this.$toast('登录失败');
+          return;
+        }
+        
+        if (res.data && res.data.code === 0) {
+          this.$toast('登录成功');
+          // 跳转到首页
+          uni.switchTab({
+            url: '/pages/index/index'
+          });
+        } else {
+          this.$toast(res.data.msg || '登录失败');
+        }
+      });
+    },
+    
+    // Promise方式登录
+    async handleLoginPromise() {
+      try {
+        const loginData = {
+          username: this.username,
+          password: this.password
+        };
+        
+        const res = await promiseApi.login(loginData);
+        
+        if (res.data && res.data.code === 0) {
+          this.$toast('登录成功');
+          // 跳转到首页
+          uni.switchTab({
+            url: '/pages/index/index'
+          });
+        } else {
+          this.$toast(res.data.msg || '登录失败');
+        }
+      } catch (error) {
+        this.$toast('网络异常,请稍后重试');
+      }
+    }
+  }
+}
+*/

+ 6 - 1
jsconfig.json

@@ -8,6 +8,8 @@
     "allowJs": true,
     "noEmit": true,
     "strict": false,
+    "esModuleInterop": true,
+    "skipLibCheck": true,
     "paths": {
       "@/*": ["./src/*"],
       "@/components/*": ["./src/components/*"],
@@ -16,10 +18,13 @@
     },
     "types": [
       "node"
+    ],
+    "typeRoots": [
+      "./node_modules/@types",
+      "./types"
     ]
   },
   "vueCompilerOptions": {
-    "experimentalRuntimeMode": "runtime-uni-app",
     "globalTypesPath": "./types/global.d.ts"
   },
   "include": [

+ 128 - 0
node_modules/.vue-global-types/vue_3.5_0.d.ts

@@ -0,0 +1,128 @@
+// @ts-nocheck
+export {};
+
+; declare global {
+	const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any };
+	const __VLS_unref: typeof import('vue').unref;
+	const __VLS_placeholder: any;
+
+	type __VLS_NativeElements = __VLS_SpreadMerge<SVGElementTagNameMap, HTMLElementTagNameMap>;
+	type __VLS_IntrinsicElements = import('vue/jsx-runtime').JSX.IntrinsicElements;
+	type __VLS_Element = import('vue/jsx-runtime').JSX.Element;
+	type __VLS_GlobalComponents = import('vue').GlobalComponents;
+	type __VLS_GlobalDirectives = import('vue').GlobalDirectives;
+	type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
+	type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
+	type __VLS_SpreadMerge<A, B> = Omit<A, keyof B> & B;
+	type __VLS_WithComponent<N0 extends string, LocalComponents, Self, N1 extends string, N2 extends string, N3 extends string> =
+		N1 extends keyof LocalComponents ? N1 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N1] } :
+		N2 extends keyof LocalComponents ? N2 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N2] } :
+		N3 extends keyof LocalComponents ? N3 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N3] } :
+		Self extends object ? { [K in N0]: Self } :
+		N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } :
+		N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } :
+		N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } :
+		{};
+	type __VLS_FunctionalComponentCtx<T, K> = __VLS_PickNotAny<'__ctx' extends keyof __VLS_PickNotAny<K, {}>
+		? K extends { __ctx?: infer Ctx } ? NonNullable<Ctx> : never : any
+		, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
+	>;
+	type __VLS_FunctionalComponentProps<T, K> = '__ctx' extends keyof __VLS_PickNotAny<K, {}>
+		? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
+		: T extends (props: infer P, ...args: any) => any ? P
+		: {};
+	type __VLS_FunctionalComponent<T> = (props: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>, ctx?: any) => __VLS_Element & {
+		__ctx?: {
+			attrs?: any;
+			slots?: T extends { $slots: infer Slots } ? Slots : Record<string, any>;
+			emit?: T extends { $emit: infer Emit } ? Emit : {};
+			props?: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>;
+			expose?: (exposed: T) => void;
+		};
+	};
+	type __VLS_IsFunction<T, K> = K extends keyof T
+		? __VLS_IsAny<T[K]> extends false
+		? unknown extends T[K]
+		? false
+		: true
+		: false
+		: false;
+	type __VLS_NormalizeComponentEvent<
+		Props,
+		Emits,
+		onEvent extends keyof Props,
+		Event extends keyof Emits,
+		CamelizedEvent extends keyof Emits,
+	> = __VLS_IsFunction<Props, onEvent> extends true
+		? Props
+		: __VLS_IsFunction<Emits, Event> extends true
+			? { [K in onEvent]?: Emits[Event] }
+			: __VLS_IsFunction<Emits, CamelizedEvent> extends true
+				? { [K in onEvent]?: Emits[CamelizedEvent] }
+				: Props;
+	// fix https://github.com/vuejs/language-tools/issues/926
+	type __VLS_UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;
+	type __VLS_OverloadUnionInner<T, U = unknown> = U & T extends (...args: infer A) => infer R
+		? U extends T
+		? never
+		: __VLS_OverloadUnionInner<T, Pick<T, keyof T> & U & ((...args: A) => R)> | ((...args: A) => R)
+		: never;
+	type __VLS_OverloadUnion<T> = Exclude<
+		__VLS_OverloadUnionInner<(() => never) & T>,
+		T extends () => never ? never : () => never
+	>;
+	type __VLS_ConstructorOverloads<T> = __VLS_OverloadUnion<T> extends infer F
+		? F extends (event: infer E, ...args: infer A) => any
+		? { [K in E & string]: (...args: A) => void; }
+		: never
+		: never;
+	type __VLS_NormalizeEmits<T> = __VLS_PrettifyGlobal<
+		__VLS_UnionToIntersection<
+			__VLS_ConstructorOverloads<T> & {
+				[K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never
+			}
+		>
+	>;
+	type __VLS_ResolveEmits<
+		Comp,
+		Emits,
+		TypeEmits = {},
+		NormalizedEmits = __VLS_NormalizeEmits<Emits> extends infer E ? string extends keyof E ? {} : E : never,
+	> = __VLS_SpreadMerge<NormalizedEmits, TypeEmits>;
+	type __VLS_ResolveDirectives<T> = {
+		[K in Exclude<keyof T, keyof __VLS_GlobalDirectives> & string as `v${Capitalize<K>}`]: T[K];
+	};
+	type __VLS_PrettifyGlobal<T> = { [K in keyof T as K]: T[K]; } & {};
+	type __VLS_UseTemplateRef<T> = Readonly<import('vue').ShallowRef<T | null>>;
+
+	function __VLS_getVForSourceType<T extends number | string | any[] | Iterable<any>>(source: T): [
+		item: T extends number ? number
+			: T extends string ? string
+			: T extends any[] ? T[number]
+			: T extends Iterable<infer T1> ? T1
+			: any,
+		index: number,
+	][];
+	function __VLS_getVForSourceType<T>(source: T): [
+		item: T[keyof T],
+		key: keyof T,
+		index: number,
+	][];
+	function __VLS_getSlotParameters<S, D extends S>(slot: S, decl?: D):
+		D extends (...args: infer P) => any ? P : any[];
+	function __VLS_asFunctionalDirective<T>(dir: T): T extends import('vue').ObjectDirective
+		? NonNullable<T['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>
+		: T extends (...args: any) => any
+			? T
+			: (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
+	function __VLS_makeOptional<T>(t: T): { [K in keyof T]?: T[K] };
+	function __VLS_asFunctionalComponent<T, K = T extends new (...args: any) => any ? InstanceType<T> : unknown>(t: T, instance?: K):
+		T extends new (...args: any) => any ? __VLS_FunctionalComponent<K>
+		: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
+		: T extends (...args: any) => any ? T
+		: __VLS_FunctionalComponent<{}>;
+	function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
+	function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (attrs: T & Record<string, unknown>) => void;
+	function __VLS_asFunctionalSlot<S>(slot: S): S extends () => infer R ? (props: {}) => R : NonNullable<S>;
+	function __VLS_tryAsConstant<const T>(t: T): T;
+}

+ 178 - 67
pages/discover/discover.vue

@@ -1,27 +1,26 @@
 <template>
   <view class="discover-container">
+    <!-- 自定义状态栏 -->
+    <view class="status-bar"></view>
+    
     <!-- 搜索框 -->
     <view class="search-section">
       <view class="search-box">
-        <uni-icons type="search" size="18" color="#999"></uni-icons>
+        <text class="search-icon">🔍</text>
         <input class="search-input" placeholder="python · html · java..." />
       </view>
     </view>
 
     <!-- 轮播图 -->
-    <view class="banner-section">
+    <view class="banner-section" v-if="swipeGroup.length > 0">
       <swiper class="banner-swiper" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000">
-        <swiper-item v-for="(item, index) in bannerList" :key="index">
-          <view class="banner-item" :style="{ backgroundColor: item.bgColor }">
-            <view class="banner-content">
-              <view class="banner-text">
-                <text class="banner-title">{{ item.title }}</text>
-                <text class="banner-subtitle">{{ item.subtitle }}</text>
-                <view class="banner-btn">{{ item.btnText }}</view>
-              </view>
-              <image class="banner-image" :src="item.image" mode="aspectFit"></image>
-            </view>
-          </view>
+        <swiper-item v-for="(item, index) in swipeGroup" :key="index">
+          <image 
+            class="swipe-image" 
+            :src="item.cover" 
+            mode="aspectFill"
+            @click="navigateToSwipeLink(index)"
+          />
         </swiper-item>
       </swiper>
     </view>
@@ -64,7 +63,7 @@
         <text class="section-title">大家都在学</text>
         <view class="more-btn">
           <text>更多课程</text>
-          <uni-icons type="right" size="14" color="#999"></uni-icons>
+          <text class="arrow-icon">›</text>
         </view>
       </view>
 
@@ -87,17 +86,11 @@
 
 <script setup>
 import { ref, onMounted } from 'vue'
+import requestModule from '@/utils/request'
+import config from '@/config/index.js'
 
 // 轮播图数据
-const bannerList = ref([
-  {
-    title: 'Python编程班',
-    subtitle: '产品体验有奖用户调研',
-    btnText: '立即参与 >>',
-    bgColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
-    image: '/static/banner/python-banner.png'
-  }
-])
+const swipeGroup = ref([])
 
 // 功能模块数据
 const functionList = ref([
@@ -154,6 +147,138 @@ const courseList = ref([
   }
 ])
 
+// 获取轮播图
+const getSwipe = () => {
+  try {
+    // 先尝试从本地存储获取缓存数据
+    const cachedSwipe = uni.getStorageSync('swipeGroup')
+    if (cachedSwipe) {
+      swipeGroup.value = JSON.parse(cachedSwipe)
+    }
+  } catch (e) {
+    console.error('获取缓存轮播图失败:', e)
+  }
+
+  // 检查必要的依赖
+  if (!requestModule || !config) {
+    console.error('request或config模块未正确导入')
+    return
+  }
+
+  try {
+    // 安全获取系统信息
+    const systemInfo = uni.getSystemInfoSync()
+    
+    // 从服务器获取最新数据 - 使用正确的API调用方式
+    requestModule.request('/api/appapi/getSlide', {}, {
+      apptype: config.APP_TYPE || 'production',
+      systemType: systemInfo.platform || 'unknown'
+    }, (ret, err) => {
+      try {
+        if (ret && ret.statusCode < 300 && ret.data) {
+          swipeGroup.value = []
+          // console.log('轮播图原始数据:', JSON.stringify(ret.data))
+          // console.log('ret.data类型:', typeof ret.data, 'isArray:', Array.isArray(ret.data))
+          
+          // 确保ret.data是数组
+          let dataArray = ret.data
+          if (typeof ret.data === 'string') {
+            try {
+              dataArray = JSON.parse(ret.data)
+            } catch (e) {
+              console.error('解析轮播图数据失败:', e)
+              dataArray = []
+            }
+          }
+          
+          // 如果不是数组,尝试从其他可能的字段获取
+          if (!Array.isArray(dataArray)) {
+            if (dataArray && dataArray.list && Array.isArray(dataArray.list)) {
+              dataArray = dataArray.list
+            } else if (dataArray && dataArray.data && Array.isArray(dataArray.data)) {
+              dataArray = dataArray.data
+            } else {
+              console.error('轮播图数据格式不正确:', dataArray)
+              dataArray = []
+            }
+          }
+          
+          // 处理数组数据
+          if (Array.isArray(dataArray) && dataArray.length > 0) {
+            dataArray.forEach((item) => {
+              if (item && item.cover) {
+                const swipeItem = {
+                  cover: config.BASE_URL + '/' + item.cover,
+                  link: item.link || '',
+                  title: item.title || ''
+                }
+                swipeGroup.value.push(swipeItem)
+              }
+            })
+          }
+          
+          // 缓存到本地存储
+          try {
+            uni.setStorageSync('swipeGroup', JSON.stringify(swipeGroup.value))
+          } catch (e) {
+            console.error('缓存轮播图失败:', e)
+          }
+          
+          // console.log('处理后的轮播图数据:', JSON.stringify(swipeGroup.value))
+        } else if (swipeGroup.value.length === 0) {
+          // 如果没有数据且缓存也为空,使用默认图片
+          swipeGroup.value = [{
+            cover: '/static/banner/default-slide.png',
+            link: '',
+            title: '默认轮播图'
+          }]
+        }
+        
+        if (err) {
+          console.error('获取轮播图失败:', err)
+        }
+      } catch (error) {
+        console.error('处理轮播图数据时出错:', error)
+      }
+    })
+  } catch (error) {
+    console.error('获取系统信息或发起请求时出错:', error)
+  }
+}
+
+// 轮播图点击跳转
+const navigateToSwipeLink = (index) => {
+  const item = swipeGroup.value[index]
+  if (!item || !item.link) return
+  
+  console.log('点击轮播图:', item.title, item.link)
+  
+  // 处理不同类型的链接
+  if (item.link.startsWith('http')) {
+    // 外部链接,使用webview打开
+    uni.navigateTo({
+      url: `/pages/webview/webview?url=${encodeURIComponent(item.link)}&title=${encodeURIComponent(item.title || '详情')}`
+    })
+  } else if (item.link.includes('||')) {
+    // 内部页面跳转格式: winname||path||params
+    const linkParts = item.link.split('||')
+    if (linkParts.length >= 2) {
+      const pagePath = linkParts[1]
+      const params = linkParts[2] || ''
+      
+      // 构建跳转路径
+      let navigateUrl = `/pages/${pagePath}`
+      if (params) {
+        navigateUrl += `?params=${encodeURIComponent(params)}`
+      }
+      
+      uni.navigateTo({
+        url: navigateUrl
+      })
+    }
+  }
+}
+
 // 方法
 const handleFunctionClick = (item) => {
   console.log('点击功能:', item.name)
@@ -166,7 +291,13 @@ const handleCourseClick = (course) => {
 }
 
 onMounted(() => {
-  console.log('发现页面加载完成')
+  try {
+    console.log('发现页面加载完成')
+    // 获取轮播图数据
+    getSwipe()
+  } catch (error) {
+    console.error('页面初始化时出错:', error)
+  }
 })
 </script>
 
@@ -176,6 +307,16 @@ onMounted(() => {
   min-height: 100vh;
 }
 
+// 自定义状态栏
+.status-bar {
+  background-color: #fff;
+  /* 使用CSS环境变量适配安全区域 */
+  height: env(safe-area-inset-top);
+  height: constant(safe-area-inset-top); /* 兼容iOS < 11.2 */
+  /* 设置最小高度,确保在没有刘海屏的设备上也有合适的高度 */
+  min-height: 20px;
+}
+
 // 搜索框样式
 .search-section {
   background-color: #fff;
@@ -188,6 +329,11 @@ onMounted(() => {
     border-radius: 50rpx;
     padding: 20rpx 30rpx;
     
+    .search-icon {
+      font-size: 32rpx;
+      color: #999;
+    }
+    
     .search-input {
       flex: 1;
       margin-left: 20rpx;
@@ -207,51 +353,10 @@ onMounted(() => {
     overflow: hidden;
   }
   
-  .banner-item {
+  .swipe-image {
+    width: 100%;
     height: 100%;
     border-radius: 20rpx;
-    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-  }
-  
-  .banner-content {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    height: 100%;
-    padding: 40rpx;
-  }
-  
-  .banner-text {
-    flex: 1;
-    
-    .banner-title {
-      display: block;
-      color: #fff;
-      font-size: 32rpx;
-      font-weight: bold;
-      margin-bottom: 10rpx;
-    }
-    
-    .banner-subtitle {
-      display: block;
-      color: #fff;
-      font-size: 28rpx;
-      margin-bottom: 30rpx;
-    }
-    
-    .banner-btn {
-      background-color: #FF6B35;
-      color: #fff;
-      padding: 12rpx 24rpx;
-      border-radius: 30rpx;
-      font-size: 24rpx;
-      display: inline-block;
-    }
-  }
-  
-  .banner-image {
-    width: 120rpx;
-    height: 120rpx;
   }
 }
 
@@ -373,6 +478,12 @@ onMounted(() => {
       align-items: center;
       color: #999;
       font-size: 24rpx;
+      
+      .arrow-icon {
+        font-size: 28rpx;
+        margin-left: 8rpx;
+        color: #999;
+      }
     }
   }
   

+ 125 - 14
types/global.d.ts

@@ -1,17 +1,128 @@
-declare global {
-  interface Uni {
-    $u: any;
-  }
-}
+// @ts-nocheck
+export {};
 
-declare module '*.vue' {
-  import { DefineComponent } from 'vue'
-  const component: DefineComponent<{}, {}, any>
-  export default component
-}
+; declare global {
+	const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any };
+	const __VLS_unref: typeof import('vue').unref;
+	const __VLS_placeholder: any;
 
-declare module '@dcloudio/uni-app' {
-  export * from '@dcloudio/uni-app'
-}
+	type __VLS_NativeElements = __VLS_SpreadMerge<SVGElementTagNameMap, HTMLElementTagNameMap>;
+	type __VLS_IntrinsicElements = import('vue/jsx-runtime').JSX.IntrinsicElements;
+	type __VLS_Element = import('vue/jsx-runtime').JSX.Element;
+	type __VLS_GlobalComponents = import('vue').GlobalComponents;
+	type __VLS_GlobalDirectives = import('vue').GlobalDirectives;
+	type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
+	type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
+	type __VLS_SpreadMerge<A, B> = Omit<A, keyof B> & B;
+	type __VLS_WithComponent<N0 extends string, LocalComponents, Self, N1 extends string, N2 extends string, N3 extends string> =
+		N1 extends keyof LocalComponents ? N1 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N1] } :
+		N2 extends keyof LocalComponents ? N2 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N2] } :
+		N3 extends keyof LocalComponents ? N3 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N3] } :
+		Self extends object ? { [K in N0]: Self } :
+		N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } :
+		N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } :
+		N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } :
+		{};
+	type __VLS_FunctionalComponentCtx<T, K> = __VLS_PickNotAny<'__ctx' extends keyof __VLS_PickNotAny<K, {}>
+		? K extends { __ctx?: infer Ctx } ? NonNullable<Ctx> : never : any
+		, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
+	>;
+	type __VLS_FunctionalComponentProps<T, K> = '__ctx' extends keyof __VLS_PickNotAny<K, {}>
+		? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
+		: T extends (props: infer P, ...args: any) => any ? P
+		: {};
+	type __VLS_FunctionalComponent<T> = (props: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>, ctx?: any) => __VLS_Element & {
+		__ctx?: {
+			attrs?: any;
+			slots?: T extends { $slots: infer Slots } ? Slots : Record<string, any>;
+			emit?: T extends { $emit: infer Emit } ? Emit : {};
+			props?: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>;
+			expose?: (exposed: T) => void;
+		};
+	};
+	type __VLS_IsFunction<T, K> = K extends keyof T
+		? __VLS_IsAny<T[K]> extends false
+		? unknown extends T[K]
+		? false
+		: true
+		: false
+		: false;
+	type __VLS_NormalizeComponentEvent<
+		Props,
+		Emits,
+		onEvent extends keyof Props,
+		Event extends keyof Emits,
+		CamelizedEvent extends keyof Emits,
+	> = __VLS_IsFunction<Props, onEvent> extends true
+		? Props
+		: __VLS_IsFunction<Emits, Event> extends true
+			? { [K in onEvent]?: Emits[Event] }
+			: __VLS_IsFunction<Emits, CamelizedEvent> extends true
+				? { [K in onEvent]?: Emits[CamelizedEvent] }
+				: Props;
+	// fix https://github.com/vuejs/language-tools/issues/926
+	type __VLS_UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;
+	type __VLS_OverloadUnionInner<T, U = unknown> = U & T extends (...args: infer A) => infer R
+		? U extends T
+		? never
+		: __VLS_OverloadUnionInner<T, Pick<T, keyof T> & U & ((...args: A) => R)> | ((...args: A) => R)
+		: never;
+	type __VLS_OverloadUnion<T> = Exclude<
+		__VLS_OverloadUnionInner<(() => never) & T>,
+		T extends () => never ? never : () => never
+	>;
+	type __VLS_ConstructorOverloads<T> = __VLS_OverloadUnion<T> extends infer F
+		? F extends (event: infer E, ...args: infer A) => any
+		? { [K in E & string]: (...args: A) => void; }
+		: never
+		: never;
+	type __VLS_NormalizeEmits<T> = __VLS_PrettifyGlobal<
+		__VLS_UnionToIntersection<
+			__VLS_ConstructorOverloads<T> & {
+				[K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never
+			}
+		>
+	>;
+	type __VLS_ResolveEmits<
+		Comp,
+		Emits,
+		TypeEmits = {},
+		NormalizedEmits = __VLS_NormalizeEmits<Emits> extends infer E ? string extends keyof E ? {} : E : never,
+	> = __VLS_SpreadMerge<NormalizedEmits, TypeEmits>;
+	type __VLS_ResolveDirectives<T> = {
+		[K in Exclude<keyof T, keyof __VLS_GlobalDirectives> & string as `v${Capitalize<K>}`]: T[K];
+	};
+	type __VLS_PrettifyGlobal<T> = { [K in keyof T as K]: T[K]; } & {};
+	type __VLS_UseTemplateRef<T> = Readonly<import('vue').ShallowRef<T | null>>;
 
-export {}
+	function __VLS_getVForSourceType<T extends number | string | any[] | Iterable<any>>(source: T): [
+		item: T extends number ? number
+			: T extends string ? string
+			: T extends any[] ? T[number]
+			: T extends Iterable<infer T1> ? T1
+			: any,
+		index: number,
+	][];
+	function __VLS_getVForSourceType<T>(source: T): [
+		item: T[keyof T],
+		key: keyof T,
+		index: number,
+	][];
+	function __VLS_getSlotParameters<S, D extends S>(slot: S, decl?: D):
+		D extends (...args: infer P) => any ? P : any[];
+	function __VLS_asFunctionalDirective<T>(dir: T): T extends import('vue').ObjectDirective
+		? NonNullable<T['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>
+		: T extends (...args: any) => any
+			? T
+			: (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
+	function __VLS_makeOptional<T>(t: T): { [K in keyof T]?: T[K] };
+	function __VLS_asFunctionalComponent<T, K = T extends new (...args: any) => any ? InstanceType<T> : unknown>(t: T, instance?: K):
+		T extends new (...args: any) => any ? __VLS_FunctionalComponent<K>
+		: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
+		: T extends (...args: any) => any ? T
+		: __VLS_FunctionalComponent<{}>;
+	function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
+	function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (attrs: T & Record<string, unknown>) => void;
+	function __VLS_asFunctionalSlot<S>(slot: S): S extends () => infer R ? (props: {}) => R : NonNullable<S>;
+	function __VLS_tryAsConstant<const T>(t: T): T;
+}

+ 3 - 3
unpackage/dist/cache/.vite/deps/_metadata.json

@@ -1,8 +1,8 @@
 {
-  "hash": "b609455a",
+  "hash": "c1b261c9",
   "configHash": "3a1a3183",
-  "lockfileHash": "e3b0c442",
-  "browserHash": "167b7ad0",
+  "lockfileHash": "e84d4a85",
+  "browserHash": "99625017",
   "optimized": {},
   "chunks": {}
 }

+ 11 - 0
unpackage/dist/dev/.nvue/app.css.js

@@ -0,0 +1,11 @@
+var __getOwnPropNames = Object.getOwnPropertyNames;
+var __commonJS = (cb, mod) => function __require() {
+  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
+};
+var require_app_css = __commonJS({
+  "app.css.js"(exports) {
+    const _style_0 = {};
+    exports.styles = [_style_0];
+  }
+});
+export default require_app_css();

+ 2 - 0
unpackage/dist/dev/.nvue/app.js

@@ -0,0 +1,2 @@
+Promise.resolve("./app.css.js").then(() => {
+});

Datei-Diff unterdrückt, da er zu groß ist
+ 15 - 0
unpackage/dist/dev/app-plus/__uniappautomator.js


Datei-Diff unterdrückt, da er zu groß ist
+ 31 - 0
unpackage/dist/dev/app-plus/__uniappchooselocation.js


BIN
unpackage/dist/dev/app-plus/__uniapperror.png


Datei-Diff unterdrückt, da er zu groß ist
+ 31 - 0
unpackage/dist/dev/app-plus/__uniappopenlocation.js


Datei-Diff unterdrückt, da er zu groß ist
+ 31 - 0
unpackage/dist/dev/app-plus/__uniapppicker.js


Datei-Diff unterdrückt, da er zu groß ist
+ 6 - 0
unpackage/dist/dev/app-plus/__uniappquill.js


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
unpackage/dist/dev/app-plus/__uniappquillimageresize.js


Datei-Diff unterdrückt, da er zu groß ist
+ 31 - 0
unpackage/dist/dev/app-plus/__uniappscan.js


BIN
unpackage/dist/dev/app-plus/__uniappsuccess.png


+ 24 - 0
unpackage/dist/dev/app-plus/__uniappview.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>View</title>
+    <link rel="icon" href="data:,">
+    <link rel="stylesheet" href="app.css" />
+    <script>var __uniConfig = {"globalStyle":{},"darkmode":false}</script>
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script src="uni-app-view.umd.js"></script>
+    
+    
+    
+  </body>
+</html>

+ 11 - 0
unpackage/dist/dev/app-plus/app-config-service.js

@@ -0,0 +1,11 @@
+
+  ;(function(){
+  let u=void 0,isReady=false,onReadyCallbacks=[],isServiceReady=false,onServiceReadyCallbacks=[];
+  const __uniConfig = {"pages":[],"globalStyle":{"backgroundColor":"#f5f5f5","navigationBar":{"backgroundColor":"#ffffff","titleText":"Python学习","type":"default","titleColor":"#000000"},"isNVue":false},"nvue":{"compiler":"uni-app","styleCompiler":"uni-app","flex-direction":"column"},"renderer":"auto","appname":"python_Harmony","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":true},"compilerVersion":"4.75","entryPagePath":"pages/discover/discover","entryPageQuery":"","realEntryPagePath":"","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000},"tabBar":{"position":"bottom","color":"#999999","selectedColor":"#667eea","borderStyle":"white","blurEffect":"none","fontSize":"10px","iconWidth":"24px","spacing":"3px","height":"50px","list":[{"pagePath":"pages/discover/discover","iconPath":"/static/tabbar/discover_normal.png","selectedIconPath":"/static/tabbar/discover_selected.png","text":"发现"},{"pagePath":"pages/codebase/codebase","iconPath":"/static/tabbar/codebase_normal.png","selectedIconPath":"/static/tabbar/codebase_selected.png","text":"代码库"},{"pagePath":"pages/editor/editor","iconPath":"/static/tabbar/editor_normal.png","selectedIconPath":"/static/tabbar/editor_selected.png","text":"编辑器"},{"pagePath":"pages/study/study","iconPath":"/static/tabbar/study_normal.png","selectedIconPath":"/static/tabbar/study_selected.png","text":"学习"},{"pagePath":"pages/profile/profile","iconPath":"/static/tabbar/profile_normal.png","selectedIconPath":"/static/tabbar/profile_selected.png","text":"账号"}],"backgroundColor":"#ffffff","selectedIndex":0,"shown":true},"locales":{},"darkmode":false,"themeConfig":{}};
+  const __uniRoutes = [{"path":"pages/discover/discover","meta":{"isQuit":true,"isEntry":true,"isTabBar":true,"tabBarIndex":0,"navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/codebase/codebase","meta":{"isQuit":true,"isTabBar":true,"tabBarIndex":1,"navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/editor/editor","meta":{"isQuit":true,"isTabBar":true,"tabBarIndex":2,"navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/study/study","meta":{"isQuit":true,"isTabBar":true,"tabBarIndex":3,"navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/profile/profile","meta":{"isQuit":true,"isTabBar":true,"tabBarIndex":4,"navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute));
+  __uniConfig.styles=[];//styles
+  __uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
+  __uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
+  service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:16})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,global:u,window:u,document:u,frames:u,self:u,location:u,navigator:u,localStorage:u,history:u,Caches:u,screen:u,alert:u,confirm:u,prompt:u,fetch:u,XMLHttpRequest:u,WebSocket:u,webkit:u,print:u}}}}); 
+  })();
+  

+ 1 - 0
unpackage/dist/dev/app-plus/app-config.js

@@ -0,0 +1 @@
+(function(){})();

+ 710 - 0
unpackage/dist/dev/app-plus/app-service.js

@@ -0,0 +1,710 @@
+if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
+  Promise.prototype.finally = function(callback) {
+    const promise = this.constructor;
+    return this.then(
+      (value) => promise.resolve(callback()).then(() => value),
+      (reason) => promise.resolve(callback()).then(() => {
+        throw reason;
+      })
+    );
+  };
+}
+;
+if (typeof uni !== "undefined" && uni && uni.requireGlobal) {
+  const global = uni.requireGlobal();
+  ArrayBuffer = global.ArrayBuffer;
+  Int8Array = global.Int8Array;
+  Uint8Array = global.Uint8Array;
+  Uint8ClampedArray = global.Uint8ClampedArray;
+  Int16Array = global.Int16Array;
+  Uint16Array = global.Uint16Array;
+  Int32Array = global.Int32Array;
+  Uint32Array = global.Uint32Array;
+  Float32Array = global.Float32Array;
+  Float64Array = global.Float64Array;
+  BigInt64Array = global.BigInt64Array;
+  BigUint64Array = global.BigUint64Array;
+}
+;
+if (uni.restoreGlobal) {
+  uni.restoreGlobal(Vue, weex, plus, setTimeout, clearTimeout, setInterval, clearInterval);
+}
+(function(vue) {
+  "use strict";
+  function formatAppLog(type, filename, ...args) {
+    if (uni.__log__) {
+      uni.__log__(type, filename, ...args);
+    } else {
+      console[type].apply(console, [...args, filename]);
+    }
+  }
+  const DEV_CONFIG = {
+    BASE_URL: "https://www.w3cschool.cn",
+    APP_ID: "python_harmony_dev",
+    APP_TYPE: "development",
+    CHANNEL: "dev_channel"
+  };
+  const CONFIG$1 = DEV_CONFIG;
+  const config = {
+    ...CONFIG$1,
+    // 超时时间
+    TIMEOUT: 3e4,
+    // 版本号
+    VERSION: "1.0.0",
+    // 其他全局配置
+    TOKEN_KEY: "auth_token",
+    USER_INFO_KEY: "userInfo"
+  };
+  const CONFIG = config;
+  const getUserInfo = function(callback) {
+    try {
+      const userInfoStr = uni.getStorageSync("userInfo");
+      if (userInfoStr) {
+        const userInfo = JSON.parse(userInfoStr);
+        callback && callback({ status: true, data: userInfo });
+      } else {
+        callback && callback({ status: false });
+      }
+    } catch (e) {
+      formatAppLog("error", "at utils/request.js:27", "获取用户信息失败", e);
+      callback && callback({ status: false, error: e });
+    }
+  };
+  const checkValue = function(value) {
+    return value !== void 0 && value !== null ? value : "";
+  };
+  const requestInterceptor = function(options) {
+    return options;
+  };
+  const responseInterceptor = function(response, options) {
+    return response;
+  };
+  const request = function(url, headParam = {}, bodyParam = {}, callback) {
+    getUserInfo(function(ret) {
+      let userInfo = {};
+      if (ret && ret.status === true && ret.data) {
+        userInfo = ret.data;
+      }
+      if (!(headParam instanceof Object)) {
+        headParam = {};
+      }
+      if (!(bodyParam instanceof Object)) {
+        bodyParam = {};
+      }
+      let systemInfo = {};
+      try {
+        systemInfo = uni.getSystemInfoSync();
+      } catch (e) {
+        formatAppLog("error", "at utils/request.js:90", "获取系统信息失败:", e);
+        systemInfo = { platform: "unknown", deviceId: "" };
+      }
+      let appVersion = "";
+      try {
+        if (typeof plus !== "undefined" && plus.runtime) {
+          appVersion = plus.runtime.version;
+        } else {
+          appVersion = CONFIG.VERSION || "1.0.0";
+        }
+      } catch (e) {
+        formatAppLog("error", "at utils/request.js:105", "获取应用版本失败:", e);
+        appVersion = CONFIG.VERSION || "1.0.0";
+      }
+      bodyParam.deviceId = systemInfo.deviceId || "";
+      bodyParam.platform = systemInfo.platform || "unknown";
+      bodyParam.appVersion = appVersion;
+      bodyParam.systemType = systemInfo.platform || "unknown";
+      bodyParam.channel = CONFIG.CHANNEL || "default";
+      bodyParam.apikey = checkValue(userInfo.apikey);
+      bodyParam.apiauth = checkValue(userInfo.apiauth);
+      const requestOptions = {
+        url: /^(http|https):\/\//.test(url) ? url : CONFIG.BASE_URL + url,
+        method: headParam.method || "POST",
+        header: headParam.header || {
+          "Content-Type": "application/json;charset=UTF-8"
+        },
+        data: bodyParam,
+        timeout: headParam.timeout || 3e4
+      };
+      const finalOptions = requestInterceptor(requestOptions);
+      uni.request({
+        ...finalOptions,
+        success: (res) => {
+          const processedRes = responseInterceptor(res, finalOptions);
+          callback && callback(processedRes, null);
+        },
+        fail: (err) => {
+          formatAppLog("error", "at utils/request.js:143", "请求失败", err);
+          callback && callback(null, err);
+        }
+      });
+    });
+  };
+  const get = function(url, data = {}, options = {}, callback) {
+    const headParam = { ...options, method: "GET" };
+    request(url, headParam, data, callback);
+  };
+  const post = function(url, data = {}, options = {}, callback) {
+    const headParam = { ...options, method: "POST" };
+    request(url, headParam, data, callback);
+  };
+  const requestPromise = function(url, headParam = {}, bodyParam = {}) {
+    return new Promise((resolve, reject) => {
+      request(url, headParam, bodyParam, (res, err) => {
+        if (err) {
+          reject(err);
+        } else {
+          resolve(res);
+        }
+      });
+    });
+  };
+  const getPromise = function(url, data = {}, options = {}) {
+    return requestPromise(url, { ...options, method: "GET" }, data);
+  };
+  const postPromise = function(url, data = {}, options = {}) {
+    return requestPromise(url, { ...options, method: "POST" }, data);
+  };
+  const requestModule = {
+    request,
+    get,
+    post,
+    requestPromise,
+    getPromise,
+    postPromise,
+    getUserInfo,
+    checkValue
+  };
+  const _export_sfc = (sfc, props) => {
+    const target = sfc.__vccOpts || sfc;
+    for (const [key, val] of props) {
+      target[key] = val;
+    }
+    return target;
+  };
+  const _sfc_main$5 = {
+    __name: "discover",
+    setup(__props, { expose: __expose }) {
+      __expose();
+      const swipeGroup = vue.ref([]);
+      const functionList = vue.ref([
+        { name: "学练课", icon: "/static/icons/study.png", bgColor: "#FF6B6B", badge: "好课" },
+        { name: "视频课", icon: "/static/icons/video.png", bgColor: "#9B59B6" },
+        { name: "题库", icon: "/static/icons/question.png", bgColor: "#F39C12" },
+        { name: "教程", icon: "/static/icons/tutorial.png", bgColor: "#2ECC71" },
+        { name: "VIP会员", icon: "/static/icons/vip.png", bgColor: "#E74C3C", badge: "16周年" },
+        { name: "基础入门", icon: "/static/icons/basic.png", bgColor: "#3498DB" },
+        { name: "数据分析", icon: "/static/icons/data.png", bgColor: "#1ABC9C" },
+        { name: "人工智能", icon: "/static/icons/ai.png", bgColor: "#95A5A6" },
+        { name: "爬虫", icon: "/static/icons/spider.png", bgColor: "#34495E" },
+        { name: "办公自动化", icon: "/static/icons/office.png", bgColor: "#16A085" }
+      ]);
+      const courseCards = vue.ref([
+        {
+          title: "Python高薪就业课",
+          subtitle: "面向大学、职场就业提效",
+          bgColor: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
+          image: "/static/cards/job-card.png"
+        },
+        {
+          title: "Python少儿编程",
+          subtitle: "面向小学、初高中学习",
+          bgColor: "linear-gradient(135deg, #f093fb 0%, #f5576c 100%)",
+          image: "/static/cards/kids-card.png"
+        }
+      ]);
+      const courseList = vue.ref([
+        {
+          cover: "/static/courses/python-basic.png",
+          title: "Python3 入门课程",
+          students: "5037",
+          type: "学练课",
+          description: "爬虫+机器学习? Python无所不能..."
+        },
+        {
+          cover: "/static/courses/python-zero.png",
+          title: "零基础入门学Python",
+          students: "619",
+          type: "视频课",
+          description: "通过Python入门到实战全套教程..."
+        },
+        {
+          cover: "/static/courses/python-guide.png",
+          title: "Python3零基础入门先导课",
+          students: "5847",
+          type: "学练课",
+          description: "0基础的python入门先导课程..."
+        }
+      ]);
+      const getSwipe = () => {
+        try {
+          const cachedSwipe = uni.getStorageSync("swipeGroup");
+          if (cachedSwipe) {
+            swipeGroup.value = JSON.parse(cachedSwipe);
+          }
+        } catch (e) {
+          formatAppLog("error", "at pages/discover/discover.vue:159", "获取缓存轮播图失败:", e);
+        }
+        if (!requestModule || !config) {
+          formatAppLog("error", "at pages/discover/discover.vue:164", "request或config模块未正确导入");
+          return;
+        }
+        try {
+          const systemInfo = uni.getSystemInfoSync();
+          requestModule.request("/api/appapi/getSlide", {}, {
+            apptype: config.APP_TYPE || "production",
+            systemType: systemInfo.platform || "unknown"
+          }, (ret, err) => {
+            try {
+              if (ret && ret.statusCode < 300 && ret.data) {
+                swipeGroup.value = [];
+                let dataArray = ret.data;
+                if (typeof ret.data === "string") {
+                  try {
+                    dataArray = JSON.parse(ret.data);
+                  } catch (e) {
+                    formatAppLog("error", "at pages/discover/discover.vue:189", "解析轮播图数据失败:", e);
+                    dataArray = [];
+                  }
+                }
+                if (!Array.isArray(dataArray)) {
+                  if (dataArray && dataArray.list && Array.isArray(dataArray.list)) {
+                    dataArray = dataArray.list;
+                  } else if (dataArray && dataArray.data && Array.isArray(dataArray.data)) {
+                    dataArray = dataArray.data;
+                  } else {
+                    formatAppLog("error", "at pages/discover/discover.vue:201", "轮播图数据格式不正确:", dataArray);
+                    dataArray = [];
+                  }
+                }
+                if (Array.isArray(dataArray) && dataArray.length > 0) {
+                  dataArray.forEach((item) => {
+                    if (item && item.cover) {
+                      const swipeItem = {
+                        cover: config.BASE_URL + "/" + item.cover,
+                        link: item.link || "",
+                        title: item.title || ""
+                      };
+                      swipeGroup.value.push(swipeItem);
+                    }
+                  });
+                }
+                try {
+                  uni.setStorageSync("swipeGroup", JSON.stringify(swipeGroup.value));
+                } catch (e) {
+                  formatAppLog("error", "at pages/discover/discover.vue:224", "缓存轮播图失败:", e);
+                }
+              } else if (swipeGroup.value.length === 0) {
+                swipeGroup.value = [{
+                  cover: "/static/banner/default-slide.png",
+                  link: "",
+                  title: "默认轮播图"
+                }];
+              }
+              if (err) {
+                formatAppLog("error", "at pages/discover/discover.vue:238", "获取轮播图失败:", err);
+              }
+            } catch (error) {
+              formatAppLog("error", "at pages/discover/discover.vue:241", "处理轮播图数据时出错:", error);
+            }
+          });
+        } catch (error) {
+          formatAppLog("error", "at pages/discover/discover.vue:245", "获取系统信息或发起请求时出错:", error);
+        }
+      };
+      const navigateToSwipeLink = (index) => {
+        const item = swipeGroup.value[index];
+        if (!item || !item.link)
+          return;
+        formatAppLog("log", "at pages/discover/discover.vue:254", "点击轮播图:", item.title, item.link);
+        if (item.link.startsWith("http")) {
+          uni.navigateTo({
+            url: `/pages/webview/webview?url=${encodeURIComponent(item.link)}&title=${encodeURIComponent(item.title || "详情")}`
+          });
+        } else if (item.link.includes("||")) {
+          const linkParts = item.link.split("||");
+          if (linkParts.length >= 2) {
+            const pagePath = linkParts[1];
+            const params = linkParts[2] || "";
+            let navigateUrl = `/pages/${pagePath}`;
+            if (params) {
+              navigateUrl += `?params=${encodeURIComponent(params)}`;
+            }
+            uni.navigateTo({
+              url: navigateUrl
+            });
+          }
+        }
+      };
+      const handleFunctionClick = (item) => {
+        formatAppLog("log", "at pages/discover/discover.vue:284", "点击功能:", item.name);
+      };
+      const handleCourseClick = (course) => {
+        formatAppLog("log", "at pages/discover/discover.vue:289", "点击课程:", course.title);
+      };
+      vue.onMounted(() => {
+        try {
+          formatAppLog("log", "at pages/discover/discover.vue:295", "发现页面加载完成");
+          getSwipe();
+        } catch (error) {
+          formatAppLog("error", "at pages/discover/discover.vue:299", "页面初始化时出错:", error);
+        }
+      });
+      const __returned__ = { swipeGroup, functionList, courseCards, courseList, getSwipe, navigateToSwipeLink, handleFunctionClick, handleCourseClick, ref: vue.ref, onMounted: vue.onMounted, get requestModule() {
+        return requestModule;
+      }, get config() {
+        return config;
+      } };
+      Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
+      return __returned__;
+    }
+  };
+  function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
+    return vue.openBlock(), vue.createElementBlock("view", { class: "discover-container" }, [
+      vue.createCommentVNode(" 自定义状态栏 "),
+      vue.createElementVNode("view", { class: "status-bar" }),
+      vue.createCommentVNode(" 搜索框 "),
+      vue.createElementVNode("view", { class: "search-section" }, [
+        vue.createElementVNode("view", { class: "search-box" }, [
+          vue.createElementVNode("text", { class: "search-icon" }, "🔍"),
+          vue.createElementVNode("input", {
+            class: "search-input",
+            placeholder: "python · html · java..."
+          })
+        ])
+      ]),
+      vue.createCommentVNode(" 轮播图 "),
+      $setup.swipeGroup.length > 0 ? (vue.openBlock(), vue.createElementBlock("view", {
+        key: 0,
+        class: "banner-section"
+      }, [
+        vue.createElementVNode("swiper", {
+          class: "banner-swiper",
+          "indicator-dots": true,
+          autoplay: true,
+          interval: 3e3,
+          duration: 1e3
+        }, [
+          (vue.openBlock(true), vue.createElementBlock(
+            vue.Fragment,
+            null,
+            vue.renderList($setup.swipeGroup, (item, index) => {
+              return vue.openBlock(), vue.createElementBlock("swiper-item", { key: index }, [
+                vue.createElementVNode("image", {
+                  class: "swipe-image",
+                  src: item.cover,
+                  mode: "aspectFill",
+                  onClick: ($event) => $setup.navigateToSwipeLink(index)
+                }, null, 8, ["src", "onClick"])
+              ]);
+            }),
+            128
+            /* KEYED_FRAGMENT */
+          ))
+        ])
+      ])) : vue.createCommentVNode("v-if", true),
+      vue.createCommentVNode(" 功能模块 "),
+      vue.createElementVNode("view", { class: "function-section" }, [
+        vue.createElementVNode("view", { class: "function-row" }, [
+          (vue.openBlock(true), vue.createElementBlock(
+            vue.Fragment,
+            null,
+            vue.renderList($setup.functionList.slice(0, 5), (item, index) => {
+              return vue.openBlock(), vue.createElementBlock("view", {
+                class: "function-item",
+                key: index,
+                onClick: ($event) => $setup.handleFunctionClick(item)
+              }, [
+                vue.createElementVNode("view", { class: "function-icon" }, [
+                  vue.createElementVNode("image", {
+                    src: item.icon,
+                    mode: "aspectFit"
+                  }, null, 8, ["src"])
+                ]),
+                vue.createElementVNode(
+                  "text",
+                  { class: "function-text" },
+                  vue.toDisplayString(item.name),
+                  1
+                  /* TEXT */
+                ),
+                item.badge ? (vue.openBlock(), vue.createElementBlock(
+                  "view",
+                  {
+                    key: 0,
+                    class: "function-badge"
+                  },
+                  vue.toDisplayString(item.badge),
+                  1
+                  /* TEXT */
+                )) : vue.createCommentVNode("v-if", true)
+              ], 8, ["onClick"]);
+            }),
+            128
+            /* KEYED_FRAGMENT */
+          ))
+        ]),
+        vue.createElementVNode("view", { class: "function-row" }, [
+          (vue.openBlock(true), vue.createElementBlock(
+            vue.Fragment,
+            null,
+            vue.renderList($setup.functionList.slice(5, 10), (item, index) => {
+              return vue.openBlock(), vue.createElementBlock("view", {
+                class: "function-item",
+                key: index,
+                onClick: ($event) => $setup.handleFunctionClick(item)
+              }, [
+                vue.createElementVNode(
+                  "view",
+                  {
+                    class: "function-icon",
+                    style: vue.normalizeStyle({ backgroundColor: item.bgColor })
+                  },
+                  [
+                    vue.createElementVNode("image", {
+                      src: item.icon,
+                      mode: "aspectFit"
+                    }, null, 8, ["src"])
+                  ],
+                  4
+                  /* STYLE */
+                ),
+                vue.createElementVNode(
+                  "text",
+                  { class: "function-text" },
+                  vue.toDisplayString(item.name),
+                  1
+                  /* TEXT */
+                )
+              ], 8, ["onClick"]);
+            }),
+            128
+            /* KEYED_FRAGMENT */
+          ))
+        ])
+      ]),
+      vue.createCommentVNode(" 课程推荐卡片 "),
+      vue.createElementVNode("view", { class: "course-cards" }, [
+        (vue.openBlock(true), vue.createElementBlock(
+          vue.Fragment,
+          null,
+          vue.renderList($setup.courseCards, (card, index) => {
+            return vue.openBlock(), vue.createElementBlock(
+              "view",
+              {
+                class: "course-card",
+                key: index,
+                style: vue.normalizeStyle({ backgroundColor: card.bgColor })
+              },
+              [
+                vue.createElementVNode("view", { class: "card-content" }, [
+                  vue.createElementVNode(
+                    "text",
+                    { class: "card-title" },
+                    vue.toDisplayString(card.title),
+                    1
+                    /* TEXT */
+                  ),
+                  vue.createElementVNode(
+                    "text",
+                    { class: "card-subtitle" },
+                    vue.toDisplayString(card.subtitle),
+                    1
+                    /* TEXT */
+                  )
+                ]),
+                vue.createElementVNode("image", {
+                  class: "card-image",
+                  src: card.image,
+                  mode: "aspectFit"
+                }, null, 8, ["src"])
+              ],
+              4
+              /* STYLE */
+            );
+          }),
+          128
+          /* KEYED_FRAGMENT */
+        ))
+      ]),
+      vue.createCommentVNode(" 课程列表 "),
+      vue.createElementVNode("view", { class: "course-section" }, [
+        vue.createElementVNode("view", { class: "section-header" }, [
+          vue.createElementVNode("text", { class: "section-title" }, "大家都在学"),
+          vue.createElementVNode("view", { class: "more-btn" }, [
+            vue.createElementVNode("text", null, "更多课程"),
+            vue.createElementVNode("text", { class: "arrow-icon" }, "›")
+          ])
+        ]),
+        vue.createElementVNode("view", { class: "course-list" }, [
+          (vue.openBlock(true), vue.createElementBlock(
+            vue.Fragment,
+            null,
+            vue.renderList($setup.courseList, (course, index) => {
+              return vue.openBlock(), vue.createElementBlock("view", {
+                class: "course-item",
+                key: index,
+                onClick: ($event) => $setup.handleCourseClick(course)
+              }, [
+                vue.createElementVNode("image", {
+                  class: "course-cover",
+                  src: course.cover,
+                  mode: "aspectFill"
+                }, null, 8, ["src"]),
+                vue.createElementVNode("view", { class: "course-info" }, [
+                  vue.createElementVNode(
+                    "text",
+                    { class: "course-title" },
+                    vue.toDisplayString(course.title),
+                    1
+                    /* TEXT */
+                  ),
+                  vue.createElementVNode("view", { class: "course-stats" }, [
+                    vue.createElementVNode(
+                      "text",
+                      { class: "course-students" },
+                      vue.toDisplayString(course.students) + "人学习",
+                      1
+                      /* TEXT */
+                    ),
+                    vue.createElementVNode(
+                      "text",
+                      { class: "course-type" },
+                      vue.toDisplayString(course.type),
+                      1
+                      /* TEXT */
+                    )
+                  ]),
+                  vue.createElementVNode(
+                    "text",
+                    { class: "course-desc" },
+                    vue.toDisplayString(course.description),
+                    1
+                    /* TEXT */
+                  )
+                ])
+              ], 8, ["onClick"]);
+            }),
+            128
+            /* KEYED_FRAGMENT */
+          ))
+        ])
+      ])
+    ]);
+  }
+  const PagesDiscoverDiscover = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["render", _sfc_render$4], ["__scopeId", "data-v-7f6951af"], ["__file", "E:/work/python_Harmony/python_Harmony/pages/discover/discover.vue"]]);
+  const _sfc_main$4 = {
+    __name: "codebase",
+    setup(__props, { expose: __expose }) {
+      __expose();
+      vue.onMounted(() => {
+        formatAppLog("log", "at pages/codebase/codebase.vue:14", "代码库页面加载完成");
+      });
+      const __returned__ = { onMounted: vue.onMounted };
+      Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
+      return __returned__;
+    }
+  };
+  function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
+    return vue.openBlock(), vue.createElementBlock("view", { class: "codebase-container" }, [
+      vue.createElementVNode("view", { class: "page-content" }, [
+        vue.createElementVNode("text", { class: "page-title" }, "代码库"),
+        vue.createElementVNode("text", { class: "page-desc" }, "这里是代码库页面,功能开发中...")
+      ])
+    ]);
+  }
+  const PagesCodebaseCodebase = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$3], ["__scopeId", "data-v-c13aa6cc"], ["__file", "E:/work/python_Harmony/python_Harmony/pages/codebase/codebase.vue"]]);
+  const _sfc_main$3 = {
+    __name: "editor",
+    setup(__props, { expose: __expose }) {
+      __expose();
+      vue.onMounted(() => {
+        formatAppLog("log", "at pages/editor/editor.vue:14", "编辑器页面加载完成");
+      });
+      const __returned__ = { onMounted: vue.onMounted };
+      Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
+      return __returned__;
+    }
+  };
+  function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
+    return vue.openBlock(), vue.createElementBlock("view", { class: "editor-container" }, [
+      vue.createElementVNode("view", { class: "page-content" }, [
+        vue.createElementVNode("text", { class: "page-title" }, "编辑器"),
+        vue.createElementVNode("text", { class: "page-desc" }, "这里是编辑器页面,功能开发中...")
+      ])
+    ]);
+  }
+  const PagesEditorEditor = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$2], ["__scopeId", "data-v-17f547cb"], ["__file", "E:/work/python_Harmony/python_Harmony/pages/editor/editor.vue"]]);
+  const _sfc_main$2 = {
+    __name: "study",
+    setup(__props, { expose: __expose }) {
+      __expose();
+      vue.onMounted(() => {
+        formatAppLog("log", "at pages/study/study.vue:14", "学习页面加载完成");
+      });
+      const __returned__ = { onMounted: vue.onMounted };
+      Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
+      return __returned__;
+    }
+  };
+  function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
+    return vue.openBlock(), vue.createElementBlock("view", { class: "study-container" }, [
+      vue.createElementVNode("view", { class: "page-content" }, [
+        vue.createElementVNode("text", { class: "page-title" }, "学习"),
+        vue.createElementVNode("text", { class: "page-desc" }, "这里是学习页面,功能开发中...")
+      ])
+    ]);
+  }
+  const PagesStudyStudy = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__scopeId", "data-v-3f273c1e"], ["__file", "E:/work/python_Harmony/python_Harmony/pages/study/study.vue"]]);
+  const _sfc_main$1 = {
+    __name: "profile",
+    setup(__props, { expose: __expose }) {
+      __expose();
+      vue.onMounted(() => {
+        formatAppLog("log", "at pages/profile/profile.vue:14", "账号页面加载完成");
+      });
+      const __returned__ = { onMounted: vue.onMounted };
+      Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
+      return __returned__;
+    }
+  };
+  function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
+    return vue.openBlock(), vue.createElementBlock("view", { class: "profile-container" }, [
+      vue.createElementVNode("view", { class: "page-content" }, [
+        vue.createElementVNode("text", { class: "page-title" }, "账号"),
+        vue.createElementVNode("text", { class: "page-desc" }, "这里是账号页面,功能开发中...")
+      ])
+    ]);
+  }
+  const PagesProfileProfile = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-dd383ca2"], ["__file", "E:/work/python_Harmony/python_Harmony/pages/profile/profile.vue"]]);
+  __definePage("pages/discover/discover", PagesDiscoverDiscover);
+  __definePage("pages/codebase/codebase", PagesCodebaseCodebase);
+  __definePage("pages/editor/editor", PagesEditorEditor);
+  __definePage("pages/study/study", PagesStudyStudy);
+  __definePage("pages/profile/profile", PagesProfileProfile);
+  const _sfc_main = {
+    onLaunch: function() {
+      formatAppLog("log", "at App.vue:4", "App Launch");
+    },
+    onShow: function() {
+      formatAppLog("log", "at App.vue:7", "App Show");
+    },
+    onHide: function() {
+      formatAppLog("log", "at App.vue:10", "App Hide");
+    }
+  };
+  const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "E:/work/python_Harmony/python_Harmony/App.vue"]]);
+  function createApp() {
+    const app = vue.createVueApp(App);
+    return {
+      app
+    };
+  }
+  const { app: __app__, Vuex: __Vuex__, Pinia: __Pinia__ } = createApp();
+  uni.Vuex = __Vuex__;
+  uni.Pinia = __Pinia__;
+  __app__.provide("__globalStyles", __uniConfig.styles);
+  __app__._component.mpType = "app";
+  __app__._component.render = () => {
+  };
+  __app__.mount("#app");
+})(Vue);

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
unpackage/dist/dev/app-plus/app.css


+ 168 - 0
unpackage/dist/dev/app-plus/manifest.json

@@ -0,0 +1,168 @@
+{
+  "@platforms": [
+    "android",
+    "iPhone",
+    "iPad"
+  ],
+  "id": "",
+  "name": "python_Harmony",
+  "version": {
+    "name": "1.0.0",
+    "code": "100"
+  },
+  "description": "",
+  "developer": {
+    "name": "",
+    "email": "",
+    "url": ""
+  },
+  "permissions": {
+    "UniNView": {
+      "description": "UniNView原生渲染"
+    }
+  },
+  "plus": {
+    "useragent": {
+      "value": "uni-app",
+      "concatenate": true
+    },
+    "splashscreen": {
+      "target": "id:1",
+      "autoclose": true,
+      "waiting": true,
+      "delay": 0
+    },
+    "popGesture": "close",
+    "launchwebview": {
+      "id": "1",
+      "kernel": "WKWebview"
+    },
+    "usingComponents": true,
+    "nvueStyleCompiler": "uni-app",
+    "compilerVersion": 3,
+    "distribute": {
+      "google": {
+        "permissions": [
+          "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+          "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+          "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+          "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+          "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+          "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+          "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+          "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+          "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+          "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+          "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+          "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+          "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+          "<uses-feature android:name=\"android.hardware.camera\"/>",
+          "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+        ]
+      },
+      "apple": {},
+      "plugins": {
+        "audio": {
+          "mp3": {
+            "description": "Android平台录音支持MP3格式文件"
+          }
+        }
+      }
+    },
+    "statusbar": {
+      "immersed": "supportedDevice",
+      "style": "dark",
+      "background": "#ffffff"
+    },
+    "uniStatistics": {
+      "enable": false
+    },
+    "allowsInlineMediaPlayback": true,
+    "safearea": {
+      "background": "#ffffff",
+      "bottom": {
+        "offset": "auto"
+      }
+    },
+    "uni-app": {
+      "control": "uni-v3",
+      "vueVersion": "3",
+      "compilerVersion": "4.75",
+      "nvueCompiler": "uni-app",
+      "renderer": "auto",
+      "nvue": {
+        "flex-direction": "column"
+      },
+      "nvueLaunchMode": "normal",
+      "webView": {
+        "minUserAgentVersion": "49.0"
+      }
+    },
+    "tabBar": {
+      "position": "bottom",
+      "color": "#999999",
+      "selectedColor": "#667eea",
+      "borderStyle": "rgba(255,255,255,0.4)",
+      "blurEffect": "none",
+      "fontSize": "10px",
+      "iconWidth": "24px",
+      "spacing": "3px",
+      "height": "50px",
+      "list": [
+        {
+          "pagePath": "pages/discover/discover",
+          "iconPath": "/static/tabbar/discover_normal.png",
+          "selectedIconPath": "/static/tabbar/discover_selected.png",
+          "text": "发现"
+        },
+        {
+          "pagePath": "pages/codebase/codebase",
+          "iconPath": "/static/tabbar/codebase_normal.png",
+          "selectedIconPath": "/static/tabbar/codebase_selected.png",
+          "text": "代码库"
+        },
+        {
+          "pagePath": "pages/editor/editor",
+          "iconPath": "/static/tabbar/editor_normal.png",
+          "selectedIconPath": "/static/tabbar/editor_selected.png",
+          "text": "编辑器"
+        },
+        {
+          "pagePath": "pages/study/study",
+          "iconPath": "/static/tabbar/study_normal.png",
+          "selectedIconPath": "/static/tabbar/study_selected.png",
+          "text": "学习"
+        },
+        {
+          "pagePath": "pages/profile/profile",
+          "iconPath": "/static/tabbar/profile_normal.png",
+          "selectedIconPath": "/static/tabbar/profile_selected.png",
+          "text": "账号"
+        }
+      ],
+      "backgroundColor": "#ffffff",
+      "selectedIndex": 0,
+      "shown": true,
+      "child": [
+        "lauchwebview"
+      ],
+      "selected": 0
+    }
+  },
+  "app-harmony": {
+    "useragent": {
+      "value": "uni-app",
+      "concatenate": true
+    },
+    "uniStatistics": {
+      "enable": false
+    },
+    "safearea": {
+      "background": "#ffffff",
+      "bottom": {
+        "offset": "auto"
+      }
+    }
+  },
+  "launch_path": "__uniappview.html"
+}

+ 47 - 0
unpackage/dist/dev/app-plus/pages/codebase/codebase.css

@@ -0,0 +1,47 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+/* 颜色变量 */
+/* 行为相关颜色 */
+/* 文字基本颜色 */
+/* 背景颜色 */
+/* 边框颜色 */
+/* 尺寸变量 */
+/* 文字尺寸 */
+/* 图片尺寸 */
+/* Border Radius */
+/* 水平间距 */
+/* 垂直间距 */
+/* 透明度 */
+/* 文章场景相关 */
+/* 自定义项目颜色 */
+/* 功能模块颜色 */
+.codebase-container[data-v-c13aa6cc] {
+  background-color: #f5f5f5;
+  min-height: 100vh;
+}
+.codebase-container .page-content[data-v-c13aa6cc] {
+  padding: 3.125rem 1.875rem;
+  text-align: center;
+}
+.codebase-container .page-content .page-title[data-v-c13aa6cc] {
+  display: block;
+  font-size: 1.5rem;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 0.9375rem;
+}
+.codebase-container .page-content .page-desc[data-v-c13aa6cc] {
+  display: block;
+  font-size: 0.875rem;
+  color: #666;
+}

+ 223 - 0
unpackage/dist/dev/app-plus/pages/discover/discover.css

@@ -0,0 +1,223 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+/* 颜色变量 */
+/* 行为相关颜色 */
+/* 文字基本颜色 */
+/* 背景颜色 */
+/* 边框颜色 */
+/* 尺寸变量 */
+/* 文字尺寸 */
+/* 图片尺寸 */
+/* Border Radius */
+/* 水平间距 */
+/* 垂直间距 */
+/* 透明度 */
+/* 文章场景相关 */
+/* 自定义项目颜色 */
+/* 功能模块颜色 */
+.discover-container[data-v-7f6951af] {
+  background-color: #f5f5f5;
+  min-height: 100vh;
+}
+.status-bar[data-v-7f6951af] {
+  background-color: #fff;
+  /* 使用CSS环境变量适配安全区域 */
+  height: env(safe-area-inset-top);
+  height: constant(safe-area-inset-top);
+  /* 兼容iOS < 11.2 */
+  /* 设置最小高度,确保在没有刘海屏的设备上也有合适的高度 */
+  min-height: 20px;
+}
+.search-section[data-v-7f6951af] {
+  background-color: #fff;
+  padding: 0.625rem 0.9375rem;
+}
+.search-section .search-box[data-v-7f6951af] {
+  display: flex;
+  align-items: center;
+  background-color: #f8f8f8;
+  border-radius: 1.5625rem;
+  padding: 0.625rem 0.9375rem;
+}
+.search-section .search-box .search-icon[data-v-7f6951af] {
+  font-size: 1rem;
+  color: #999;
+}
+.search-section .search-box .search-input[data-v-7f6951af] {
+  flex: 1;
+  margin-left: 0.625rem;
+  font-size: 0.875rem;
+  color: #333;
+}
+.banner-section[data-v-7f6951af] {
+  margin: 0.625rem 0.9375rem;
+}
+.banner-section .banner-swiper[data-v-7f6951af] {
+  height: 9.375rem;
+  border-radius: 0.625rem;
+  overflow: hidden;
+}
+.banner-section .swipe-image[data-v-7f6951af] {
+  width: 100%;
+  height: 100%;
+  border-radius: 0.625rem;
+}
+.function-section[data-v-7f6951af] {
+  background-color: #fff;
+  margin: 0.625rem 0.9375rem;
+  border-radius: 0.625rem;
+  padding: 1.25rem 0.625rem;
+}
+.function-section .function-row[data-v-7f6951af] {
+  display: flex;
+  justify-content: space-around;
+  margin-bottom: 1.25rem;
+}
+.function-section .function-row[data-v-7f6951af]:last-child {
+  margin-bottom: 0;
+}
+.function-section .function-item[data-v-7f6951af] {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  position: relative;
+}
+.function-section .function-item .function-icon[data-v-7f6951af] {
+  width: 2.5rem;
+  height: 2.5rem;
+  border-radius: 0.625rem;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 0.46875rem;
+}
+.function-section .function-item .function-text[data-v-7f6951af] {
+  font-size: 0.75rem;
+  color: #333;
+}
+.function-section .function-item .function-badge[data-v-7f6951af] {
+  position: absolute;
+  top: -0.3125rem;
+  right: -0.3125rem;
+  background-color: #FF4757;
+  color: #fff;
+  font-size: 0.5625rem;
+  padding: 0.125rem 0.25rem;
+  border-radius: 0.625rem;
+  transform: scale(0.8);
+}
+.course-cards[data-v-7f6951af] {
+  margin: 0.625rem 0.9375rem;
+  display: flex;
+  gap: 0.625rem;
+}
+.course-cards .course-card[data-v-7f6951af] {
+  flex: 1;
+  height: 6.25rem;
+  border-radius: 0.625rem;
+  padding: 0.9375rem;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+.course-cards .course-card .card-content[data-v-7f6951af] {
+  flex: 1;
+}
+.course-cards .course-card .card-content .card-title[data-v-7f6951af] {
+  display: block;
+  color: #fff;
+  font-size: 0.875rem;
+  font-weight: bold;
+  margin-bottom: 0.3125rem;
+}
+.course-cards .course-card .card-content .card-subtitle[data-v-7f6951af] {
+  display: block;
+  color: #fff;
+  font-size: 0.6875rem;
+  opacity: 0.9;
+}
+.course-cards .course-card .card-image[data-v-7f6951af] {
+  width: 2.5rem;
+  height: 2.5rem;
+}
+.course-section[data-v-7f6951af] {
+  background-color: #fff;
+  margin: 0.625rem 0.9375rem;
+  border-radius: 0.625rem;
+  padding: 0.9375rem;
+}
+.course-section .section-header[data-v-7f6951af] {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 0.9375rem;
+}
+.course-section .section-header .section-title[data-v-7f6951af] {
+  font-size: 1rem;
+  font-weight: bold;
+  color: #333;
+}
+.course-section .section-header .more-btn[data-v-7f6951af] {
+  display: flex;
+  align-items: center;
+  color: #999;
+  font-size: 0.75rem;
+}
+.course-section .section-header .more-btn .arrow-icon[data-v-7f6951af] {
+  font-size: 0.875rem;
+  margin-left: 0.25rem;
+  color: #999;
+}
+.course-section .course-list .course-item[data-v-7f6951af] {
+  display: flex;
+  margin-bottom: 0.9375rem;
+}
+.course-section .course-list .course-item[data-v-7f6951af]:last-child {
+  margin-bottom: 0;
+}
+.course-section .course-list .course-item .course-cover[data-v-7f6951af] {
+  width: 5rem;
+  height: 3.75rem;
+  border-radius: 0.375rem;
+  margin-right: 0.625rem;
+}
+.course-section .course-list .course-item .course-info[data-v-7f6951af] {
+  flex: 1;
+}
+.course-section .course-list .course-item .course-info .course-title[data-v-7f6951af] {
+  display: block;
+  font-size: 0.875rem;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 0.3125rem;
+}
+.course-section .course-list .course-item .course-info .course-stats[data-v-7f6951af] {
+  display: flex;
+  align-items: center;
+  margin-bottom: 0.3125rem;
+}
+.course-section .course-list .course-item .course-info .course-stats .course-students[data-v-7f6951af] {
+  font-size: 0.6875rem;
+  color: #999;
+  margin-right: 0.625rem;
+}
+.course-section .course-list .course-item .course-info .course-stats .course-type[data-v-7f6951af] {
+  font-size: 0.6875rem;
+  color: #999;
+}
+.course-section .course-list .course-item .course-info .course-desc[data-v-7f6951af] {
+  font-size: 0.75rem;
+  color: #666;
+  line-height: 1.4;
+}

+ 47 - 0
unpackage/dist/dev/app-plus/pages/editor/editor.css

@@ -0,0 +1,47 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+/* 颜色变量 */
+/* 行为相关颜色 */
+/* 文字基本颜色 */
+/* 背景颜色 */
+/* 边框颜色 */
+/* 尺寸变量 */
+/* 文字尺寸 */
+/* 图片尺寸 */
+/* Border Radius */
+/* 水平间距 */
+/* 垂直间距 */
+/* 透明度 */
+/* 文章场景相关 */
+/* 自定义项目颜色 */
+/* 功能模块颜色 */
+.editor-container[data-v-17f547cb] {
+  background-color: #f5f5f5;
+  min-height: 100vh;
+}
+.editor-container .page-content[data-v-17f547cb] {
+  padding: 3.125rem 1.875rem;
+  text-align: center;
+}
+.editor-container .page-content .page-title[data-v-17f547cb] {
+  display: block;
+  font-size: 1.5rem;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 0.9375rem;
+}
+.editor-container .page-content .page-desc[data-v-17f547cb] {
+  display: block;
+  font-size: 0.875rem;
+  color: #666;
+}

+ 47 - 0
unpackage/dist/dev/app-plus/pages/profile/profile.css

@@ -0,0 +1,47 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+/* 颜色变量 */
+/* 行为相关颜色 */
+/* 文字基本颜色 */
+/* 背景颜色 */
+/* 边框颜色 */
+/* 尺寸变量 */
+/* 文字尺寸 */
+/* 图片尺寸 */
+/* Border Radius */
+/* 水平间距 */
+/* 垂直间距 */
+/* 透明度 */
+/* 文章场景相关 */
+/* 自定义项目颜色 */
+/* 功能模块颜色 */
+.profile-container[data-v-dd383ca2] {
+  background-color: #f5f5f5;
+  min-height: 100vh;
+}
+.profile-container .page-content[data-v-dd383ca2] {
+  padding: 3.125rem 1.875rem;
+  text-align: center;
+}
+.profile-container .page-content .page-title[data-v-dd383ca2] {
+  display: block;
+  font-size: 1.5rem;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 0.9375rem;
+}
+.profile-container .page-content .page-desc[data-v-dd383ca2] {
+  display: block;
+  font-size: 0.875rem;
+  color: #666;
+}

+ 47 - 0
unpackage/dist/dev/app-plus/pages/study/study.css

@@ -0,0 +1,47 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+/* 颜色变量 */
+/* 行为相关颜色 */
+/* 文字基本颜色 */
+/* 背景颜色 */
+/* 边框颜色 */
+/* 尺寸变量 */
+/* 文字尺寸 */
+/* 图片尺寸 */
+/* Border Radius */
+/* 水平间距 */
+/* 垂直间距 */
+/* 透明度 */
+/* 文章场景相关 */
+/* 自定义项目颜色 */
+/* 功能模块颜色 */
+.study-container[data-v-3f273c1e] {
+  background-color: #f5f5f5;
+  min-height: 100vh;
+}
+.study-container .page-content[data-v-3f273c1e] {
+  padding: 3.125rem 1.875rem;
+  text-align: center;
+}
+.study-container .page-content .page-title[data-v-3f273c1e] {
+  display: block;
+  font-size: 1.5rem;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 0.9375rem;
+}
+.study-container .page-content .page-desc[data-v-3f273c1e] {
+  display: block;
+  font-size: 0.875rem;
+  color: #666;
+}

BIN
unpackage/dist/dev/app-plus/static/icons/question.png


BIN
unpackage/dist/dev/app-plus/static/icons/study.png


BIN
unpackage/dist/dev/app-plus/static/icons/tutorial.png


BIN
unpackage/dist/dev/app-plus/static/icons/video.png


BIN
unpackage/dist/dev/app-plus/static/icons/vip.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/codebase_normal.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/codebase_selected.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/discover_normal.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/discover_selected.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/editor_normal.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/editor_selected.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/profile_normal.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/profile_selected.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/study_normal.png


BIN
unpackage/dist/dev/app-plus/static/tabbar/study_selected.png


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
unpackage/dist/dev/app-plus/uni-app-view.umd.js


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
unpackage/dist/dev/cache/.app-plus/tsc/app-android/.tsbuildInfo


+ 54 - 0
utils/api.js

@@ -0,0 +1,54 @@
+/**
+ * API接口封装
+ */
+import request from './request';
+
+// 用户相关接口
+const userApi = {
+  // 登录
+  login(data, callback) {
+    return request.post('/user/login', data, {}, callback);
+  },
+  
+  // 注册
+  register(data, callback) {
+    return request.post('/user/register', data, {}, callback);
+  },
+  
+  // 获取用户信息
+  getUserInfo(data, callback) {
+    return request.get('/user/info', data, {}, callback);
+  }
+};
+
+// 内容相关接口
+const contentApi = {
+  // 获取列表
+  getList(data, callback) {
+    return request.get('/content/list', data, {}, callback);
+  },
+  
+  // 获取详情
+  getDetail(id, callback) {
+    return request.get('/content/detail', { id }, {}, callback);
+  }
+};
+
+// Promise风格的API
+const promiseApi = {
+  // 登录
+  login(data) {
+    return request.postPromise('/user/login', data);
+  },
+  
+  // 获取用户信息
+  getUserInfo(data) {
+    return request.getPromise('/user/info', data);
+  }
+};
+
+export {
+  userApi,
+  contentApi,
+  promiseApi
+};

+ 224 - 0
utils/request.js

@@ -0,0 +1,224 @@
+/**
+ * 网络请求封装
+ * 基于uni-app的网络请求API进行封装
+ */
+
+// 导入配置文件
+import config from '@/config/index.js';
+
+// 使用导入的配置
+const CONFIG = config;
+
+/**
+ * 获取用户信息
+ * @param {Function} callback 回调函数
+ */
+const getUserInfo = function(callback) {
+  try {
+    // 从本地存储获取用户信息
+    const userInfoStr = uni.getStorageSync('userInfo');
+    if (userInfoStr) {
+      const userInfo = JSON.parse(userInfoStr);
+      callback && callback({ status: true, data: userInfo });
+    } else {
+      callback && callback({ status: false });
+    }
+  } catch (e) {
+    console.error('获取用户信息失败', e);
+    callback && callback({ status: false, error: e });
+  }
+};
+
+/**
+ * 检查值是否存在,不存在则返回空字符串
+ * @param {*} value 需要检查的值
+ * @returns {*} 处理后的值
+ */
+const checkValue = function(value) {
+  return value !== undefined && value !== null ? value : '';
+};
+
+/**
+ * 请求拦截器
+ * @param {Object} options 请求配置
+ * @returns {Object} 处理后的请求配置
+ */
+const requestInterceptor = function(options) {
+  // 这里可以添加请求前的处理逻辑
+  return options;
+};
+
+/**
+ * 响应拦截器
+ * @param {Object} response 响应数据
+ * @param {Object} options 请求配置
+ * @returns {Object} 处理后的响应数据
+ */
+const responseInterceptor = function(response, options) {
+  // 这里可以添加响应后的处理逻辑
+  return response;
+};
+
+/**
+ * 网络请求方法
+ * @param {String} url 请求地址,不需要带域名
+ * @param {Object} headParam 请求配置参数,可调整需要的参数
+ * @param {Object} bodyParam 请求体参数
+ * @param {Function} callback 回调函数
+ */
+const request = function(url, headParam = {}, bodyParam = {}, callback) {
+  getUserInfo(function(ret) {
+    let userInfo = {};
+    
+    if (ret && ret.status === true && ret.data) {
+      userInfo = ret.data;
+    }
+    
+    // 确保参数是对象类型
+    if (!(headParam instanceof Object)) {
+      headParam = {};
+    }
+    if (!(bodyParam instanceof Object)) {
+      bodyParam = {};
+    }
+    
+    // 安全获取系统信息
+    let systemInfo = {};
+    try {
+      systemInfo = uni.getSystemInfoSync();
+    } catch (e) {
+      console.error('获取系统信息失败:', e);
+      systemInfo = { platform: 'unknown', deviceId: '' };
+    }
+    
+    // 安全获取应用版本
+    let appVersion = '';
+    try {
+      // 检查plus对象是否存在(仅在App环境下存在)
+      if (typeof plus !== 'undefined' && plus.runtime) {
+        appVersion = plus.runtime.version;
+      } else {
+        // 在非App环境下使用默认版本或从其他地方获取
+        appVersion = CONFIG.VERSION || '1.0.0';
+      }
+    } catch (e) {
+      console.error('获取应用版本失败:', e);
+      appVersion = CONFIG.VERSION || '1.0.0';
+    }
+    
+    // 添加设备和应用信息
+    bodyParam.deviceId = systemInfo.deviceId || '';
+    bodyParam.platform = systemInfo.platform || 'unknown';
+    bodyParam.appVersion = appVersion;
+    bodyParam.systemType = systemInfo.platform || 'unknown';
+    bodyParam.channel = CONFIG.CHANNEL || 'default';
+    
+    // 添加用户认证信息
+    bodyParam.apikey = checkValue(userInfo.apikey);
+    bodyParam.apiauth = checkValue(userInfo.apiauth);
+    
+    // 设置请求URL和方法
+    const requestOptions = {
+      url: /^(http|https):\/\//.test(url) ? url : CONFIG.BASE_URL + url,
+      method: headParam.method || 'POST',
+      header: headParam.header || {
+        'Content-Type': 'application/json;charset=UTF-8'
+      },
+      data: bodyParam,
+      timeout: headParam.timeout || 30000
+    };
+    
+    // 应用请求拦截器
+    const finalOptions = requestInterceptor(requestOptions);
+    
+    // 发起请求
+    uni.request({
+      ...finalOptions,
+      success: (res) => {
+        // 应用响应拦截器
+        const processedRes = responseInterceptor(res, finalOptions);
+        callback && callback(processedRes, null);
+      },
+      fail: (err) => {
+        console.error('请求失败', err);
+        callback && callback(null, err);
+      }
+    });
+  });
+};
+
+/**
+ * GET请求
+ * @param {String} url 请求地址
+ * @param {Object} data 请求参数
+ * @param {Object} options 请求配置
+ * @param {Function} callback 回调函数
+ */
+const get = function(url, data = {}, options = {}, callback) {
+  const headParam = { ...options, method: 'GET' };
+  request(url, headParam, data, callback);
+};
+
+/**
+ * POST请求
+ * @param {String} url 请求地址
+ * @param {Object} data 请求参数
+ * @param {Object} options 请求配置
+ * @param {Function} callback 回调函数
+ */
+const post = function(url, data = {}, options = {}, callback) {
+  const headParam = { ...options, method: 'POST' };
+  request(url, headParam, data, callback);
+};
+
+/**
+ * Promise风格的请求方法
+ * @param {String} url 请求地址
+ * @param {Object} headParam 请求配置
+ * @param {Object} bodyParam 请求体参数
+ * @returns {Promise} Promise对象
+ */
+const requestPromise = function(url, headParam = {}, bodyParam = {}) {
+  return new Promise((resolve, reject) => {
+    request(url, headParam, bodyParam, (res, err) => {
+      if (err) {
+        reject(err);
+      } else {
+        resolve(res);
+      }
+    });
+  });
+};
+
+/**
+ * Promise风格的GET请求
+ * @param {String} url 请求地址
+ * @param {Object} data 请求参数
+ * @param {Object} options 请求配置
+ * @returns {Promise} Promise对象
+ */
+const getPromise = function(url, data = {}, options = {}) {
+  return requestPromise(url, { ...options, method: 'GET' }, data);
+};
+
+/**
+ * Promise风格的POST请求
+ * @param {String} url 请求地址
+ * @param {Object} data 请求参数
+ * @param {Object} options 请求配置
+ * @returns {Promise} Promise对象
+ */
+const postPromise = function(url, data = {}, options = {}) {
+  return requestPromise(url, { ...options, method: 'POST' }, data);
+};
+
+export default {
+  request,
+  get,
+  post,
+  requestPromise,
+  getPromise,
+  postPromise,
+  getUserInfo,
+  checkValue
+};

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.