Przeglądaj źródła

feat: ✨ 登录接口联调

zhangtao 9 godzin temu
rodzic
commit
608be7efa9

+ 4 - 0
src/App.vue

@@ -16,5 +16,9 @@ onLaunch(() => {});
   :deep(.inputClass) {
     background: none !important;
   }
+  :deep(.wd-input__icon) {
+    // color: none !important;
+    background: none !important;
+  }
 }
 </style>

+ 5 - 0
src/api/apiDefinitions.ts

@@ -0,0 +1,5 @@
+export default {
+  "user.loginUser": ["POST", "/sys/loginApp"],
+  "courses.list": ["GET", "/app/appCourese/list"],
+  "courses.getCourseDetail": ["GET", "/app/appCourese/queryById"],
+};

+ 115 - 0
src/api/core/handlers.ts

@@ -0,0 +1,115 @@
+import type { Method } from "alova";
+import router from "@/router";
+
+// Custom error class for API errors
+export class ApiError extends Error {
+  code: number;
+  data?: any;
+
+  constructor(message: string, code: number, data?: any) {
+    super(message);
+    this.name = "ApiError";
+    this.code = code;
+    this.data = data;
+  }
+}
+
+// Define a type for the expected API response structure
+interface ApiResponse {
+  code?: number;
+  message?: string;
+  result?: any;
+  success?: boolean;
+  total?: number;
+  more?: boolean;
+}
+
+// Handle successful responses
+export async function handleAlovaResponse(
+  response:
+    | UniApp.RequestSuccessCallbackResult
+    | UniApp.UploadFileSuccessCallbackResult
+    | UniApp.DownloadSuccessData,
+) {
+  const globalToast = useGlobalToast();
+  // Extract status code and data from UniApp response
+  const { statusCode, data } =
+    response as UniNamespace.RequestSuccessCallbackResult;
+
+  // 处理401/403错误(如果不是在handleAlovaResponse中处理的)
+  if (
+    typeof data === "object" &&
+    data !== null &&
+    "code" in data &&
+    (data.code === 401 || data.code === 403)
+  ) {
+    // 如果是未授权错误,清除用户信息并跳转到登录页
+    uni.showToast({
+      title: "登录已过期,请重新登录!",
+      duration: 2000,
+      icon: "none",
+    });
+    const timer = setTimeout(() => {
+      clearTimeout(timer);
+      router.replaceAll({ name: "login" });
+    }, 500);
+
+    throw new ApiError("登录已过期,请重新登录!", statusCode, data);
+  }
+
+  // Handle HTTP error status codes
+  if (
+    typeof data === "object" &&
+    data !== null &&
+    "code" in data &&
+    data.code >= 400
+  ) {
+    uni.showToast({
+      title: (data as any).message,
+      duration: 2000,
+      icon: "none",
+    });
+  }
+
+  // The data is already parsed by UniApp adapter
+  const json = data as ApiResponse;
+  // Log response in development
+  if (import.meta.env.MODE === "development") {
+    console.log("[Alova Response]", json);
+  }
+  // Return data for successful responses
+  return json;
+}
+
+// Handle request errors
+export function handleAlovaError(error: any, method: Method) {
+  const globalToast = useGlobalToast();
+  // Log error in development
+  if (import.meta.env.MODE === "development") {
+    console.error("[Alova Error]", error, method);
+  }
+
+  // 处理401/403错误(如果不是在handleAlovaResponse中处理的)
+  if (error instanceof ApiError && (error.code === 401 || error.code === 403)) {
+    // 如果是未授权错误,清除用户信息并跳转到登录页
+    globalToast.error({ msg: "登录已过期,请重新登录!", duration: 500 });
+    const timer = setTimeout(() => {
+      clearTimeout(timer);
+      router.replaceAll({ name: "login" });
+    }, 500);
+    throw new ApiError("登录已过期,请重新登录!", error.code, error.data);
+  }
+
+  // Handle different types of errors
+  if (error.name === "NetworkError") {
+    globalToast.error("网络错误,请检查您的网络连接");
+  } else if (error.name === "TimeoutError") {
+    globalToast.error("请求超时,请重试");
+  } else if (error instanceof ApiError) {
+    globalToast.error(error.message || "请求失败");
+  } else {
+    globalToast.error("发生意外错误");
+  }
+
+  throw error;
+}

+ 59 - 0
src/api/core/instance.ts

@@ -0,0 +1,59 @@
+import { createAlova } from "alova";
+import vueHook from "alova/vue";
+import AdapterUniapp from "@alova/adapter-uniapp";
+import { handleAlovaError, handleAlovaResponse } from "./handlers";
+import { BASE_URL } from "@/config";
+
+export const alovaInstance = createAlova({
+  baseURL: BASE_URL,
+  ...AdapterUniapp(),
+  statesHook: vueHook,
+  beforeRequest: (method) => {
+    // Add content type for POST/PUT/PATCH requests
+    if (["POST", "PUT", "PATCH"].includes(method.type)) {
+      method.config.headers["Content-Type"] = "application/json";
+    }
+    const { token } = useUserStore();
+    if (token) {
+      method.config.headers["X-Access-Token"] = token;
+    }
+    // Add timestamp to prevent caching for GET requests
+    if (method.type === "GET" && CommonUtil.isObj(method.config.params)) {
+      method.config.params._t = Date.now();
+    }
+
+    // Log request in development
+    if (import.meta.env.MODE === "development") {
+      console.log(
+        `[Alova Request] ${method.type} ${method.url}`,
+        method.data || method.config.params,
+      );
+      console.log(`[API Base URL] ${import.meta.env.VITE_API_BASE_URL}`);
+      console.log(`[Environment] ${import.meta.env.VITE_ENV_NAME}`);
+    }
+  },
+
+  // Response handlers
+  responded: {
+    // Success handler
+    onSuccess: handleAlovaResponse,
+
+    // Error handler
+    onError: handleAlovaError,
+
+    // Complete handler - runs after success or error
+    onComplete: async () => {
+      // Any cleanup or logging can be done here
+    },
+  },
+
+  // We'll use the middleware in the hooks
+  // middleware is not directly supported in createAlova options
+
+  // Default request timeout (10 seconds)
+  timeout: 60000,
+  // 设置为null即可全局关闭全部请求缓存
+  cacheFor: null,
+});
+
+export default alovaInstance;

+ 91 - 0
src/api/core/middleware.ts

@@ -0,0 +1,91 @@
+/**
+ * 延迟加载中间件
+ * 延迟显示加载状态,防止快速请求导致的闪烁
+ * @param delay 显示加载状态前的延迟时间(毫秒)
+ * @returns Alova 中间件
+ */
+export function createDelayLoadingMiddleware(delay = 300) {
+  return async (context: any, next: any) => {
+    context.controlLoading();
+
+    const { loading } = context.proxyStates;
+
+    const timer = setTimeout(() => {
+      loading.v = true;
+    }, delay);
+
+    await next();
+
+    loading.v = false;
+    clearTimeout(timer);
+  };
+}
+
+/**
+ * 全局加载中间件
+ * 为所有请求显示全局加载指示器,支持延迟显示
+ *
+ * 使用示例:
+ * ```typescript
+ * // 1. 基本用法
+ * const { send: submit } = useRequest(method, {
+ *   middleware: createGlobalLoadingMiddleware()
+ * });
+ *
+ * // 2. 自定义延迟时间和加载文本
+ * const { send: submit } = useRequest(method, {
+ *   middleware: createGlobalLoadingMiddleware({
+ *     delay: 500,           // 延迟 500ms 显示加载指示器,防止闪烁
+ *     loadingText: '正在提交...', // 自定义加载文本
+ *   })
+ * });
+ * ```
+ *
+ * @param options 加载选项
+ * @param options.delay 显示加载指示器前的延迟时间(毫秒),默认 300ms
+ * @param options.loadingText 加载指示器显示的文本,默认为 'Loading...'
+ * @returns Alova 中间件
+ */
+export function createGlobalLoadingMiddleware(
+  options: {
+    delay?: number;
+    loadingText?: string;
+  } = {},
+) {
+  const { delay = 0, loadingText = "Loading..." } = options;
+
+  return async (ctx: any, next: any) => {
+    // 自行控制loading
+    ctx.controlLoading();
+
+    // const globalLoading = useGlobalLoading();
+    let timer: ReturnType<typeof setTimeout> | null = null;
+    // console.log("ctx.proxyStates.loading", globalLoading);
+
+    // 如果delay为0或未设置,直接显示loading
+    if (delay <= 0) {
+      // globalLoading.loading(loadingText);
+      uni.showLoading({ mask: true, title: loadingText });
+    } else {
+      // 延迟特定时间显示全局loading
+      timer = setTimeout(() => {
+        uni.showLoading({ mask: true, title: loadingText });
+      }, delay);
+    }
+
+    try {
+      await next();
+    } finally {
+      // 清除定时器并关闭loading
+      if (timer) {
+        clearTimeout(timer);
+      }
+      uni.hideLoading();
+    }
+  };
+}
+
+// 导出延迟加载中间件作为默认中间件
+export const defaultMiddleware = createDelayLoadingMiddleware();
+
+export default defaultMiddleware;

+ 105 - 0
src/api/createApi.ts

@@ -0,0 +1,105 @@
+import type {
+  Alova,
+  MethodType,
+  AlovaGenerics,
+  AlovaMethodCreateConfig,
+} from "alova";
+import { Method } from "alova";
+import apiDefinitions from "./apiDefinitions";
+
+const createFunctionalProxy = (
+  array: (string | symbol)[],
+  alovaInstance: Alova<AlovaGenerics>,
+  configMap: any,
+) => {
+  // create a new proxy instance
+  return new Proxy(function () {}, {
+    get(_, property) {
+      // record the target property, so that it can get the completed accessing paths
+      const newArray = [...array, property];
+      // always return a new proxy to continue recording accessing paths.
+      return createFunctionalProxy(newArray, alovaInstance, configMap);
+    },
+    apply(_, __, [config]) {
+      const apiPathKey = array.join(".") as keyof typeof apiDefinitions;
+      const apiItem = apiDefinitions[apiPathKey];
+      if (!apiItem) {
+        throw new Error(`the api path of \`${apiPathKey}\` is not found`);
+      }
+      const mergedConfig = {
+        ...configMap[apiPathKey],
+        ...config,
+      };
+      const [method, url] = apiItem;
+      const pathParams = mergedConfig.pathParams;
+      const urlReplaced = url.replace(/\{([^}]+)\}/g, (_, key) => {
+        const pathParam = pathParams[key];
+        return pathParam;
+      });
+      delete mergedConfig.pathParams;
+      let data = mergedConfig.data;
+      if (
+        Object.prototype.toString.call(data) === "[object Object]" &&
+        typeof FormData !== "undefined"
+      ) {
+        let hasBlobData = false;
+        const formData = new FormData();
+        for (const key in data) {
+          formData.append(key, data[key]);
+          if (data[key] instanceof Blob) {
+            hasBlobData = true;
+          }
+        }
+        data = hasBlobData ? formData : data;
+      }
+      return new Method(
+        method.toUpperCase() as MethodType,
+        alovaInstance,
+        urlReplaced,
+        mergedConfig,
+        data,
+      );
+    },
+  });
+};
+
+export const createApis = (
+  alovaInstance: Alova<AlovaGenerics>,
+  configMap: any,
+) => {
+  const Apis = new Proxy({} as Apis, {
+    get(_, property) {
+      return createFunctionalProxy([property], alovaInstance, configMap);
+    },
+  });
+  // define global variable `Apis`
+  (globalThis as any).Apis = Apis;
+  return Apis;
+};
+type MethodConfig<T> = AlovaMethodCreateConfig<
+  (typeof import("./index"))["alovaInstance"] extends Alova<infer AG>
+    ? AG
+    : any,
+  any,
+  T
+>;
+type APISofParameters<
+  Tag extends string,
+  Url extends string,
+> = Tag extends keyof Apis
+  ? Url extends keyof Apis[Tag]
+    ? Apis[Tag][Url] extends (...args: any) => any
+      ? Parameters<Apis[Tag][Url]>
+      : any
+    : any
+  : any;
+type MethodsConfigMap = {
+  [P in keyof typeof import("./apiDefinitions").default]?: MethodConfig<
+    P extends `${infer Tag}.${infer Url}`
+      ? Parameters<APISofParameters<Tag, Url>[0]["transform"]>[0]
+      : any
+  >;
+};
+export const withConfigType = <Config extends MethodsConfigMap>(
+  config: Config,
+) => config;

+ 1080 - 0
src/api/globals.d.ts

@@ -0,0 +1,1080 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Swagger Petstore - OpenAPI 3.0 - version 1.0.26
+ *
+ * This is a sample Pet Store Server based on the OpenAPI 3.0 specification.  You can find out more about
+Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we&#x27;ve switched to the design first approach!
+You can now help us improve the API whether it&#x27;s by making changes to the definition itself or to the code.
+That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
+
+Some useful links:
+- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
+- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
+ *
+ * OpenAPI version: 3.0.4
+ *
+ * Contact:
+ *
+ * NOTE: This file is auto generated by the alova's vscode plugin.
+ *
+ * https://alova.js.org/devtools/vscode
+ *
+ * **Do not edit the file manually.**
+ */
+import type {
+  Alova,
+  AlovaMethodCreateConfig,
+  AlovaGenerics,
+  Method,
+} from "alova";
+import type { $$userConfigMap, alovaInstance } from ".";
+import type apiDefinitions from "./apiDefinitions";
+
+type CollapsedAlova = typeof alovaInstance;
+type UserMethodConfigMap = typeof $$userConfigMap;
+
+type Alova2MethodConfig<Responded> =
+  CollapsedAlova extends Alova<
+    AlovaGenerics<
+      any,
+      any,
+      infer RequestConfig,
+      infer Response,
+      infer ResponseHeader,
+      infer L1Cache,
+      infer L2Cache,
+      infer SE
+    >
+  >
+    ? Omit<
+        AlovaMethodCreateConfig<
+          AlovaGenerics<
+            Responded,
+            any,
+            RequestConfig,
+            Response,
+            ResponseHeader,
+            L1Cache,
+            L2Cache,
+            SE
+          >,
+          any,
+          Responded
+        >,
+        "params"
+      >
+    : never;
+
+// Extract the return type of transform function that define in $$userConfigMap, if it not exists, use the default type.
+type ExtractUserDefinedTransformed<
+  DefinitionKey extends keyof typeof apiDefinitions,
+  Default,
+> = DefinitionKey extends keyof UserMethodConfigMap
+  ? UserMethodConfigMap[DefinitionKey]["transform"] extends (
+      ...args: any[]
+    ) => any
+    ? Awaited<ReturnType<UserMethodConfigMap[DefinitionKey]["transform"]>>
+    : Default
+  : Default;
+type Alova2Method<
+  Responded,
+  DefinitionKey extends keyof typeof apiDefinitions,
+  CurrentConfig extends Alova2MethodConfig<any>,
+> =
+  CollapsedAlova extends Alova<
+    AlovaGenerics<
+      any,
+      any,
+      infer RequestConfig,
+      infer Response,
+      infer ResponseHeader,
+      infer L1Cache,
+      infer L2Cache,
+      infer SE
+    >
+  >
+    ? Method<
+        AlovaGenerics<
+          CurrentConfig extends undefined
+            ? ExtractUserDefinedTransformed<DefinitionKey, Responded>
+            : CurrentConfig["transform"] extends (...args: any[]) => any
+              ? Awaited<ReturnType<CurrentConfig["transform"]>>
+              : ExtractUserDefinedTransformed<DefinitionKey, Responded>,
+          any,
+          RequestConfig,
+          Response,
+          ResponseHeader,
+          L1Cache,
+          L2Cache,
+          SE
+        >
+      >
+    : never;
+
+export type Category = {
+  id?: number;
+  name?: string;
+};
+export type Tag = {
+  id?: number;
+  name?: string;
+};
+export type Pet = {
+  id?: number;
+  /**
+   * [required]
+   */
+  name: string;
+  category?: Category;
+  /**
+   * [required]
+   */
+  photoUrls: string[];
+  tags?: Tag[];
+  /**
+   * pet status in the store
+   */
+  status?: "available" | "pending" | "sold";
+};
+export type ApiResponse = {
+  code?: number;
+  type?: string;
+  message?: string;
+};
+export type Order = {
+  id?: number;
+  petId?: number;
+  quantity?: number;
+  shipDate?: string;
+  /**
+   * Order Status
+   */
+  status?: "placed" | "approved" | "delivered";
+  complete?: boolean;
+};
+export type User = {
+  id?: number;
+  username?: string;
+  firstName?: string;
+  lastName?: string;
+  email?: string;
+  password?: string;
+  phone?: string;
+  /**
+   * User Status
+   */
+  userStatus?: number;
+};
+declare global {
+  interface Apis {
+    pet: {
+      /**
+       * ---
+       *
+       * [PUT] Update an existing pet.
+       *
+       * **path:** /pet
+       *
+       * ---
+       *
+       * **RequestBody**
+       * ```ts
+       * type RequestBody = {
+       *   id?: number
+       *   // [required]
+       *   name: string
+       *   category?: {
+       *     id?: number
+       *     name?: string
+       *   }
+       *   // [required]
+       *   photoUrls: string[]
+       *   tags?: Array<{
+       *     id?: number
+       *     name?: string
+       *   }>
+       *   // pet status in the store
+       *   status?: 'available' | 'pending' | 'sold'
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   // [required]
+       *   name: string
+       *   category?: {
+       *     id?: number
+       *     name?: string
+       *   }
+       *   // [required]
+       *   photoUrls: string[]
+       *   tags?: Array<{
+       *     id?: number
+       *     name?: string
+       *   }>
+       *   // pet status in the store
+       *   status?: 'available' | 'pending' | 'sold'
+       * }
+       * ```
+       */
+      updatePet<
+        Config extends Alova2MethodConfig<Pet> & {
+          data: Pet;
+        },
+      >(
+        config: Config,
+      ): Alova2Method<Pet, "pet.updatePet", Config>;
+      /**
+       * ---
+       *
+       * [POST] Add a new pet to the store.
+       *
+       * **path:** /pet
+       *
+       * ---
+       *
+       * **RequestBody**
+       * ```ts
+       * type RequestBody = {
+       *   id?: number
+       *   // [required]
+       *   name: string
+       *   category?: {
+       *     id?: number
+       *     name?: string
+       *   }
+       *   // [required]
+       *   photoUrls: string[]
+       *   tags?: Array<{
+       *     id?: number
+       *     name?: string
+       *   }>
+       *   // pet status in the store
+       *   status?: 'available' | 'pending' | 'sold'
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   // [required]
+       *   name: string
+       *   category?: {
+       *     id?: number
+       *     name?: string
+       *   }
+       *   // [required]
+       *   photoUrls: string[]
+       *   tags?: Array<{
+       *     id?: number
+       *     name?: string
+       *   }>
+       *   // pet status in the store
+       *   status?: 'available' | 'pending' | 'sold'
+       * }
+       * ```
+       */
+      addPet<
+        Config extends Alova2MethodConfig<Pet> & {
+          data: Pet;
+        },
+      >(
+        config: Config,
+      ): Alova2Method<Pet, "pet.addPet", Config>;
+      /**
+       * ---
+       *
+       * [GET] Finds Pets by status.
+       *
+       * **path:** /pet/findByStatus
+       *
+       * ---
+       *
+       * **Query Parameters**
+       * ```ts
+       * type QueryParameters = {
+       *   // Status values that need to be considered for filter
+       *   status?: 'available' | 'pending' | 'sold'
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = Array<{
+       *   id?: number
+       *   // [required]
+       *   name: string
+       *   category?: {
+       *     id?: number
+       *     name?: string
+       *   }
+       *   // [required]
+       *   photoUrls: string[]
+       *   tags?: Array<{
+       *     id?: number
+       *     name?: string
+       *   }>
+       *   // pet status in the store
+       *   status?: 'available' | 'pending' | 'sold'
+       * }>
+       * ```
+       */
+      findPetsByStatus<
+        Config extends Alova2MethodConfig<Pet[]> & {
+          params: {
+            /**
+             * Status values that need to be considered for filter
+             */
+            status?: "available" | "pending" | "sold";
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<Pet[], "pet.findPetsByStatus", Config>;
+      /**
+       * ---
+       *
+       * [GET] Finds Pets by tags.
+       *
+       * **path:** /pet/findByTags
+       *
+       * ---
+       *
+       * **Query Parameters**
+       * ```ts
+       * type QueryParameters = {
+       *   // Tags to filter by
+       *   tags?: string[]
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = Array<{
+       *   id?: number
+       *   // [required]
+       *   name: string
+       *   category?: {
+       *     id?: number
+       *     name?: string
+       *   }
+       *   // [required]
+       *   photoUrls: string[]
+       *   tags?: Array<{
+       *     id?: number
+       *     name?: string
+       *   }>
+       *   // pet status in the store
+       *   status?: 'available' | 'pending' | 'sold'
+       * }>
+       * ```
+       */
+      findPetsByTags<
+        Config extends Alova2MethodConfig<Pet[]> & {
+          params: {
+            /**
+             * Tags to filter by
+             */
+            tags?: string[];
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<Pet[], "pet.findPetsByTags", Config>;
+      /**
+       * ---
+       *
+       * [GET] Find pet by ID.
+       *
+       * **path:** /pet/{petId}
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // ID of pet to return
+       *   // [required]
+       *   petId: number
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   // [required]
+       *   name: string
+       *   category?: {
+       *     id?: number
+       *     name?: string
+       *   }
+       *   // [required]
+       *   photoUrls: string[]
+       *   tags?: Array<{
+       *     id?: number
+       *     name?: string
+       *   }>
+       *   // pet status in the store
+       *   status?: 'available' | 'pending' | 'sold'
+       * }
+       * ```
+       */
+      getPetById<
+        Config extends Alova2MethodConfig<Pet> & {
+          pathParams: {
+            /**
+             * ID of pet to return
+             * [required]
+             */
+            petId: number;
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<Pet, "pet.getPetById", Config>;
+      /**
+       * ---
+       *
+       * [POST] Updates a pet in the store with form data.
+       *
+       * **path:** /pet/{petId}
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // ID of pet that needs to be updated
+       *   // [required]
+       *   petId: number
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Query Parameters**
+       * ```ts
+       * type QueryParameters = {
+       *   // Name of pet that needs to be updated
+       *   name?: string
+       *   // Status of pet that needs to be updated
+       *   status?: string
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   // [required]
+       *   name: string
+       *   category?: {
+       *     id?: number
+       *     name?: string
+       *   }
+       *   // [required]
+       *   photoUrls: string[]
+       *   tags?: Array<{
+       *     id?: number
+       *     name?: string
+       *   }>
+       *   // pet status in the store
+       *   status?: 'available' | 'pending' | 'sold'
+       * }
+       * ```
+       */
+      updatePetWithForm<
+        Config extends Alova2MethodConfig<Pet> & {
+          pathParams: {
+            /**
+             * ID of pet that needs to be updated
+             * [required]
+             */
+            petId: number;
+          };
+          params: {
+            /**
+             * Name of pet that needs to be updated
+             */
+            name?: string;
+            /**
+             * Status of pet that needs to be updated
+             */
+            status?: string;
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<Pet, "pet.updatePetWithForm", Config>;
+      /**
+       * ---
+       *
+       * [DELETE] Deletes a pet.
+       *
+       * **path:** /pet/{petId}
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // Pet id to delete
+       *   // [required]
+       *   petId: number
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = unknown
+       * ```
+       */
+      deletePet<
+        Config extends Alova2MethodConfig<unknown> & {
+          pathParams: {
+            /**
+             * Pet id to delete
+             * [required]
+             */
+            petId: number;
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<unknown, "pet.deletePet", Config>;
+      /**
+       * ---
+       *
+       * [POST] Uploads an image.
+       *
+       * **path:** /pet/{petId}/uploadImage
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // ID of pet to update
+       *   // [required]
+       *   petId: number
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Query Parameters**
+       * ```ts
+       * type QueryParameters = {
+       *   // Additional Metadata
+       *   additionalMetadata?: string
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **RequestBody**
+       * ```ts
+       * type RequestBody = Blob
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   code?: number
+       *   type?: string
+       *   message?: string
+       * }
+       * ```
+       */
+      uploadFile<
+        Config extends Alova2MethodConfig<ApiResponse> & {
+          pathParams: {
+            /**
+             * ID of pet to update
+             * [required]
+             */
+            petId: number;
+          };
+          params: {
+            /**
+             * Additional Metadata
+             */
+            additionalMetadata?: string;
+          };
+          data: Blob;
+        },
+      >(
+        config: Config,
+      ): Alova2Method<ApiResponse, "pet.uploadFile", Config>;
+    };
+    store: {
+      /**
+       * ---
+       *
+       * [GET] Returns pet inventories by status.
+       *
+       * **path:** /store/inventory
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = Record<string, number>
+       * ```
+       */
+      getInventory<Config extends Alova2MethodConfig<Record<string, number>>>(
+        config?: Config,
+      ): Alova2Method<Record<string, number>, "store.getInventory", Config>;
+      /**
+       * ---
+       *
+       * [POST] Place an order for a pet.
+       *
+       * **path:** /store/order
+       *
+       * ---
+       *
+       * **RequestBody**
+       * ```ts
+       * type RequestBody = {
+       *   id?: number
+       *   petId?: number
+       *   quantity?: number
+       *   shipDate?: string
+       *   // Order Status
+       *   status?: 'placed' | 'approved' | 'delivered'
+       *   complete?: boolean
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   petId?: number
+       *   quantity?: number
+       *   shipDate?: string
+       *   // Order Status
+       *   status?: 'placed' | 'approved' | 'delivered'
+       *   complete?: boolean
+       * }
+       * ```
+       */
+      placeOrder<
+        Config extends Alova2MethodConfig<Order> & {
+          data: Order;
+        },
+      >(
+        config: Config,
+      ): Alova2Method<Order, "store.placeOrder", Config>;
+      /**
+       * ---
+       *
+       * [GET] Find purchase order by ID.
+       *
+       * **path:** /store/order/{orderId}
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // ID of order that needs to be fetched
+       *   // [required]
+       *   orderId: number
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   petId?: number
+       *   quantity?: number
+       *   shipDate?: string
+       *   // Order Status
+       *   status?: 'placed' | 'approved' | 'delivered'
+       *   complete?: boolean
+       * }
+       * ```
+       */
+      getOrderById<
+        Config extends Alova2MethodConfig<Order> & {
+          pathParams: {
+            /**
+             * ID of order that needs to be fetched
+             * [required]
+             */
+            orderId: number;
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<Order, "store.getOrderById", Config>;
+      /**
+       * ---
+       *
+       * [DELETE] Delete purchase order by identifier.
+       *
+       * **path:** /store/order/{orderId}
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // ID of the order that needs to be deleted
+       *   // [required]
+       *   orderId: number
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = unknown
+       * ```
+       */
+      deleteOrder<
+        Config extends Alova2MethodConfig<unknown> & {
+          pathParams: {
+            /**
+             * ID of the order that needs to be deleted
+             * [required]
+             */
+            orderId: number;
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<unknown, "store.deleteOrder", Config>;
+    };
+    user: {
+      /**
+       * ---
+       *
+       * [POST] Create user.
+       *
+       * **path:** /user
+       *
+       * ---
+       *
+       * **RequestBody**
+       * ```ts
+       * type RequestBody = {
+       *   id?: number
+       *   username?: string
+       *   firstName?: string
+       *   lastName?: string
+       *   email?: string
+       *   password?: string
+       *   phone?: string
+       *   // User Status
+       *   userStatus?: number
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   username?: string
+       *   firstName?: string
+       *   lastName?: string
+       *   email?: string
+       *   password?: string
+       *   phone?: string
+       *   // User Status
+       *   userStatus?: number
+       * }
+       * ```
+       */
+      createUser<
+        Config extends Alova2MethodConfig<User> & {
+          data: User;
+        },
+      >(
+        config: Config,
+      ): Alova2Method<User, "user.createUser", Config>;
+      /**
+       * ---
+       *
+       * [POST] Creates list of users with given input array.
+       *
+       * **path:** /user/createWithList
+       *
+       * ---
+       *
+       * **RequestBody**
+       * ```ts
+       * type RequestBody = Array<{
+       *   id?: number
+       *   username?: string
+       *   firstName?: string
+       *   lastName?: string
+       *   email?: string
+       *   password?: string
+       *   phone?: string
+       *   // User Status
+       *   userStatus?: number
+       * }>
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   username?: string
+       *   firstName?: string
+       *   lastName?: string
+       *   email?: string
+       *   password?: string
+       *   phone?: string
+       *   // User Status
+       *   userStatus?: number
+       * }
+       * ```
+       */
+      createUsersWithListInput<
+        Config extends Alova2MethodConfig<User> & {
+          data: User[];
+        },
+      >(
+        config: Config,
+      ): Alova2Method<User, "user.createUsersWithListInput", Config>;
+      /**
+       * ---
+       *
+       * [GET] Logs user into the system.
+       *
+       * **path:** /user/login
+       *
+       * ---
+       *
+       * **Query Parameters**
+       * ```ts
+       * type QueryParameters = {
+       *   // The user name for login
+       *   username?: string
+       *   // The password for login in clear text
+       *   password?: string
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = string
+       * ```
+       */
+      loginUser<
+        Config extends Alova2MethodConfig<string> & {
+          params: {
+            /**
+             * The user name for login
+             */
+            username?: string;
+            /**
+             * The password for login in clear text
+             */
+            password?: string;
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<string, "user.loginUser", Config>;
+      /**
+       * ---
+       *
+       * [GET] Logs out current logged in user session.
+       *
+       * **path:** /user/logout
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = unknown
+       * ```
+       */
+      logoutUser<Config extends Alova2MethodConfig<unknown>>(
+        config?: Config,
+      ): Alova2Method<unknown, "user.logoutUser", Config>;
+      /**
+       * ---
+       *
+       * [GET] Get user by user name.
+       *
+       * **path:** /user/{username}
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // The name that needs to be fetched. Use user1 for testing
+       *   // [required]
+       *   username: string
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = {
+       *   id?: number
+       *   username?: string
+       *   firstName?: string
+       *   lastName?: string
+       *   email?: string
+       *   password?: string
+       *   phone?: string
+       *   // User Status
+       *   userStatus?: number
+       * }
+       * ```
+       */
+      getUserByName<
+        Config extends Alova2MethodConfig<User> & {
+          pathParams: {
+            /**
+             * The name that needs to be fetched. Use user1 for testing
+             * [required]
+             */
+            username: string;
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<User, "user.getUserByName", Config>;
+      /**
+       * ---
+       *
+       * [PUT] Update user resource.
+       *
+       * **path:** /user/{username}
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // name that need to be deleted
+       *   // [required]
+       *   username: string
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **RequestBody**
+       * ```ts
+       * type RequestBody = {
+       *   id?: number
+       *   username?: string
+       *   firstName?: string
+       *   lastName?: string
+       *   email?: string
+       *   password?: string
+       *   phone?: string
+       *   // User Status
+       *   userStatus?: number
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = unknown
+       * ```
+       */
+      updateUser<
+        Config extends Alova2MethodConfig<unknown> & {
+          pathParams: {
+            /**
+             * name that need to be deleted
+             * [required]
+             */
+            username: string;
+          };
+          data: User;
+        },
+      >(
+        config: Config,
+      ): Alova2Method<unknown, "user.updateUser", Config>;
+      /**
+       * ---
+       *
+       * [DELETE] Delete user resource.
+       *
+       * **path:** /user/{username}
+       *
+       * ---
+       *
+       * **Path Parameters**
+       * ```ts
+       * type PathParameters = {
+       *   // The name that needs to be deleted
+       *   // [required]
+       *   username: string
+       * }
+       * ```
+       *
+       * ---
+       *
+       * **Response**
+       * ```ts
+       * type Response = unknown
+       * ```
+       */
+      deleteUser<
+        Config extends Alova2MethodConfig<unknown> & {
+          pathParams: {
+            /**
+             * The name that needs to be deleted
+             * [required]
+             */
+            username: string;
+          };
+        },
+      >(
+        config: Config,
+      ): Alova2Method<unknown, "user.deleteUser", Config>;
+    };
+  }
+
+  var Apis: Apis;
+}

+ 18 - 0
src/api/index.ts

@@ -0,0 +1,18 @@
+// Import the core alova instance
+import alovaInstance from "./core/instance";
+
+// Export the global Apis object from the generated code
+import { createApis, withConfigType } from "./createApi";
+
+// Export the alova instance for direct use if needed
+export { alovaInstance };
+
+// Configure method options for specific APIs
+export const $$userConfigMap = withConfigType({});
+
+// Create the global Apis object
+const Apis = createApis(alovaInstance, $$userConfigMap);
+
+// Export both default and named export for AutoImport
+export default Apis;
+export { Apis };

+ 9 - 2
src/auto-imports.d.ts

@@ -22,7 +22,7 @@ declare global {
   const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
   const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
   const controlledRef: typeof import('@vueuse/core')['controlledRef']
-  const createApis: typeof import('./api/createApis')['createApis']
+  const createApis: typeof import('./api/createApi')['createApis']
   const createApp: typeof import('vue')['createApp']
   const createEventHook: typeof import('@vueuse/core')['createEventHook']
   const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
@@ -338,7 +338,7 @@ declare global {
   const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
   const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
   const whenever: typeof import('@vueuse/core')['whenever']
-  const withConfigType: typeof import('./api/createApis')['withConfigType']
+  const withConfigType: typeof import('./api/createApi')['withConfigType']
 }
 // for type re-export
 declare global {
@@ -351,9 +351,14 @@ import { UnwrapRef } from 'vue'
 declare module 'vue' {
   interface GlobalComponents {}
   interface ComponentCustomProperties {
+    readonly $$userConfigMap: UnwrapRef<typeof import('./api/index')['$$userConfigMap']>
+    readonly Apis: UnwrapRef<typeof import('./api/index')['Apis']>
     readonly CommonUtil: UnwrapRef<typeof import('wot-design-uni')['CommonUtil']>
     readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
     readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
+    readonly alovaInstance: UnwrapRef<typeof import('./api/index')['alovaInstance']>
+    readonly api: UnwrapRef<typeof import('./api/index')['default']>
+    readonly apiDefinitions: UnwrapRef<typeof import('./api/apiDefinitions')['default']>
     readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
     readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
     readonly computed: UnwrapRef<typeof import('vue')['computed']>
@@ -363,6 +368,7 @@ declare module 'vue' {
     readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
     readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
     readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
+    readonly createApis: UnwrapRef<typeof import('./api/createApi')['createApis']>
     readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
     readonly createEventHook: UnwrapRef<typeof import('@vueuse/core')['createEventHook']>
     readonly createGlobalState: UnwrapRef<typeof import('@vueuse/core')['createGlobalState']>
@@ -678,5 +684,6 @@ declare module 'vue' {
     readonly watchTriggerable: UnwrapRef<typeof import('@vueuse/core')['watchTriggerable']>
     readonly watchWithFilter: UnwrapRef<typeof import('@vueuse/core')['watchWithFilter']>
     readonly whenever: UnwrapRef<typeof import('@vueuse/core')['whenever']>
+    readonly withConfigType: UnwrapRef<typeof import('./api/createApi')['withConfigType']>
   }
 }

+ 20 - 6
src/components/course/index.vue

@@ -8,14 +8,12 @@
       class="w-200rpx h-200rpx rounded-32rpx min-w-200rpx"
     />
     <view class="ml20rpx">
-      <view class="text-#000000 text-32rpx font-semibold"
-        >一对一篮球30分钟室内精...</view
-      >
+      <view class="text-#000000 text-32rpx font-semibold">{{ item.name }}</view>
       <view class="text-[rgba(0,0,0,0.3)] text-28rpx mt-24rpx"
-        >上课地点:观山湖区第十二小学</view
+        >上课地点:{{ item.address }}</view
       >
       <view class="text-[rgba(0,0,0,0.3)] text-28rpx mt-24rpx"
-        >10课时 06.26-06.28</view
+        >{{ item.totalNum }}课时 {{ item.startTime }}-{{ item.endTime }}</view
       >
     </view>
   </view>
@@ -28,6 +26,22 @@ interface props {
    */
   type?: number;
   disabled?: boolean;
+  item: {
+    address: string;
+    categoryId: string;
+    categoryIds: string;
+    coursesStatus: number;
+    cover: string;
+    endTime: string;
+    id: string;
+    name: string;
+    originalPrice: number;
+    priceType: number;
+    rackingStatus: number;
+    sellingPrice: number;
+    startTime: string;
+    totalNum: number;
+  };
 }
 const props = defineProps<props>();
 function handleDetailes() {
@@ -40,7 +54,7 @@ function handleDetailes() {
     return;
   }
   uni.navigateTo({
-    url: `/subPack/classInspectionDetaile/index?type=${props.type}`,
+    url: `/subPack/classInspectionDetaile/index?type=${props.type}&id=${props.item.id}`,
   });
 }
 </script>

+ 1 - 1
src/components/customFormItem/index.vue

@@ -2,7 +2,7 @@
   <view class="mb24rpx formItem">
     <view class="flex items-center">
       <view class="text-#FB5B5B font-semibold" v-if="required">*</view>
-      <view class="font-semibold ml20rpx">{{ label }}</view>
+      <view class="font-semibold ml20rpx label">{{ label }}</view>
     </view>
     <view class="bg-#F6F6F6 rounded-16rpx input mt20rpx">
       <slot></slot>

+ 22 - 0
src/config/index.ts

@@ -0,0 +1,22 @@
+const mapEnvVersion = {
+  /**
+   * 	开发版
+   */
+  // develop: "http://192.168.1.166:8080/jeecg-boot",
+  // develop: "http://192.168.1.34:8080/jeecg-boot",
+  develop: "http://192.168.0.11:8080/jeecg-boot",
+  /**
+   * 	体验版
+   */
+  trial: "",
+  /**
+   * 正式版
+   */
+  release: "",
+};
+
+/**
+ * Base URL请求基本url
+ */
+export const BASE_URL =
+  mapEnvVersion[uni.getAccountInfoSync().miniProgram.envVersion];

+ 10 - 0
src/pages.json

@@ -18,6 +18,16 @@
         "navigationBarTitleText": "关于",
         "navigationStyle": "custom"
       }
+    },
+    {
+      "path": "pages/login/index",
+      "type": "page",
+      "name": "login",
+      "style": {
+        "navigationBarTitleText": "账户密码登录",
+        "navigationStyle": "custom",
+        "disableScroll": true
+      }
     }
   ],
   "globalStyle": {

+ 13 - 7
src/pages/index/index.vue

@@ -1,15 +1,15 @@
 <script setup lang="ts">
+import router from "@/router";
+
 const { statusBarHeight } = uni.getSystemInfoSync();
-const { isLogin } = storeToRefs(useUserStore());
+const { token, userInfo } = storeToRefs(useUserStore());
 const option1 = ref<Record<string, any>[]>([
   { label: "选择场馆", value: 0 },
   { label: "选择场馆1", value: 1 },
   { label: "选择场馆1", value: 2 },
 ]);
 function handleCommenPath(url: string) {
-  uni.navigateTo({
-    url: url,
-  });
+  router.push(url);
 }
 function handleScanCode() {
   // uni.scanCode({
@@ -28,12 +28,16 @@ const value = ref<number>(0);
     :style="{ paddingTop: `${statusBarHeight}px` }"
   >
     <view class="h-58rpx"></view>
-    <view class="px-32rpx mt-48rpx" v-if="!isLogin">
+    <view class="px-32rpx mt-48rpx" v-if="!token">
       <view
         class="bg-white rounded-16rpx flex items-center justify-between py-20rpx px-24rpx"
       >
         <view>您还未登录,快去登录吧!</view>
-        <commonbtn bg-color="#0074FF">去登录</commonbtn>
+        <commonbtn
+          bg-color="#0074FF"
+          @click="handleCommenPath('/pages/login/index')"
+          >去登录</commonbtn
+        >
       </view>
     </view>
     <view class="relative">
@@ -43,7 +47,9 @@ const value = ref<number>(0);
       <view class="absolute top-50% -translate-y-50% right-50rpx">
         <view class="flex items-center">
           <image src="@/static/index/user.png" class="w-40rpx h-40rpx"></image>
-          <view class="text-32rpx font-semibold ml-3">xxxx</view>
+          <view class="text-32rpx font-semibold ml-3">{{
+            userInfo.realname
+          }}</view>
         </view>
       </view>
     </view>

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

@@ -0,0 +1,130 @@
+<template>
+  <view class="header-top h-screen">
+    <wd-navbar
+      title="账户密码登录"
+      fixed
+      placeholder
+      safeAreaInsetTop
+      :bordered="false"
+      leftArrow
+      @click-left="handleClickLeft"
+      custom-style="background-color: transparent !important"
+    ></wd-navbar>
+    <view class="flex items-center justify-center mt114rpx">
+      <view class="flex flex-col items-center">
+        <view
+          class="bg-white rounded-full w160rpx h160rpx flex items-center justify-center"
+          >logo</view
+        >
+        <view class="text-40rpx mt-32rpx text-[rgb(0,0,0,.9))]"
+          >全龄运动商家端</view
+        >
+      </view>
+    </view>
+    <view class="px32rpx mt68rpx">
+      <customFormItem label="账号">
+        <wd-input
+          type="text"
+          placeholder="请输入账号"
+          clearable
+          v-model:model-value="modelForm.username"
+          custom-class="inputClass"
+        />
+      </customFormItem>
+      <customFormItem label="密码">
+        <wd-input
+          show-password
+          placeholder="请输入密码"
+          custom-class="inputClass"
+          clearable
+          v-model:model-value="modelForm.password"
+        />
+      </customFormItem>
+      <view class="mt94rpx">
+        <wd-button type="primary" size="large" block @click="handleLogin"
+          >提交
+        </wd-button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { createGlobalLoadingMiddleware } from "@/api/core/middleware";
+
+const {
+  data: loginData,
+  loading: loginLoading,
+  error: loginError,
+  send: performLogin,
+} = useRequest(
+  (username: string, password: string) =>
+    Apis.user.loginUser({
+      data: {
+        username,
+        password,
+      },
+    }),
+
+  {
+    immediate: false,
+    middleware: createGlobalLoadingMiddleware({
+      loadingText: "登录中...",
+    }),
+  },
+).onError((error) => {});
+
+const modelForm = reactive({
+  username: "测试张涛",
+  password: "Qwer123456.",
+});
+function handleClickLeft() {
+  uni.navigateBack();
+}
+async function handleLogin() {
+  if (!modelForm.username || !modelForm.password) {
+    return uni.showToast({
+      title: "请填写完整",
+      icon: "none",
+    });
+  }
+  const res = await performLogin(modelForm.username, modelForm.password);
+  useUserStore().handleLogin(res.result.userInfo, res.result.token);
+  uni.showToast({
+    title: res.message,
+    icon: "none",
+    duration: 2000,
+    mask: true,
+  });
+  setTimeout(() => {
+    handleClickLeft();
+  }, 2000);
+}
+</script>
+
+<style scoped lang="scss">
+:deep(.wd-input.is-not-empty:not(.is-disabled)::after) {
+  background-color: var(--wot-input-border-color, #dadada) !important;
+}
+.header-top {
+  background: linear-gradient(180deg, #e4efff 0%, #f6f6f6 29%);
+}
+:deep(.label) {
+  font-size: 32rpx;
+  color: rgba(0, 0, 0, 0.9);
+  font-weight: normal;
+}
+:deep(.input) {
+  margin-top: 0rpx !important;
+}
+</style>
+<route lang="json">
+{
+  "name": "login",
+  "style": {
+    "navigationBarTitleText": "账户密码登录",
+    "navigationStyle": "custom",
+    "disableScroll": true
+  }
+}
+</route>

+ 35 - 60
src/router/index.ts

@@ -1,79 +1,54 @@
 /// <reference types="@uni-helper/vite-plugin-uni-pages/client" />
-import { pages, subPackages } from 'virtual:uni-pages'
+import { pages, subPackages } from "virtual:uni-pages";
 
 function generateRoutes() {
   const routes = pages.map((page) => {
-    const newPath = `/${page.path}`
-    return { ...page, path: newPath }
-  })
+    const newPath = `/${page.path}`;
+    return { ...page, path: newPath };
+  });
   if (subPackages && subPackages.length > 0) {
     subPackages.forEach((subPackage) => {
       const subRoutes = subPackage.pages.map((page: any) => {
-        const newPath = `/${subPackage.root}/${page.path}`
-        return { ...page, path: newPath }
-      })
-      routes.push(...subRoutes)
-    })
+        const newPath = `/${subPackage.root}/${page.path}`;
+        return { ...page, path: newPath };
+      });
+      routes.push(...subRoutes);
+    });
   }
-  return routes
+  return routes;
 }
 
 const router = createRouter({
   routes: generateRoutes(),
-})
-router.beforeEach((to, from, next) => {
-  console.log('🚀 beforeEach 守卫触发:', { to, from })
-
-  // 演示:基本的导航日志记录
-  if (to.path && from.path) {
-    console.log(`📍 导航: ${from.path} → ${to.path}`)
-  }
+});
+const writePath = ["index", "login"];
 
-  // 演示:对受保护页面的简单拦截
-  if (to.name === 'demo-protected') {
-    const { confirm: showConfirm } = useGlobalMessage()
-    console.log('🛡️ 检测到访问受保护页面')
-
-    return new Promise<void>((resolve, reject) => {
-      showConfirm({
-        title: '守卫拦截演示',
-        msg: '这是一个受保护的页面,需要确认后才能访问',
-        confirmButtonText: '允许访问',
-        cancelButtonText: '取消',
-        success() {
-          console.log('✅ 用户确认访问,允许导航')
-          next()
-          resolve()
-        },
-        fail() {
-          console.log('❌ 用户取消访问,阻止导航')
-          next(false)
-          reject(new Error('用户取消访问'))
-        },
-      })
-    })
+router.beforeEach((to, from, next) => {
+  console.log("🚀 beforeEach 守卫触发:", { to, from });
+  const { token } = useUserStore();
+  if (!writePath.includes(String(to.name)) && !token) {
+    uni.showToast({ title: "请先登录", icon: "none" });
+    next(false);
   }
-
   // 继续导航
-  next()
-})
-
+  next();
+});
 router.afterEach((to, from) => {
-  console.log('🎯 afterEach 钩子触发:', { to, from })
+  console.log("🎯 afterEach 钩子触发:", { to, from });
 
-  // 演示:简单的页面切换记录
-  if (to.path) {
-    console.log(`📄 页面切换完成: ${to.path}`)
-  }
+  // // 演示:简单的页面切换记录
+  // if (to.path) {
+  //   console.log(`📄 页面切换完成: ${to.path}`);
+  // }
 
-  // 演示:针对 afterEach 演示页面的简单提示
-  if (to.name === 'demo-aftereach') {
-    const { show: showToast } = useGlobalToast()
-    console.log('📊 进入 afterEach 演示页面')
-    setTimeout(() => {
-      showToast('afterEach 钩子已触发!')
-    }, 500)
-  }
-})
+  // // 演示:针对 afterEach 演示页面的简单提示
+  // if (to.name === "demo-aftereach") {
+
+  //   console.log("📊 进入 afterEach 演示页面");
+  //   setTimeout(() => {
+  //     showToast("afterEach 钩子已触发!");
+  //   }, 500);
+  // }
+});
 
-export default router
+export default router;

+ 77 - 3
src/store/user.ts

@@ -1,12 +1,86 @@
 import { defineStore } from "pinia";
+interface user {
+  activitiSync: number;
+  avatar: string;
+  birthday: string;
+  bpmStatus: string;
+  certificateInnocence: string;
+  clientId: string;
+  createBy: string;
+  createTime: string;
+  delFlag: number;
+  departIds: string;
+  email: string;
+  esignAuth: string;
+  healthy: string;
+  homePath: string;
+  honor: string;
+  id: string;
+  izBindThird: string;
+  loginTenantId: number;
+  openid: null;
+  orgCode: string;
+  orgCodeTxt: null;
+  phone: string;
+  post: string;
+  postText: string;
+  realname: string;
+  relTenantIds: string;
+  sex: string;
+  status: 1;
+  telephone: string;
+  userIdentity: number;
+  username: string;
+  workNo: string;
+}
 interface UserState {
-  isLogin: boolean;
+  token: string;
+  userInfo: user;
 }
 export const useUserStore = defineStore({
   id: "app-user",
   state: (): UserState => ({
-    isLogin: false,
+    userInfo: {
+      activitiSync: 0,
+      avatar: "",
+      birthday: "",
+      bpmStatus: "",
+      certificateInnocence: "",
+      clientId: "",
+      createBy: "",
+      createTime: "",
+      delFlag: 0,
+      departIds: "",
+      email: "",
+      esignAuth: "",
+      healthy: "",
+      homePath: "",
+      honor: "",
+      id: "",
+      izBindThird: "",
+      loginTenantId: 0,
+      openid: null,
+      orgCode: "",
+      orgCodeTxt: null,
+      phone: "",
+      post: "",
+      postText: "",
+      realname: "",
+      relTenantIds: "",
+      sex: "",
+      status: 1,
+      telephone: "",
+      userIdentity: 0,
+      username: "",
+      workNo: "",
+    },
+    token: "",
   }),
   getters: {},
-  actions: {},
+  actions: {
+    handleLogin(userInfo: user, token: string) {
+      this.userInfo = userInfo;
+      this.token = token;
+    },
+  },
 });

+ 43 - 3
src/subPack/classInspection/index.vue

@@ -9,21 +9,61 @@
     @click-left="handleClickLeft"
   ></wd-navbar>
   <view class="px32rpx py-24rpx">
-    <view v-for="item in 20" class="mb24rpx">
-      <course :type="type"></course>
+    <view v-for="item in data" class="mb24rpx">
+      <course :type="type" :item="item"></course>
     </view>
   </view>
 </template>
 
 <script setup lang="ts">
 const type = ref(0);
-const titleArr = ["拍照验课", "选择课程", "选择课程延期",'选择课程'];
+const titleArr = ["拍照验课", "选择课程", "选择课程延期", "选择课程"];
 onLoad((query: any) => {
   type.value = query.type;
 });
+const {
+  loading,
+
+  // 列表数据
+  data,
+
+  // 是否为最后一页
+  // 下拉加载时可通过此参数判断是否还需要加载
+  isLastPage,
+
+  // 当前页码,改变此页码将自动触发请求
+  page,
+
+  // 每页数据条数
+  pageSize,
+
+  // 分页页数
+  pageCount,
+
+  // 总数据量
+  total,
+} = usePagination(
+  (page, pageSize) =>
+    Apis.courses.list({
+      data: { page, pageSize },
+    }),
+
+  {
+    data: (resp) => resp.result.records,
+    initialPage: 1, // 初始页码,默认为1
+    initialPageSize: 10, // 初始每页数据条数,默认为10
+  },
+).onError((error) => {});
+
 function handleClickLeft() {
   uni.navigateBack();
 }
+onReachBottom(() => {
+  console.log(total.value, "页面触底", data.value.length);
+  if (total.value > data.value.length) {
+    page.value++;
+  }
+});
 </script>
 
 <style scoped></style>

+ 19 - 1
src/subPack/classInspectionDetaile/index.vue

@@ -9,7 +9,7 @@
     @click-left="handleClickLeft"
   ></wd-navbar>
   <view class="px32rpx py-24rpx">
-    <course disabled></course>
+    <course disabled :item="courses"></course>
     <view class="mt24rpx flex items-center">
       <image src="@/subPack/static/nz.png" class="w-30rpx h30rpx"></image>
       <view class="text-32rpx ml-3 font-semibold">今日上课</view>
@@ -48,9 +48,27 @@ import img1 from "@/subPack/static/yq.png";
 const tab = ref(0);
 const type = ref(0);
 const titleArr = ["拍照验课", "选择课程", "选择延期课时"];
+const courses = ref();
 onLoad((query: any) => {
   type.value = query.type;
+  getDataDetaile(query.id);
 });
+const { send: getData } = useRequest(
+  (id: string) =>
+    Apis.courses.getCourseDetail({
+      data: {
+        id,
+      },
+    }),
+
+  {
+    immediate: false,
+  },
+).onError((error) => {});
+async function getDataDetaile(id: string) {
+  const res = await getData(id);
+  courses.value = res.result.courses;
+}
 function handleGoView() {
   tab.value = 1;
   uni.pageScrollTo({ selector: ".view" });

+ 1 - 0
src/uni-pages.d.ts

@@ -6,6 +6,7 @@
 interface NavigateToOptions {
   url: "/pages/index/index" |
        "/pages/about/index" |
+       "/pages/login/index" |
        "/subPack/classInspection/index" |
        "/subPack/classInspectionDetaile/index" |
        "/subPack/EmployeeList/index" |