如何处理管理系统中(Vue PC + uni-app 移动端):业务逻辑复用基本方案

本文主要从以下几个方面入手:

  1. API请求、

  2. 状态管理、

  3. 工具函数

  4. 路由

  5. 包管理工具

一、整体设计:分层解耦,复用优先

核心思路是 "业务逻辑抽离为公共层,两端仅保留平台特有逻辑" ,整体架构分为 5 层,从下到上分别为:

bash 复制代码
├─ 1. 基础工具层(utils):纯函数工具,两端完全复用
├─ 2. API 通信层(api):统一请求逻辑,适配两端差异
├─ 3. 状态管理层(store):核心业务状态,两端共享
├─ 4. 业务逻辑层(services):封装业务方法,两端复用
└─ 5. 视图层(components/views):平台特有组件/页面,差异化实现

核心原则

  1. 公共逻辑 "上提" :API、状态、工具函数等无平台依赖的逻辑,抽离到独立包 / 目录,避免两端重复编写;
  2. 平台差异 "下沉" :UI 组件、路由、原生能力(如扫码、推送)等平台特有逻辑,在两端单独实现,通过 "适配层" 对接公共逻辑;
  3. 依赖统一管理:统一包管理工具(如 npm),公共依赖(如 axios、lodash)在两端共享版本,避免兼容性问题。

二、具体技术方案:分层实现复用

1. 基础工具层(utils):100% 复用,纯函数设计

核心目标

提供无副作用的纯函数工具,覆盖格式化、验证、计算等通用能力,两端完全复用。

实现方案

  • 目录结构 :在项目根目录创建 packages/utils(或独立 npm 包),按功能分类:

    bash 复制代码
    utils/
    ├─ format/:时间格式化(dateFormat)、金额格式化(moneyFormat)
    ├─ validate/:手机号验证(isPhone)、邮箱验证(isEmail)、表单规则(formRules)
    ├─ compute/:分页计算(calcPageInfo)、权限判断(hasPermission)
    └─ common/:深拷贝(deepClone)、防抖节流(debounce/throttle)
  • 代码示例(时间格式化工具):

    javascript 复制代码
    // packages/utils/format/dateFormat.js
    export const dateFormat = (date, format = 'YYYY-MM-DD HH:mm:ss') => {
      // 纯函数实现,无平台依赖
      const dt = new Date(date);
      const options = {
        'YYYY': dt.getFullYear(),
        'MM': String(dt.getMonth() + 1).padStart(2, '0'),
        // ... 其他格式化逻辑
      };
      return Object.entries(options).reduce((res, [key, val]) => res.replace(key, val), format);
    };
  • 复用方式 :两端通过 import { dateFormat } from '@/utils/format/dateFormat' 直接引入,无需修改。

2. API 通信层(api):统一请求逻辑,适配两端差异

核心目标

封装 API 请求逻辑(请求拦截、响应拦截、错误处理),统一接口调用方式,仅适配两端的请求库差异(Vue 用 axios,uni-app 用 uni.request)。

实现方案

  • 分层设计:分为 "基础请求适配层" 和 "业务接口层",前者处理平台差异,后者纯业务逻辑复用:

    vbscript 复制代码
    api/
    ├─ request/:基础请求适配(区分 Vue/uni-app)
    │  ├─ index.js:入口文件(根据环境导出对应请求实例)
    │  ├─ webRequest.js:Vue 端(基于 axios)
    │  └─ uniRequest.js:uni-app 端(基于 uni.request)
    └─ modules/:业务接口(两端完全复用)
       ├─ user.js:用户相关(登录、权限)
       ├─ order.js:订单相关(列表、详情)
       └─ goods.js:商品相关(新增、编辑)
  • 关键实现:请求适配层(处理平台差异):

    javascript 复制代码
    // api/request/webRequest.js(Vue 端)
    import axios from 'axios';
    const service = axios.create({
      baseURL: import.meta.env.VITE_API_BASE_URL, // PC 端环境变量
      timeout: 10000
    });
    // 请求拦截:添加 token
    service.interceptors.request.use(config => {
      config.headers.token = localStorage.getItem('token');
      return config;
    });
    // 响应拦截:统一错误处理
    service.interceptors.response.use(
      res => res.data,
      err => { /* 统一错误提示(如 Element Plus Message) */ }
    );
    export default service;
    
    // api/request/uniRequest.js(uni-app 端)
    export default function uniRequest(config) {
      return new Promise((resolve, reject) => {
        uni.request({
          url: config.baseURL || import.meta.env.VITE_API_BASE_URL, // 移动端环境变量
          method: config.method || 'GET',
          data: config.data,
          header: { token: uni.getStorageSync('token') }, // uni-app 存储 API
          success: res => resolve(res.data),
          fail: err => { /* 统一错误提示(如 uni.showToast) */ }
        });
      });
    }
    
    // api/request/index.js(入口:自动适配环境)
    let request;
    if (process.env.VUE_APP_PLATFORM === 'web') {
      request = import('./webRequest').then(m => m.default);
    } else {
      request = import('./uniRequest').then(m => m.default);
    }
    export default request;
  • 业务接口层(纯逻辑复用,无平台依赖):

    javascript 复制代码
    // api/modules/user.js(两端完全复用)
    import request from '../request';
    
    // 登录接口
    export const login = (params) => request({
      url: '/api/user/login',
      method: 'POST',
      data: params
    });
    
    // 获取用户权限列表
    export const getUserPermissions = () => request({
      url: '/api/user/permissions',
      method: 'GET'
    });

3. 状态管理层(store):核心业务状态共享

核心目标

用 Vuex/Pinia 管理全局状态(如用户信息、权限、全局配置),两端共享状态定义和 mutations/actions,仅适配平台特有存储(如 token 存储)。

实现方案

  • 技术选型:优先用 Pinia(Vue 3 推荐,支持 TypeScript,更轻量,如果项目的搭建时不要求ts,推荐vuex),两端共用 Pinia 实例。

  • 目录结构

    复制代码
    store/
    ├─ index.js:Pinia 实例创建(适配两端存储)
    ├─ modules/
    │  ├─ userStore.js:用户状态(登录、权限)
    │  ├─ appStore.js:应用配置(主题、语言)
    │  └─ orderStore.js:订单状态(待办数量、筛选条件)
  • 关键实现:适配两端存储

    javascript 复制代码
    // store/index.js(Pinia 实例创建)
    import { createPinia } from 'pinia';
    import { createPersistedState } from 'pinia-plugin-persistedstate'; // 持久化插件
    
    const pinia = createPinia();
    
    // 适配两端持久化存储:Vue 用 localStorage,uni-app 用 uniStorage
    const storage = process.env.VUE_APP_PLATFORM === 'web' 
      ? window.localStorage 
      : {
          getItem: uni.getStorageSync,
          setItem: uni.setStorageSync,
          removeItem: uni.removeStorageSync
        };
    
    // 安装持久化插件,指定存储方式
    pinia.use(createPersistedState({
      storage: {
        getItem: (key) => storage.getItem(key),
        setItem: (key, value) => storage.setItem(key, value),
        removeItem: (key) => storage.removeItem(key)
      }
    }));
    
    export default pinia;
  • 业务状态示例(用户状态,两端复用):

    javascript 复制代码
    // store/modules/userStore.js
    import { defineStore } from 'pinia';
    import { login, getUserPermissions } from '@/api/modules/user';
    import { hasPermission } from '@/utils/compute/permission';
    
    export const useUserStore = defineStore('user', {
      state: () => ({
        token: '',
        info: {}, // 用户信息
        permissions: [] // 权限列表
      }),
      actions: {
        // 登录:业务逻辑完全复用
        async loginAction(params) {
          const res = await login(params);
          this.token = res.token;
          await this.getPermissionsAction(); // 登录后获取权限
        },
        // 获取权限:业务逻辑完全复用
        async getPermissionsAction() {
          const res = await getUserPermissions();
          this.permissions = res.list;
        },
        // 权限判断:复用工具函数
        hasPerm(perm) {
          return hasPermission(this.permissions, perm);
        }
      },
      persist: true // 持久化状态(适配两端存储)
    });

4. 业务逻辑层(services):封装复杂业务流程

核心目标

将跨组件的复杂业务流程(如表单提交、数据导出、批量操作)抽离为 service 方法,两端复用业务逻辑,仅调用平台特有 UI 交互(如弹窗、加载提示)。

实现方案

  • 目录结构:按业务模块分类,每个 service 方法接收 "平台适配回调" 处理 UI 差异:

    plaintext

    复制代码
    services/
    ├─ userService.js:用户相关(密码重置、信息修改)
    ├─ orderService.js:订单相关(批量审核、导出订单)
    └─ formService.js:表单相关(复杂表单提交、数据校验)
  • 代码示例(订单批量审核,适配两端 UI):

    javascript 复制代码
    // services/orderService.js
    import { useOrderStore } from '@/store/modules/orderStore';
    import { batchAuditOrder } from '@/api/modules/order';
    
    /**
     * 批量审核订单
     * @param {Array} orderIds - 订单ID列表
     * @param {Function} loadingCallback - 平台加载提示(如 Vue 的 ElLoading、uni 的 showLoading)
     * @param {Function} successCallback - 平台成功提示(如 ElMessage、uni.showToast)
     */
    export const batchAuditOrderService = async (orderIds, loadingCallback, successCallback) => {
      const orderStore = useOrderStore();
      // 1. 调用平台加载提示(通过回调适配)
      const closeLoading = loadingCallback();
      
      try {
        // 2. 业务逻辑:调用 API + 更新状态(两端复用)
        await batchAuditOrder({ ids: orderIds });
        await orderStore.getOrderListAction(); // 重新获取订单列表
        // 3. 调用平台成功提示(通过回调适配)
        successCallback('审核成功');
      } catch (err) {
        throw err; // 抛错让调用方处理(如统一错误提示)
      } finally {
        closeLoading(); // 关闭加载提示
      }
    };
  • 两端调用示例

    javascript 复制代码
    // Vue PC 端调用
    import { batchAuditOrderService } from '@/services/orderService';
    import { ElLoading, ElMessage } from 'element-plus';
    
    const handleBatchAudit = async () => {
      await batchAuditOrderService(
        selectedIds,
        () => ElLoading.service({ text: '审核中...' }), // PC 加载提示
        (msg) => ElMessage.success(msg) // PC 成功提示
      );
    };
    
    // uni-app 移动端调用
    import { batchAuditOrderService } from '@/services/orderService';
    
    const handleBatchAudit = async () => {
      await batchAuditOrderService(
        selectedIds,
        () => { // 移动端加载提示
          uni.showLoading({ title: '审核中...' });
          return () => uni.hideLoading(); // 返回关闭方法
        },
        (msg) => uni.showToast({ title: msg, icon: 'success' }) // 移动端成功提示
      );
    };

5. 视图层:差异化实现,复用公共组件

核心目标

UI 组件和页面因平台交互差异(PC 用鼠标 / 键盘,移动端用触摸)需单独实现,但可抽离 "无交互纯展示组件"(如数据卡片、空状态)两端复用。

实现方案

  • 公共 UI 组件 :在 components/common 目录创建纯展示组件(无平台依赖),如:

    css 复制代码
    components/
    ├─ common/:两端复用组件
    │  ├─ EmptyState.vue:空状态(无数据提示)
    │  ├─ DataCard.vue:数据卡片(展示统计数据)
    │  └─ TablePagination.vue:分页控件(适配两端表格)
    ├─ web/:Vue PC 特有组件(如 ElTable 封装)
    └─ uni/:uni-app 特有组件(如 uni-table 封装)
  • 页面差异化:两端页面目录分开,但复用公共业务逻辑:

    ruby 复制代码
    // Vue PC 端页面
    views/web/order/OrderList.vue:用 Element Plus 组件,调用 orderService
    // uni-app 移动端页面
    pages/uni/order/OrderList.vue:用 uni-app 组件,调用 same orderService

三、工程化保障:统一规范,降低维护成本

1. 环境配置统一

  • .env 文件统一管理环境变量(API 地址、环境标识),两端共享变量名:

    ini 复制代码
    // Vue PC 端 .env.development
    VITE_API_BASE_URL = 'https://pc-api.xxx.com'
    VITE_APP_PLATFORM = 'web'
    
    // uni-app 端 .env.development
    VITE_API_BASE_URL = 'https://mobile-api.xxx.com'
    VITE_APP_PLATFORM = 'uni'

2. 代码规范统一

  • 用 ESLint + Prettier 统一代码风格,两端共用配置文件(.eslintrc.js.prettierrc);
  • 用 Husky + lint-staged 做提交校验,避免不规范代码提交。

3. 构建部署统一

  • Vue PC 端:用 Vite 构建,部署到 Web 服务器(如 Nginx);
  • uni-app 端:用 HBuilderX 或 CLI 构建,发布为微信小程序 / APP;
  • 公共层(utils/api/store)可打包为 npm 私有包,两端通过 npm 安装,避免代码复制。
相关推荐
LabVIEW开发8 小时前
LabVIEW QMH 队列消息处理架构
架构·labview·labview知识·labview功能·labview程序
代码搬运媛8 小时前
Jest 测试框架详解与实现指南
前端
counterxing9 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
wangqiaowq9 小时前
windows下nginx的安装
linux·服务器·前端
rising start9 小时前
二、全面理解MySQL架构
mysql·架构
之歆9 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜10 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
麦客奥德彪10 小时前
Android Skills
架构·ai编程
Maimai1080810 小时前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly
candyTong10 小时前
Claude Code 的 Edit 工具是怎么工作的
javascript·后端·架构