Name Last Update
.vercel Loading commit data...
config Loading commit data...
src Loading commit data...
.editorconfig Loading commit data...
.eslintrc Loading commit data...
.gitignore Loading commit data...
README.md Loading commit data...
babel.config.js Loading commit data...
components.d.ts Loading commit data...
package.json Loading commit data...
payment-status-update-guide.md Loading commit data...
permission-fix-guide.md Loading commit data...
pnpm-lock.yaml Loading commit data...
postcss.config.js Loading commit data...
project.config.json Loading commit data...
project.tt.json Loading commit data...
tailwind.config.js Loading commit data...
yarn.lock Loading commit data...

捡个电驴 - Taro4 微信小程序项目

项目介绍

基于Taro4框架开发的微信小程序,主要功能包括二手电动车交易、用户认证、订单管理、消息通知等。项目采用Vue3 + Composition API + Pinia状态管理的现代化开发方式。

技术栈

  • 框架: Taro4 + Vue3 (Composition API)
  • UI组件库: NutUI4 + @nutui/icons-vue-taro
  • 状态管理: Pinia
  • 样式: Less + TailwindCSS
  • 网络请求: axios-miniprogram
  • 包管理: pnpm
  • 构建工具: Webpack5

项目结构

src/
├── api/                    # API接口层
│   ├── index.js           # 用户相关API
│   ├── car.js             # 车辆相关API
│   ├── orders.js          # 订单相关API
│   ├── chat.js            # 聊天相关API
│   ├── common.js          # 通用API
│   ├── other.js           # 其他API
│   ├── fn.js              # API请求封装函数
│   └── wx/                # 微信相关API
├── assets/                # 静态资源
│   ├── images/           # 图片资源
│   └── styles/           # 全局样式
├── components/            # 全局组件
│   ├── MessageDetail.vue  # 消息详情组件
│   ├── TabBar.vue        # 底部导航栏
│   ├── navBar.vue        # 顶部导航栏
│   └── ...
├── pages/                 # 页面文件
│   ├── index/            # 首页
│   ├── messages/         # 消息页面
│   ├── profile/          # 个人中心
│   ├── collectionSettings/ # 收款设置
│   └── ...
├── stores/               # 状态管理
│   ├── user.js          # 用户状态
│   ├── router.js        # 路由状态
│   └── ...
├── utils/                # 工具函数
│   ├── request.js       # 网络请求配置
│   ├── config.js        # 环境配置
│   ├── permission.js    # 权限管理
│   ├── tools.js         # 通用工具函数
│   └── ...
├── app.config.js         # 应用配置
├── app.js               # 应用入口
└── app.less             # 全局样式

快速开始

1. 安装依赖

# 推荐使用pnpm
pnpm install

# 或使用npm
npm install

2. 开发环境运行

# 微信小程序开发
npm run dev:weapp

# H5开发
npm run dev:h5

3. 生产环境构建

# 构建微信小程序
npm run build:weapp

# 构建H5
npm run build:h5

API开发规范

API文件组织结构

项目采用模块化的API组织方式,按功能模块划分API文件:

  • api/index.js - 用户相关API(注册、登录、个人信息等)
  • api/car.js - 车辆相关API(发布、编辑、列表、详情等)
  • api/orders.js - 订单相关API(创建、查询、状态更新等)
  • api/chat.js - 聊天消息相关API
  • api/common.js - 通用API(上传、地区数据等)
  • api/other.js - 其他业务API
  • api/fn.js - API请求封装函数

API编写规范

1. API接口定义

// 定义API端点常量
const Api = {
  ADD_VEHICLE: '/srv/?a=vehicle&t=add',
  EDIT_VEHICLE: '/srv/?a=vehicle&t=edit',
  LIST_VEHICLE: '/srv/?a=vehicle&t=list',
}

/**
 * @description: 添加车辆
 * @param {string} title - 标题
 * @param {string} brand - 品牌
 * @param {string} model - 型号
 * @param {number} price - 出让价格
 * @returns {Promise} API响应结果
 */
export const addVehicleAPI = (params) => fn(fetch.post(Api.ADD_VEHICLE, params));

2. 请求封装函数使用

项目使用统一的请求封装函数 fn() 来处理API响应:

import { fn, fetch } from './fn';

// GET请求
export const getProfileAPI = (params) => fn(fetch.get(Api.GET_PROFILE, params));

// POST请求
export const updateProfileAPI = (params) => fn(fetch.post(Api.UPDATE_PROFILE, params));

3. 错误处理机制

fn() 函数处理API响应并返回标准格式:

  • 返回格式:{ code, msg, data }
  • code === 1 表示请求成功,其他值表示失败
  • msg 包含服务器返回的提示信息
  • data 包含具体的业务数据
  • 特殊处理"计全付"相关错误,使用模态框显示

4. API使用示例

在组件中调用API
<script setup>
import { ref, onMounted } from 'vue';
import { getProfileAPI, updateProfileAPI } from '@/api/index';
import { getVehicleListAPI, addVehicleAPI } from '@/api/car';
import { getOrderListAPI } from '@/api/orders';

// 响应式数据
const userInfo = ref({});
const vehicleList = ref([]);
const orderList = ref([]);
const loading = ref(false);

// 获取用户信息
const fetchUserProfile = async () => {
    loading.value = true;
    try {
        const { code, msg, data } = await getProfileAPI();
        if (code === 1) {
            userInfo.value = data;
            console.log('用户信息获取成功:', data);
        } else {
            console.error('获取用户信息失败:', msg);
        }
    } catch (error) {
        console.error('获取用户信息异常:', error);
    } finally {
        loading.value = false;
    }
};

// 更新用户资料
const updateProfile = async (profileData) => {
    const params = {
        nickname: profileData.nickname,
        avatar_url: profileData.avatar_url,
        gender: profileData.gender
    };

    const { code, msg, data } = await updateProfileAPI(params);
    if (code === 1) {
        console.log('资料更新成功:', msg);
        // 重新获取用户信息
        await fetchUserProfile();
    }
};

// 获取车辆列表
const fetchVehicleList = async () => {
    const params = {
        page: 1,
        limit: 10,
        status: 1 // 1-在售 2-已售
    };

    const { code, msg, data } = await getVehicleListAPI(params);
    if (code === 1) {
        vehicleList.value = data.list;
        console.log('车辆列表:', data);
    }
};

// 添加车辆
const addVehicle = async (vehicleData) => {
    const params = {
        brand_id: vehicleData.brand_id,
        model_id: vehicleData.model_id,
        price: vehicleData.price,
        description: vehicleData.description,
        images: vehicleData.images, // 图片数组
        contact_phone: vehicleData.contact_phone
    };

    const { code, msg, data } = await addVehicleAPI(params);
    if (code === 1) {
        console.log('车辆添加成功:', msg);
        // 刷新车辆列表
        await fetchVehicleList();
    }
};

// 获取订单列表
const fetchOrderList = async (status = '') => {
    const params = {
        page: 1,
        limit: 20
    };

    if (status) {
        params.status = status; // 订单状态筛选
    }

    const { code, msg, data } = await getOrderListAPI(params);
    if (code === 1) {
        orderList.value = data.list;
        console.log('订单列表:', data);
    }
};

// 页面加载时获取数据
onMounted(() => {
    fetchUserProfile();
    fetchVehicleList();
    fetchOrderList();
});
</script>
带错误处理的API调用
// 完整的错误处理示例
const handleApiCall = async (apiFunction, params, successMessage = '') => {
    loading.value = true;

    try {
        const { code, msg, data } = await apiFunction(params);

        if (code === 1) {
            // API调用成功
            if (successMessage) {
                // 显示成功提示
                Taro.showToast({
                    title: successMessage,
                    icon: 'success'
                });
            }
            return { code, msg, data };
        } else {
            // API返回失败,显示服务器返回的错误信息
            Taro.showToast({
                title: msg || '操作失败',
                icon: 'none'
            });
            return false;
        }
    } catch (error) {
        // 网络错误或其他异常
        console.error('API调用异常:', error);
        Taro.showToast({
            title: '网络异常,请稍后重试',
            icon: 'none'
        });
        return false;
    } finally {
        loading.value = false;
    }
};

// 使用示例
const saveProfile = async () => {
    const result = await handleApiCall(
        updateProfileAPI,
        { nickname: '新昵称' },
        '资料保存成功'
    );

    if (result) {
        // 处理成功后的逻辑
        await fetchUserProfile();
    }
};
分页数据加载示例
// 分页加载车辆列表
const vehicleListData = ref({
    list: [],
    page: 1,
    limit: 10,
    total: 0,
    hasMore: true
});

// 加载车辆列表(支持分页和下拉刷新)
const loadVehicleList = async (refresh = false) => {
    if (refresh) {
        vehicleListData.value.page = 1;
        vehicleListData.value.list = [];
        vehicleListData.value.hasMore = true;
    }

    if (!vehicleListData.value.hasMore) return;

    const params = {
        page: vehicleListData.value.page,
        limit: vehicleListData.value.limit,
        status: 1
    };

    const { code, msg, data } = await getVehicleListAPI(params);
    if (code === 1) {
        const { list, total } = data;

        if (refresh) {
            vehicleListData.value.list = list;
        } else {
            vehicleListData.value.list.push(...list);
        }

        vehicleListData.value.total = total;
        vehicleListData.value.page++;

        // 判断是否还有更多数据
        vehicleListData.value.hasMore = vehicleListData.value.list.length < total;
    }
};

// 下拉刷新
const onRefresh = () => {
    loadVehicleList(true);
};

// 上拉加载更多
const onLoadMore = () => {
    loadVehicleList(false);
};

网络请求配置

1. 环境配置

项目支持多环境自动切换(utils/config.js):

// 根据小程序运行环境自动切换API地址
function getBaseUrl() {
    const accountInfo = wx.getAccountInfoSync();
    const envVersion = accountInfo.miniProgram.envVersion;

    switch (envVersion) {
        case 'develop': // 开发版
            return 'https://oa-dev.onwall.cn';
        case 'trial': // 体验版
            return 'https://oa-dev.onwall.cn';
        case 'release': // 正式版
            return 'https://jiangedianlv.onwall.cn';
        default:
            return 'https://jiangedianlv.onwall.cn';
    }
}

2. 请求拦截器

自动添加sessionid到请求头:

// 请求拦截器自动添加认证信息
service.interceptors.request.use(config => {
    const sessionid = getSessionId();
    if (sessionid) {
        config.headers.cookie = sessionid;
    }
    return config;
});

3. 响应拦截器

自动处理sessionid更新和错误响应:

// 响应拦截器处理sessionid和错误
service.interceptors.response.use(
    response => {
        // 自动更新sessionid
        const newSessionId = response.headers['set-cookie'];
        if (newSessionId) {
            setSessionId(newSessionId);
        }
        return response;
    },
    error => {
        // 统一错误处理
        console.error('请求失败:', error);
        return Promise.reject(error);
    }
);

状态管理规范

Pinia Store使用

1. 用户状态管理 (stores/user.js)

export const useUserStore = defineStore('user', {
    state: () => ({
        userInfo: {
            avatar_url: '',
            nickname: '',
            phone: '',
            // ... 其他用户信息字段
        },
        isAuthenticated: false,
        isLoading: false
    }),

    getters: {
        // 检查用户信息完整性
        hasCompleteProfile: (state) => {
            return !!(state.userInfo.phone && state.userInfo.nickname);
        },

        // 检查收款信息完整性
        hasCompleteCollectionInfo: (state) => {
            return !!(
                state.userInfo.name &&
                state.userInfo.bank_id &&
                state.userInfo.bank_no &&
                state.userInfo.idcard
            );
        }
    },

    actions: {
        // 获取用户信息
        async fetchUserInfo() {
            this.isLoading = true;
            try {
                const result = await getProfileAPI();
                if (result && result.data) {
                    this.updateUserInfo(result.data);
                    this.isAuthenticated = true;
                }
            } finally {
                this.isLoading = false;
            }
        },

        // 更新用户信息
        updateUserInfo(newUserInfo) {
            this.userInfo = { ...this.userInfo, ...newUserInfo };
        }
    }
});

2. 在组件中使用Store

<script setup>
import { useUserStore } from '@/stores/user';

const userStore = useUserStore();

// 获取用户信息
onMounted(() => {
    userStore.fetchUserInfo();
});

// 使用计算属性
const hasCompleteInfo = computed(() => userStore.hasCompleteProfile);
</script>

权限管理

权限检查机制 (utils/permission.js)

/**
 * 检查用户权限
 * @param {Object} userInfo - 用户信息
 * @param {Array} requiredFields - 必需字段列表
 * @returns {Object} 权限检查结果
 */
export function checkUserPermissions(userInfo, requiredFields = []) {
    const missingFields = requiredFields.filter(field => {
        return !userInfo[field] || userInfo[field] === '';
    });

    return {
        hasPermission: missingFields.length === 0,
        missingFields,
        message: missingFields.length > 0 
            ? `请先完善:${missingFields.join('、')}` 
            : ''
    };
}

组件开发规范

1. Vue3 Composition API

<script setup>
import { ref, computed, onMounted } from 'vue';
import { useUserStore } from '@/stores/user';

// 响应式数据
const loading = ref(false);
const userStore = useUserStore();

// 计算属性
const displayName = computed(() => {
    return userStore.userInfo.nickname || '未设置昵称';
});

// 生命周期
onMounted(() => {
    initData();
});

// 方法定义
const initData = async () => {
    loading.value = true;
    try {
        await userStore.fetchUserInfo();
    } finally {
        loading.value = false;
    }
};
</script>

2. NutUI组件使用

<template>
    <nut-button type="primary" @click="handleSubmit">
        提交
    </nut-button>

    <nut-popup v-model:visible="showPopup">
        <nut-form>
            <nut-form-item label="姓名">
                <nut-input v-model="form.name" placeholder="请输入姓名" />
            </nut-form-item>
        </nut-form>
    </nut-popup>
</template>

样式开发规范

1. TailwindCSS + Less结合使用

<template>
    <view class="container">
        <view class="flex justify-between items-center p-4">
            <text class="title">标题</text>
        </view>
    </view>
</template>

<style lang="less" scoped>
.container {
    background: #f5f5f5;

    .title {
        font-size: 32rpx;
        font-weight: bold;
        color: #333;
    }
}
</style>

2. 响应式单位使用

  • 使用 rpx 作为主要单位(小程序自适应)
  • 字体大小:28rpx32rpx36rpx
  • 间距:16rpx24rpx32rpx

注意事项

1. 开发环境

  • 推荐使用 pnpm 作为包管理器
  • Node.js 版本要求:>= 16.0.0
  • 微信开发者工具版本:>= 1.06.0

2. 代码规范

  • 使用 ESLint 进行代码检查
  • 组件名使用 PascalCase
  • 文件名使用 kebab-case
  • 变量名使用 camelCase

3. 性能优化

  • 合理使用 computedwatch
  • 避免在模板中使用复杂表达式
  • 大列表使用虚拟滚动
  • 图片使用懒加载

4. 调试技巧

  • 使用 console.log 进行调试
  • 利用微信开发者工具的调试功能
  • 网络请求在 Network 面板查看

功能模块说明

已屏蔽功能

  • 用户认证功能:移除了我的认证选项,清理了不再使用的视图模式切换选项
  • 认证相关显示:在"我卖的车"页面移除了认证按钮和认证状态文字
  • 认证菜单项:屏蔽了首页认证入口、个人页面认证菜单、订单页面认证列表等3个地方的认证功能

核心功能模块

  1. 用户管理:注册、登录、个人信息管理
  2. 车辆交易:发布车辆、浏览车辆、车辆详情
  3. 订单系统:下单、支付、订单管理
  4. 消息通知:系统消息、聊天消息
  5. 收款设置:银行卡绑定、收款账户管理

部署说明

1. 微信小程序部署

  1. 运行 npm run build:weapp 构建项目
  2. 使用微信开发者工具打开 dist 目录
  3. 点击"上传"按钮上传代码
  4. 在微信公众平台提交审核

2. H5部署

  1. 运行 npm run build:h5 构建项目
  2. dist 目录部署到服务器
  3. 配置 nginx 或其他 web 服务器

更新日志

  • refactor(订单管理): 移除我的认证选项, 清理不再使用的视图模式切换选项,简化界面
  • 我卖的车: 移除认证相关显示和功能,认证按钮和认证状态文字都屏蔽了
  • refactor(profile): 移除我的认证菜单项, 屏蔽了首页认证入口,我的页面列表上菜单,我的订单上我的认证列表,3个地方