环境安装
全局安装 vue 脚手架
npm install -g @vue/cli
创建 uniapp
使用正式版
vue create -p dcloudio/uni-preset-vue my-project
使用 alpha 版
vue create -p dcloudio/uni-preset-vue#alpha my-alpha-project
等待下载一万年......之后出现如下图,可以选择子集想要的模板,我一般选默认模板,什么都没有,自己继续配置

默认模板确认之后,文件结构如下

下面我们要进行改造项目了
自定义项目内容,搭建基础
项目配置文件 .vscode
为了项目开发方便,当然也为了跟我们共同开发的小伙伴,达成代码共识,也就是代码有规范,我们利用插件和简单配置,格式化我们项目代码,并且附带代码规范,在 .vscode文件夹中,也可以放一些项目文档等

其中 setttings.json是项目配置文件,单独在此项目中起作用
我的配置如下,具体就不介绍了,感兴趣小伙伴自行整理,符合自己团队就好
{
"editor.formatOnSave": true, // #每次保存的时候自动格式化*
"editor.formatOnPaste": true, // #每次粘贴的时候自动格式化*
"editor.codeActionsOnSave": { // #每次保存的时候将代码按eslint格式进行修复*
"source.fixAll.eslint": true
},
"prettier.jsxSingleQuote": true,
"prettier.semi": false, // #去掉代码结尾的分号*
"prettier.singleQuote": true, // #使用带引号替代双引号*
"prettier.requireConfig": true,
"javascript.preferences.quoteStyle": "single",
"javascript.format.insertSpaceBeforeFunctionParenthesis": true, // #让函数(名)和后面的括号之间加个空格*
"javascript.updateImportsOnFileMove.enabled": "always",
"html.format.endWithNewline": true,
"vetur.format.defaultFormatter.js": "vscode-typescript", // #让vue中的js按编辑器自带的ts格式进行格式化*
"vetur.format.defaultFormatter.html": "js-beautify-html",
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap_line_length": 120,
"wrap_attributes": "force-aligned", // 第一个属性不折行,后续所有属性和第一个对齐折行*
"end_with_newline": false
}
},
"[vue]": {
"editor.defaultFormatter": "octref.vetur"
},
"cSpell.words": [
"scrollbar",
"wrapper"
],
"git.ignoreLimitWarning": true,
}
请求工具 api
这里根据uni-app 的请求,来封装了一下,适合自己公司的请求,仅供参考
封装之后,接口请求全部统一管理,页面上只需要调用 this.$api.接口名
文件结构
├── src
│ ├── api
│ │ ├── modules // 模块文件夹,各个文件需要的不同接口做区分
│ │ │ ├── app.js // app 全局需要的请求,放这里
│ │ ├── index.js // 此文件作为 api 唯一出口,引入模块,在 vue 全局挂载
│ │ ├── request.js // 根据 uniapp 请求封装的请求,带拦截
│ │ └── urls.js // 后端的接口,统一管理文件
贴出例子,仅供参考
- app.js
import { urls } from '../urls'
import { getFullUrl } from '@/utils/tools.js'
// type: 2: 图片,3: 视频,4: 音频
export function uploadFile (params, url = urls.uploadFile) { // 上传图片
const temConfig = {
header: {
'Content-Type': 'multipart/form-data'
}
}
return new Promise((resolve, reject) => {
uni.uploadFile({
url: url, //仅为示例,非真实的接口地址
name: 'files',
filePath: params.file,
fileType: params.fileType,
...temConfig,
formData: {
'loginName': uni.getStorageSync('thirdUserId'),
'orgId': uni.getStorageSync('orgId'),
'grant_type': 'password',
'appid': 'mobileWCOA',
'type': params.type
},
success: (response) => {
const res = JSON.parse(response.data)
if (res.code === 10000) {
resolve(res.content)
} else {
resolve({
isError: true,
message: res.message,
subCode: res.subCode
})
}
}
})
})
}
// 上传头像
export function uploadAvatar (file, url = urls.uploadAvatar) { // 上传头像
return new Promise((resolve, reject) => {
const headers = {
'ContentType': 'multipart/form-data',
}
uni.uploadFile({
url: getFullUrl(urls.uploadAvatar),
name: 'files',
filePath: file,
header:headers,
fileType: 'image',
success: (response) => {
const res = JSON.parse(response.data)
if (res.code === 10000) {
resolve(res.content)
} else {
resolve({
isError: true,
message: res.message,
subCode: res.subCode
})
}
}
})
})
}
- index.js
import Vue from 'vue'
import { urls, fileHost, imgHost } from './urls'
import * as app from './modules/app'
export const api = {
...app
}
Vue.prototype.$api = api
Vue.prototype.$urls = urls
- request.js
const loginErrorSubCode = ['TOKEN_EXPIRED', 'TOKEN_NULL', 'SESSION_KEY_FAIL']
const loginErrorCode = [40001, 40002]
// 报错锁
let LOGIN_LOCK = false
export default function (obj) {
return new Promise((resolve, reject) => {
// 无效请求
if (!obj.url) return reject('无效请求,没有 url')
// 没有 token,并且不在白名单,不允许请求
if (!uni.getStorageSync('token') && !obj.needlessToken) {
if (LOGIN_LOCK) return
LOGIN_LOCK = true
uni.navigateTo({ url: '/pages/public/login' })
return
}
LOGIN_LOCK = false
let headers = {
'authToken': uni.getStorageSync('token') || '',
'masterOrgId': uni.getStorageSync('masterOrgId') || ''
}
if (obj.formData) {
headers['Content-type'] = "application/x-www-form-urlencoded"
}
// 发起请求
uni.request({
url: obj.url,
data: obj.data,
method: obj.method || 'POST',
header: headers,
success: (response) => {
if (obj.method === 'GET') {
resolve(response.data)
return
}
const res = response.data
if (loginErrorCode.includes(res.code) || loginErrorSubCode.includes(res.subCode)) {
if (LOGIN_LOCK) return
LOGIN_LOCK = true
uni.navigateTo({ url: '/pages/public/Login' })
return
}
LOGIN_LOCK = false
if (res.code === 10000) {
resolve(res)
} else {
resolve({
isError: true,
message: res.message,
subCode: res.subCode
})
}
},
fail: (e) => {
if (loginErrorCode.includes(e.data.code) && loginErrorSubCode.includes(e.data.subCode)) {
if (LOGIN_LOCK) return
LOGIN_LOCK = true
uni.navigateTo({ url: '/pages/public/Login' })
return
}
console.log(" fail:" + JSON.stringify(e.data));
},
complete: () => { }
})
})
}
- urls.js
export const urls = {
getCommunityNoteInfoById: '/community/communityNote/getCommunityNoteInfoById', // 根据笔记id查询笔记详细信息
}
此时请求封装完成,还是挺好用的
其它内容没什么特别的,就是自己封装的一些工具
整体文件结构如下
