常见开发任务.md 14 KB

常见开发任务

最后更新: 2026-02-09 目的: 记录项目中的常见开发任务和解决方案

1. 添加新页面

步骤

# 1. 创建页面目录
mkdir src/views/mypage

# 2. 创建页面文件
touch src/views/mypage/index.vue
touch src/views/mypage/index.config.js

页面文件

<!-- src/views/mypage/index.vue -->
<template>
  <div class="mypage">
    <h1>{{ title }}</h1>
  </div>
</template>

<script setup>
/**
 * 我的页面
 *
 * @description 这是一个示例页面
 */
import { ref } from 'vue';

const title = ref('我的页面');
</script>

<style lang="less" scoped>
.mypage {
  padding: 20px;

  h1 {
    font-size: 24px;
  }
}
</style>

页面配置

// src/views/mypage/index.config.js
export default {
  navigationBarTitleText: '我的页面',
};

添加路由

// src/router/routes/modules/mypage/index.js
export default {
  path: '/mypage',
  name: 'MyPage',
  component: () => import('@/views/mypage/index.vue'),
  meta: {
    title: '我的页面',
  },
};

2. 添加新组件

步骤

# 1. 创建组件目录
mkdir src/components/MyComponent

# 2. 创建组件文件
touch src/components/MyComponent/index.vue

组件文件

<!-- src/components/MyComponent/index.vue -->
<template>
  <div class="my-component">
    <h2>{{ title }}</h2>
    <slot></slot>
  </div>
</template>

<script setup>
/**
 * 我的组件
 *
 * @description 这是一个示例组件
 * @component MyComponent
 */
const props = defineProps({
  /** 标题 */
  title: {
    type: String,
    default: '',
  },
});

const emit = defineEmits({
  /** 点击事件 */
  click: (event) => true,
});
</script>

<style lang="less" scoped>
.my-component {
  h2 {
    font-size: 20px;
  }
}
</style>

使用组件

<template>
  <div>
    <MyComponent
      title="组件标题"
      @click="handleClick"
    >
      组件内容
    </MyComponent>
  </div>
</template>

<script setup>
import MyComponent from '@components/MyComponent/index.vue';

const handleClick = (event) => {
  console.log('组件被点击:', event);
};
</script>

3. 添加 API 接口

步骤

# 1. 创建 API 文件
touch src/api/myfeature.js

API 文件

/**
 * 我的特性 API
 *
 * @description 我的特性相关接口
 */
import { fn, fetch } from '@/api/fn';

const Api = {
  FEATURE: '/srv/?a=myfeature',
};

/**
 * 获取特性数据
 *
 * @description 获取特性数据
 * @param {Object} params - 请求参数
 * @param {number} params.id - ID
 * @returns {Promise<Object>} 响应数据
 */
export const featureAPI = (params) => fn(fetch.get(Api.FEATURE, params));

/**
 * 提交特性数据
 *
 * @description 提交特性数据
 * @param {Object} params - 请求参数
 * @param {string} params.name - 名称
 * @returns {Promise<Object>} 响应数据
 */
export const submitFeatureAPI = (params) => fn(fetch.post(Api.FEATURE, params));

使用 API

<script setup>
import { featureAPI, submitFeatureAPI } from '@/api/myfeature';
import { onMounted } from 'vue';

const fetchData = async () => {
  try {
    const { data } = await featureAPI({ id: 123 });

    if (data) {
      console.log('获取数据成功:', data);
    }
  } catch (err) {
    console.error('获取数据失败:', err);
  }
};

const submitData = async () => {
  try {
    const { data } = await submitFeatureAPI({ name: '名称' });

    if (data) {
      console.log('提交成功:', data);
    }
  } catch (err) {
    console.error('提交失败:', err);
  }
};

onMounted(() => {
  fetchData();
});
</script>

4. 添加 Store

步骤

# 1. 创建 Store 文件
touch src/store/myfeature.js

Store 文件

/**
 * 我的特性 Store
 *
 * @description 管理特性相关状态
 */
import { defineStore } from 'pinia';

export const useMyFeatureStore = defineStore('myfeature', {
  state: () => ({
    data: null,
    loading: false,
    error: null,
  }),

  getters: {
    /**
     * 是否有数据
     */
    hasData: (state) => state.data !== null,

    /**
     * 数据数量
     */
    dataCount: (state) => state.data?.length || 0,
  },

  actions: {
    /**
     * 获取数据
     *
     * @param {number} id - ID
     */
    async fetchData(id) {
      this.loading = true;
      this.error = null;

      try {
        const { data } = await featureAPI({ id });
        this.data = data;
      } catch (err) {
        this.error = err.message;
      } finally {
        this.loading = false;
      }
    },

    /**
     * 清空数据
     */
    clearData() {
      this.data = null;
      this.error = null;
    },
  },
});

使用 Store

<script setup>
import { useMyFeatureStore } from '@/store/myfeature';
import { onMounted } from 'vue';

const featureStore = useMyFeatureStore();

onMounted(() => {
  featureStore.fetchData(123);
});
</script>

<template>
  <div v-if="featureStore.loading">加载中...</div>
  <div v-else-if="featureStore.error">{{ featureStore.error }}</div>
  <div v-else>
    <p>数据数量: {{ featureStore.dataCount }}</p>
    <p>有数据: {{ featureStore.hasData }}</p>
  </div>
</template>

5. 路由跳转

方式 1: 路径跳转

import { useRouter } from 'vue-router';

const router = useRouter();

// 跳转(Hash 模式需要包含前缀)
router.push('/index.html/mypage');

方式 2: 命名路由

import { useRouter } from 'vue-router';

const router = useRouter();

// 跳转(推荐)
router.push({ name: 'MyPage' });

方式 3: 带参数跳转

import { useRouter } from 'vue-router';

const router = useRouter();

// 带查询参数
router.push({
  name: 'MyPage',
  query: { id: 123, name: 'test' }
});

// 带路径参数
router.push({
  name: 'MyPageDetail',
  params: { id: 123 }
});

方式 4: 返回

import { useRouter } from 'vue-router';

const router = useRouter();

// 返回上一页
router.back();

// 或
router.go(-1);

6. 获取路由参数

查询参数

import { useRoute } from 'vue-router';

const route = useRoute();

// 获取查询参数
const id = route.query.id;
const name = route.query.name;

路径参数

import { useRoute } from 'vue-router';

const route = useRoute();

// 获取路径参数
const id = route.params.id;

7. 使用 Vant 组件

按钮

<template>
  <van-button type="primary">主要按钮</van-button>
  <van-button type="success">成功按钮</van-button>
  <van-button type="warning">警告按钮</van-button>
  <van-button type="danger">危险按钮</van-button>
  <van-button plain>朴素按钮</van-button>
  <van-button round>圆形按钮</van-button>
</template>

表单

<template>
  <van-form @submit="onSubmit">
    <van-cell-group inset>
      <van-field
        v-model="username"
        name="username"
        label="用户名"
        placeholder="用户名"
        :rules="[{ required: true, message: '请填写用户名' }]"
      />
      <van-field
        v-model="password"
        type="password"
        name="password"
        label="密码"
        placeholder="密码"
        :rules="[{ required: true, message: '请填写密码' }]"
      />
    </van-cell-group>
    <div style="margin: 16px;">
      <van-button round block type="primary" native-type="submit">
        提交
      </van-button>
    </div>
  </van-form>
</template>

<script setup>
import { ref } from 'vue';

const username = ref('');
const password = ref('');

const onSubmit = (values) => {
  console.log('表单数据:', values);
};
</script>

弹窗

<template>
  <van-button type="primary" @click="showDialog">显示弹窗</van-button>
</template>

<script setup>
import { showDialog } from 'vant';

const showDialog = () => {
  showDialog({
    title: '标题',
    message: '这是弹窗内容',
  }).then((action) => {
    console.log('确认', action);
  }).catch(() => {
    console.log('取消');
  });
};
</script>

8. 使用 Element Plus 组件

按钮

<template>
  <el-button>默认按钮</el-button>
  <el-button type="primary">主要按钮</el-button>
  <el-button type="success">成功按钮</el-button>
  <el-button type="warning">警告按钮</el-button>
  <el-button type="danger">危险按钮</el-button>
  <el-button round>圆形按钮</el-button>
</template>

表格

<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column prop="name" label="姓名" width="180" />
    <el-table-column prop="age" label="年龄" width="180" />
    <el-table-column prop="address" label="地址" />
  </el-table>
</template>

<script setup>
import { ref } from 'vue';

const tableData = ref([
  {
    name: '张三',
    age: 30,
    address: '北京市',
  },
  {
    name: '李四',
    age: 25,
    address: '上海市',
  },
]);
</script>

9. 添加 Keep-Alive 缓存

步骤

// 在 store 中添加页面
import { useMainStore } from '@/store';

const mainStore = useMainStore();

// 添加需要缓存的页面
const keepThisPage = () => {
  const keepPages = [...mainStore.keepPages];

  if (!keepPages.includes('PageName')) {
    keepPages.push('PageName');
  }

  mainStore.keepPages = keepPages;
};

// 移除缓存
const removeKeepPage = () => {
  const keepPages = mainStore.keepPages.filter(page => page !== 'PageName');

  mainStore.keepPages = keepPages.length ? keepPages : ['default'];
};

使用

<script setup>
import { onMounted } from 'vue';
import { useMainStore } from '@/store';

const mainStore = useMainStore();

onMounted(() => {
  // 添加到缓存
  mainStore.keepThisPage();
});
</script>

10. 处理微信分享

步骤

// 引入微信分享工具
import { share } from '@/utils/share';

// 设置分享
const setShare = () => {
  share({
    title: '分享标题',
    desc: '分享描述',
    link: window.location.href,
    imgUrl: 'https://example.com/share.png',
  });
};

// 在页面加载时设置
onMounted(() => {
  setShare();
});

11. 添加音频播放

单模式

<template>
  <audioBackground
    :audio-url="audioUrl"
    :autoplay="false"
    @play="handlePlay"
    @pause="handlePause"
  />
</template>

<script setup>
import audioBackground from '@components/audioBackground.vue';
import { ref } from 'vue';

const audioUrl = ref('/audio/guide.mp3');

const handlePlay = () => {
  console.log('开始播放');
};

const handlePause = () => {
  console.log('暂停播放');
};
</script>

播放列表模式

<template>
  <audioList
    v-model:playlist="playlist"
    :current-index="currentIndex"
  />
</template>

<script setup>
import audioList from '@components/audioList.vue';
import { ref } from 'vue';

const playlist = ref([
  {
    id: 1,
    title: '音频 1',
    url: '/audio/1.mp3',
  },
  {
    id: 2,
    title: '音频 2',
    url: '/audio/2.mp3',
  },
]);

const currentIndex = ref(0);
</script>

12. 使用高德地图

初始化地图

import AMapLoader from '@amap/amap-jsapi-loader';

const initMap = async () => {
  try {
    const AMap = await AMapLoader.load({
      key: 'YOUR_AMAP_KEY',
      version: '2.0',
      plugins: ['AMap.Scale', 'AMap.ToolBar'],
    });

    const map = new AMap.Map('container', {
      zoom: 11,
      center: [116.397428, 39.90923],
    });

    return map;
  } catch (err) {
    console.error('地图加载失败:', err);
  }
};

添加标记

const addMarker = (map, position) => {
  const marker = new AMap.Marker({
    position: position,
    map: map,
  });

  return marker;
};

13. 错误处理

统一错误处理

const handleError = (err) => {
  console.error('操作失败:', err);

  // 显示错误提示
  showToast(err.message || '操作失败,请重试');

  // 上报错误
  // reportError(err);
};

// 使用
try {
  await someAPI();
} catch (err) {
  handleError(err);
}

Toast 提示

import { showToast } from 'vant';

// 成功提示
showToast('操作成功');

// 错误提示
showToast({
  type: 'fail',
  message: '操作失败',
});

14. 加载状态

使用 Loading

<template>
  <div v-if="loading" class="loading">加载中...</div>
  <div v-else>
    <!-- 内容 -->
  </div>
</template>

<script setup>
import { ref } from 'vue';

const loading = ref(false);

const fetchData = async () => {
  loading.value = true;

  try {
    const { data } = await someAPI();
    // 处理数据
  } catch (err) {
    console.error(err);
  } finally {
    loading.value = false;
  }
};
</script>

使用 Vant Loading

<template>
  <van-button @click="showLoading">显示加载</van-button>
</template>

<script setup>
import { showLoadingToast, closeToast } from 'vant';

const showLoading = () => {
  showLoadingToast({
    message: '加载中...',
    forbidClick: true,
    duration: 0,
  });

  // 模拟异步操作
  setTimeout(() => {
    closeToast();
  }, 2000);
};
</script>

15. 图片处理

图片懒加载

<template>
  <van-image
    :src="imageUrl"
    lazy-load
    :show-error="true"
    :show-loading="true"
  >
    <template #error>
      <div class="error">加载失败</div>
    </template>
  </van-image>
</template>

<script setup>
import { ref } from 'vue';

const imageUrl = ref('/images/example.jpg');
</script>

图片预览

<template>
  <van-image
    :src="imageUrl"
    @click="previewImage"
  />
</template>

<script setup>
import { ref } from 'vue';
import { showImagePreview } from 'vant';

const imageUrl = ref('/images/example.jpg');
const images = ref([
  '/images/1.jpg',
  '/images/2.jpg',
  '/images/3.jpg',
]);

const previewImage = () => {
  showImagePreview({
    images: images.value,
    startPosition: 0,
  });
};
</script>

参考文档