Uniapp

前期准备

运行环境要求版本推荐版本
node>=18.0.018.14.2
npm>=9.0.09.5.0
HBuilder X>=3.7>=3.7

目录结构

├─📂 server
│  ├─📂 dist             # 打包目录
│  │  ├─ 📂 dev          # 开发包
│  │  └──📂 build        # 生成包
│  │
│  ├─📂 node_modules     Node依赖
│  │  └──📂 ...           
│  │
│  ├─📂 src               # 源代码
│  ├──📂 api              # 所有请求
│  ├──📂 components       # 全局公用组件
│  ├──📂 enums            # 全局枚举
│  ├──📂 hooks            # 全局hook
│  ├──📂 pages            # 所有页面
│  ├──📂 static           # 静态资源文件
│  ├──📂 stores           # 全局状态管理
│  ├──📂 styles           # 全局样式
│  ├──📂 uni_modules      # uniapp插件市场引入的包
│  ├──📂 utils            # 全局公用方法
│  ├── App.vue            # 入口页面
│  ├── main.ts            # 入口文件 初始化,组册插件等
│  ├── manifest.json      # uniapp各端相关配置
│  ├── pages.json         # 页面配置
│  └── uni.scss           # 全局scss变量
│ 
├── .env.development      # 开发环境变量
├── .env.production       # 生产环境变量
├── .eslintrc.js          # Eslint规范配置
├── .gitignore            # Git忽略文件配置
├── .stylelintrc.js       # CSS规范配置
├── index.html            # H5网页
├── jsconfig.json         # jsconfig.json
├── package.json          # package.json
└── vite.config.ts        # vite 配置项

代码规范

  • 采用了 Eslint 控制vue和js等代码规范
  • 采用了 Stylelint 控制css的代码规范
  • 规范配置文件所在位置:
    • Eslint: src/.eslintrc.js
    • Stylelint: src/.stylelinttc.js

开发工具

【Visual Studio Code】开发

  • 打开项目终端:
    • 使用vscode打开uniapp目录,在vscode左上角菜单中点击 终端 > 新建终端
  • 复制env文件:
    • 1、复制.env.development.example,将复制的文件名修改为.env.development
    • 2、复制.env.production.example,将复制的文件名修改为.env.production
    • 3、打开.env.development文件,修改VITE_APP_BASE_URL变量的值为项目安装部署的服务端地址
  • 安装并运行:
# 1、安装依赖 (仅需要安装一次)
npm install

# 2、运行到H5 (在终端运行命令)
npm run dev:h5

# 【其它运行命令】
npm run dev:mp-weixin # 运行到微信小程序
npm run build:h5      # 发行到H5

  • 最终效果如下:
vite v2.9.18 dev server runing at:

> Local: http://localhost:3001/
> Local: http://192.168.0.23:3001/

read  in 1628ms,
  • Vscode配置建议:
    • 安装 Eslint 插件
    • 安装 Stylelint 插件
{
    // 菜单缩进的设置
    "workbench.tree.indent": 18,
    // 设网页缩放级别
    "window.zoomLevel": 1,
    // 编辑器字体大小
    "editor.fontSize": 16,
    // 关闭类型切TabSize
    "editor.detectIndentation": false,
    // 重设定缩进TabSize
    "editor.tabSize": 4,
    // 每保存时自动格式化 
    "editor.formatOnSave": true,
    // 启用Eslint规范校验
    "eslint.enable": true,
    // 启用Styles规范校验
    "stylelint.enable": true,
    // 每保存时Eslint修复
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true,
        "source.fixAll.stylelint": true
    },
    // 每保存时Styles修复
    "stylelint.validate": [
        "css",
        "less",
        "postcss",
        "scss",
        "vue",
        "sass"
    ],
    // 确定校验准则ES
    "eslint.validate": [
        "javascript",
        "javascriptreact"
    ]
}

【HBuilderX】开发

  • 导入项目:
    • 点击HBuilderX 左上角 菜单文件 > 导入 > 从本地目录导入,目录选择uniapp
  • 复制env文件:
    • 1、复制.env.development.example,将复制的文件名修改为.env.development
    • 2、复制.env.production.example,将复制的文件名修改为.env.production
    • 3、打开.env.development文件,修改VITE_APP_BASE_URL变量的值为项目安装部署的服务端地址
  • 安装并运行:
    • 选中当前项目,点击HBuilderX左上角菜单工具 > 外部命令 >npm install安装依赖
    • 运行到H5,点击HBuilderX左上角菜单 运行 > 运行到浏览器 > Chrome
    • 运行到微信小程序,点击HBuilderX左上角菜单 运行 > 运行到小程序模拟器 > 微信开发者工具 - (uniapp)

注意

  • 运行到微信小程序前,先配置好小程序的appid:

    • 点击uniapp/src/manifest.json,选择微信小程序配置>微信小程序AppID,输入appid即可
  • 一般运行到微信小程序,会自动打开微信开发者工具,(如打开失败):

    • 手动打开工具 -> 设置 -> 安全设置,将服务端口开启。
    • 也有可能是你配置的小程序appid中,你登录的账号不是这个小程序的开发者。
      • 只需要去微信小程序后台将该账号添加到开发者,重新运行即可。
  • M1/M2电脑编译报以下错误:

    • [ERROR] Cannot start service: Host version "0.18.20" does not match binary version "0.17.19"
    • 原因: 可能是你升级了vite的版本导致的兼容问题
    • 解决: 把vite的版本改为(4.3.9) => "vite": "4.3.9"

【苹果(M)】芯片报错

  • Apple M系列芯片在Uniapp下编译的报错处理:

警告

  • 1、首先你要正常的安装依赖 npm install
  • 2、在运行编译前你需要这样操作:
    • 2.1、在node_modules 找到 @esbuild 目录进入到里面
    • 2.2、这时候你看到目录里面只有一个 darwin-arm64
    • 2.3、我们把 darwin-arm64 拷贝一份, 并且把拷贝出来的那份 重命名为 darwin-x64
    • 2.4、这时候你这个文件夹就存在两份文件了,分别是 darwin-arm64darwin-x64
    • 2.5、到这里就可以了,重新运行编译,可以正常运行。

环境变量

变量命名规则:需要以VITE_为前缀的
如何使用说明:import.meta.env.VITE_
更多细节常见:https://vitejs.cn/guide/env-and-mode.html#env-variables

  • .env.development (开发环境适用)
NODE_ENV = 'development'

# 请求域名
VITE_APP_BASE_URL='https://dev.waitadmin.cn'  
  • .env.production (生产环境适用)
NODE_ENV = 'production'

# 请求域名
VITE_APP_BASE_URL='https://www.waitadmin.cn'  

页面配置

页面配置文件位置:uniapp/src/pages.json
如何配置请参考此:https://uniapp.dcloud.net.cn/collocation/pages.html

{
    "auth": true,
    "path": "pages/user/home",
    "style": {
        "navigationBarTitleText": "个人中心",
        "navigationBarTextStyle": "white",
        "navigationBarBackgroundColor": "#2979ff"
    }
}
名称类型默认说明
authbooleanfalse自定义参数,控制是否需要登录才可访问
varybooleanfalse自定义参数,是否关闭渲染主题颜色
pathstringnulluni官方参数,页面的路径,"必须存在"
styleobjectnulluni官方参数,配置页面的样式
---更多参数请看uni官方文档,自定就两个

接口请求

基本描述

  • 系统中使用uview提供的方法发起请求,并对其进行了更深一步的封装,位于src/utils/request
  • 如果你需要调整http请求时的一些参数,你可以在config.js中调整
  • 如果你需要对响应回来的数据进行统一处理,你可以在http.js中调整
├──📂 request
│  ├── config.js   # 封装请求的配置参数
│  ├── http.js     # 封装Http请求和拦截
│  ├── route.js    # 页面路由跳转等拦截
│

默认配置

export const config = {
    // 请求的本域名
    baseUrl: `${import.meta.env.VITE_APP_BASE_URL || ''}/api`,
    // 设置为JSON
    dataType: 'json',
    // 显示请求中
    showLoading: false,
    // 加载提示文
    loadingText: '请求中...',
    // 在此时间内请求中就显示加载中的动画
    loadingTime: 800,
    // 是否在拦截器中返回服务端的原始数据
    originalData: true,
    // 展示加载时候给透明蒙层防止触摸穿透
    loadingMask: true,
    // 配置网络发起的请求头信息
    header: {
        'content-type': 'application/json;charset=UTF-8'
    }
}

请求拦截

Vue.config.globalProperties.$u.http.interceptor.request = (config) => {
    const userStore = useUserStore()
    config.header.token = userStore.$state.token
    config.header.terminal = clientUtil.fetchClient()
    return config
}

响应拦截

Vue.config.globalProperties.$u.http.interceptor.response = (response) => {
    const result = response.data
    const { logout } = useUserStore()

    const isStructure = response.header.structure || response.header.Structure
    switch (result.code) {
        case errorEnum.SUCCESS:
            return isStructure ? result : result.data
        case errorEnum.SYSTEM_ERROR:
        case errorEnum.PARAMS_ERROR:
        case errorEnum.METHOD_ERROR:
        case errorEnum.CONTROL_ERROR:
        case errorEnum.REQUEST_ERROR:
        case errorEnum.OPERATE_ERROR:
        case errorEnum.UPLOADS_ERROR:
        case errorEnum.PURVIEW_ERROR:
            uni.$u.toast(result.msg)
            return Promise.reject(result)
        case errorEnum.LOGIN_EMPTY_ERROR:
        case errorEnum.LOGIN_EXPIRE_ERROR:
            logout()
            uni.navigateTo({ url: '/pages/login/enroll' })
            return Promise.reject(result)
        default:
            return result
    }
}

接口配置

  • 我们对所有请求接口进行了统一管理, 统一放在 src/api/..目录下面
  • 以下我们以文章管理的接口作为示例子展开说明一下 articleApi.js
// articleApi.js 文件的内容
export default class {
    /**
     * 分类类别
     */
    static category() {
        return uni.$u.http.get('article/category')
    }

    /**
     * 文章列表
     */
    static lists(params) {
        const param = {
            cid: params.cid || 0,
            pageNo: params.pageNo || 1,
            pageSize: params.pageSize || 10
        }
        return uni.$u.http.get('article/lists', param)
    }

    /**
     * 文章详情
     */
    static detail({ id }) {
        return uni.$u.http.get('article/detail', { id })
    }
}
  • uni.$u.http 方法的参数说明
uni.$u.http.get(
    'article/detail',    // 请求的api地址
    { id: 1 },           // 请求携带的参数
    { Structure: true }  // 请求header头参数
)

// PS: 注意上面请求头中的, Structure 参数
// 此参数的意思是,是否返回数据结构,默认是 false 即不返回结构
// 没有特殊情况,不用填写 Structure这个参数

// 问: 那到底是怎么一个结构呢?
// 默认返回的结构如下:
{
    "id": 1,
    "title": "风起东方日日新",
    "content": ""
}

// 如果开启返回数据结构,返回如下:
{
    "code": 0,
    "msg": "success",
    "data": [
        {
          "id": 1,
          "title": "风起东方日日新",
          "content": ""
        }
    ]
}

主题样式

基础样式

  • 我们定义了一些比较常用的css样式,放在了 src/styles/basis.scss
/* 字体大小 */
.font-xs { font-size: 24rpx; }
.font-sm { font-size: 26rpx; }
.font-bm { font-size: 28rpx; }
.font-lg { font-size: 30rpx; }
.font-xl { font-size: 32rpx; }
.font-2xl { font-size: 34rpx; }
.font-3xl { font-size: 36rpx; }
.font-4xl { font-size: 38rpx; }
.font-5xl { font-size: 40rpx; }
.font-6xl { font-size: 42rpx; }
.font-7xl { font-size: 44rpx; }
.font-8xl { font-size: 46rpx; }
.font-9xl { font-size: 48rpx; }
.font-xxl { font-size: 60rpx; }

/* 字体粗细 */
.font-weight-thin { font-weight: 100; }
.font-weight-fine { font-weight: 200; }
.font-weight-light { font-weight: 300; }
.font-weight-normal { font-weight: 400; }
.font-weight-medium { font-weight: 500; }
.font-weight-semi { font-weight: 600; }
.font-weight-bold { font-weight: 700; }
.font-weight-extra { font-weight: 800; }
.font-weight-black { font-weight: 900; }

// PS: 更多样式请自行在代码里面查看

// 怎么使用呢? 直接在页面标签里面使用就可以了,例如:
<template>
  <div class="font-lg font-weight-bold">
       WaitAdmin
  </div>
</template>

样式穿透

- **开启scoped属性后需要如果需要将样式作用到子组件上,可以这样处理:**
<style scoped>
:deep(.el-menu-item) {
    // todo
}
</style>

主题配置

  • 主题的css样式统一配置在以下文件中 src/styles/theme.scss
  • 目前提供3种主题颜色,您也可以扩展自己的主题色
  • 如果你扩展了别的主题色,别忘记在后台也加上哦
// 天空蓝主题
.default-theme,
.blue-theme {
    --theme-color: #2979ff;
    --theme-background: #2979ff;
    .color-theme { color: #2b85e4; }
    .u-btn--theme {
        color: #ffffff; 
        border-color: #2979ff; 
        background-color: #2979ff; 
    }
    .u-btn--theme--plain {
        color: #2979ff;
        border-color: #a0cfff;
        background-color: #ecf5ff;
    }
}

// 热情红主题
.red-theme {
    --theme-color: #ff5058;
    --theme-background: #ff5058;
    .color-theme { color: #ff5058; }
    .u-btn--theme { 
        color: #ffffff; 
        border-color: #ff5058; 
        background-color: #ff5058; 
    }
    .u-btn--theme--plain { 
        color: #ff5058; 
        border-color: #ffa0a0; 
        background-color: #ffecec; 
    }
}

// 生鲜绿主题
.green-theme {
    --theme-color: #40ca4d;
    --theme-background: #40ca4d;
    .color-theme { color: #40ca4d; }
    .u-btn--theme { 
        color: #ffffff; 
        border-color: #40ca4d; 
        background-color: #40ca4d; 
    }
    .u-btn--theme--plain { 
        color: #40ca4d; 
        border-color: #9efba7; 
        background-color: #eafcec; 
    }
}

关于主题

  • 主题采用 mixins 混入的方式进行控制, 之所以您在后台切换,页面颜色也能跟着边,和它脱不开关系
  • 我们定义了一个 appMixin.js 文件进行主题的相关处理。 (具体逻辑自己看代码)
  • 简单说明: 有了它以后, 每个页面默认有一个 onLoad() 方法, 并且它会处理主题 - 重要的说明:
  • 如果您想页面得到主题的控制,那你一定要在页面的根节点设置 :class="themeName"
  • 这个很重要,如果你不设置,则表示这个页面不受主题色控制,建议都加上。
  • 示例如下: (你也可以看下我代码是怎么写的)
<template>
    <view :class="themeName">
        <div>页面内容</div>
    </view>
</template>

常用工具

  • 为了开发更方便快捷,我们编写了一些常用的工具类 都放在以下目录: src/utils/...

缓存工具 (cacheUtil.js)

import cacheUtil from '@/utils/cacheUtil'

/**
 * 设置缓存
 * 
 * @param string k (键)
 * @param string v (值)
 * @param int t    (过期时间)
 */
cacheUtil.set(k, v, t)

/**
 * 获取缓存
 *
 * @param string k (键)
 * @param def (默认,如果为null返回这个)
 */
cacheUtil.get(k, def)

/**
 * 删除缓存
 *
 * @param string k (键)
 */
cacheUtil.remove(k)

/**
 * 清空缓存
 */
cacheUtil.clear()

验证工具 (checkUtil.js)

import checkUtil from '@/utils/checkUtil'

// PS: 以下验证都是,如果为true=是, false=不是

// 类型验证
checkUtil.is(val, type)

// 开发模式
checkUtil.isDevMode()

// 生产模型
checkUtil.isProdMode()

// Map类型
checkUtil.isMap(val)

// Date类型
checkUtil.isDate(val)

// isNumber
checkUtil.isDate(val)

// String类型
checkUtil.isString(val)

// Boolean类型
checkUtil.isBoolean(val)

// RegExp类型
checkUtil.isRegExp(val)

// Array类型
checkUtil.isArray(val)

// Function类型
checkUtil.isFunction(val)

// Object类型
checkUtil.isObject(val)

// Promise类型
checkUtil.isPromise(val)

// Null类型
checkUtil.isNull(val)

// Empty类型
checkUtil.isNull(val)

// Undefined类型
checkUtil.isNull(val)

// Undefined或Null
checkUtil.isNullOrUndefined(val)

// Window类型
checkUtil.isWindow(val)

// 是否是手机号
checkUtil.isMobile(val)

// 是否是邮箱号
checkUtil.isEmail(val)

客户端工具 (clientUtil.js)

import clientUtil from '@/utils/clientUtil'

// 是否是微信环境 (公众号/微信小程序)
// 用于判断是否在微信内置浏览器打开的
clientUtil.isWeixin()

// 是否为安卓环境 (如果是APP端)
// true=是安卓, false=苹果
clientUtil.isAndroid()

// 返回当前客户端标识
// 1 = 微信小程序
// 2 = 微信公众号
// 3 = H5
// 4 = PC
// 5 = 苹果
// 6 = 安卓
clientUtil.fetchClient()

其它工具 (toolUtil.js)

import toolUtil from '@/utils/toolUtil'

// 提取微信小程序Code
toolUtil.obtainWxCode()

// 提取微信位置(定位坐标)
toolUtil.obtainWxLocation()

// 获取底部导航
// 那些页面是固定在底部的
// 如果有变动,记得到这里修改一下
toolUtil.tarBarList()

// 获取当前页面
toolUtil.currentPage()

// 上传文件资源
toolUtil.uploadFile()

// 渲染底部导航
// 一般情况,你并不需要动它
toolUtil.setTabBar()

国际化(多语言)

关于多语言

注意

uni-app的国际化,即多语言,分为应用部分和框架部分。

应用部分,即开发者自己的代码里涉及的界面部分 框架部分,即uni-app内置组件和API涉及界面的部分 不同端的国际化方案也有差异,uni-app 自 3.1.5起,App 和 H5 支持框架国际化。小程序平台的国际化依赖于小程序平台框架自身。一般而言海外用户更多使用的是App和H5

看完本文档还不知道怎么使用,请看官方文档

PS: Uniapp关于对语言的文档: https://uniapp.dcloud.net.cn/tutorial/i18n.html#

语言包管理

  • 1、语言包统一放在 src/lang/.. 目录下

  • 2、我们内置了常用三个语言包(其它的自行扩展),内置的有:

    • en.json 英文
    • zh-Hans.json 简体中文
    • zh-Hant.json 繁体中文
  • 3、UniApp 支持多种语言地区代码,以下是一些常见语言地区代码的示例:

简体中文:zh-Hans
繁体中文:zh-Hant
英文:en
法文:fr
德文:de
日文:ja
韩文:ko
俄文:ru

语言包配置

  • 这里说的是语言包要怎么配置
  • 我们已简体中文语言包 zh-Hans.json 为示例:
  • PS: 其它语言包也是一言的,把里面的值换成对应的就行
{
    "pages.index.index": "WaitAdmin开源系统",
    "pages.login.enroll": "登录"
}

语言包的使用

  • 前面一直在在说如何去定义语言包。
  • 这里我们说一下怎么去使用语言包。

在页面中的使用

  • 页面模板中使用 $t() 获取,并传递国际化json文件中定义的key,js中使用 this.$t('')
  • PS: 在vue3中如果使用了 setup 语法糖 是无法直接在 js 中使用 this对象的,下面我再讲怎么用。
<template>
  <view class="container">
    <view class="title">{{ $t('pages.index.index') }}</view>
  </view>
</template>

在Js中的使用

  • Vue2 中使用 this.$t('')
  • Vue3 中使用:
<script setup>
import {getCurrentInstance } from 'vue'
  
// 此方法在开发环境以及生产环境下都能放到组件上下文对象
const { proxy }  = getCurrentInstance()
  
// 然后你就可以直接使用 $t 方法了
proxy.$t('pages.index.index')
</script>

在pages.json中使用

  • 使用 %% 占位
{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "%pages.index.index%" 
      }
    }
  ],
  "tabBar": {
    "list": [{
        "pagePath": "pages/index/index",
        "text": "%pages.index.index%"
      }
    ]
  }
}

如何切换语言

  • src/main.js 文件中进行切换
  • locale 参数是控制使用什么语言的。
  • 你要改成别的语言(如英文)你可以这样配置:locale: 'en'
// 以下是默认的语言: zh-Hans (简体中文)
let i18nConfig = {
    locale: uni.getLocale(),
    messages:{
        'en': en,
        'zh-Hans': zhHans,
        'zh-Hant': zhHant
  	}
}

// 切换到英文语言
let i18nConfig = {
  locale: 'en', // 这里直接改成对应的语言包就行
  messages:{
    'en': en,
    'zh-Hans': zhHans,
    'zh-Hant': zhHant
  }
}

// uni.getLocale()
    这个是获取语言的
// uni.setLocale()
    这个是设置语言的

扩展其它语言

  • 1、在lang目录加入语言包如 jp.json
  • 2、在 main.js 导入语言包,示例
import App from './App.vue'
import en from './lang/en.json'
import zhHans from './lang/zh-Hans.json'
import zhHant from './lang/zh-Hant.json'

// 1、导入你扩展的语言包
import jp from './lang/jp.json'

// 2、把语言包加入到配置
let i18nConfig = {
  locale: 'en',
  messages:{
    'en': en,
    'zh-Hans': zhHans,
    'zh-Hant': zhHant,
    'jp': jp // 加入到这里
  }
}

// 3、到这里你就可以正常使用了

小程序国际化

  • 已支持:
  • 页面
  • 组件
  • 不支持:
    • pages.json,可以通过调用API来设置,例如更改标题 uni.setNavigationBarTitle()
    • tabbar 不支持动态修改内容,但是可以通过自定义tabbar的方式,详情: https://uniapp.dcloud.net.cn/collocation/pages?id=custom-tab-bar

其它

按钮加载

  • 如果您想在请求接口的时候,在数据没有返回前,让按钮可以显示loading状态,你可以这么做。
  • 这样子操作可以避免用户重复点击按钮,导致重复请求。
<u-button
    :loading="loading"
    type="theme"
    shape="circle"
    @click="onSaLogin()"
>登录</u-button>

<script setup>
import { useLock } from '@/hooks/useLock'
import loginApi from '@/api/loginApi'

const { loading, methodAPI:$loginApi } = useLock(loginApi.login)
const onSaLogin = () => {
  let params = {...}
  $loginApi(params).then(async result => {
    // todo 结果返回后处理的逻辑 
  }).catch(() => {
    loading.value = false // 把loading状态关闭
  })
}
</script>

小部件

  • 我们制作了一些比较常用的小部件(组件),存放在 src/components/widgets/...
  • 具体参数看代码吧,暂时没有时间细说这里面的参数
  • adv (广告部件)
  • button (按钮部件)
  • service (服务部件)
上次更新:
贡献者: windy, zero, fzr