APP研发总结和分享一些经常有人遇到的问题
Anthor:'林子'
1.使用的技术栈和讲述
使用的技术栈是安卓配合uniapp实现 独立开发 本身app研发并不难 有点体力活 就是对框架的再次运用与实现灵活封装 本次遇到的新知识就是map地图的 学习了map地图 发现可以做一个简单的美团外卖 不过应该有很多难关 不是我想的这么简单
2.app的页面快速封装(当然这些并不难)
onLoad(options) {
this.current=options.id
//字典
const dict=['修改灯杆','控制器列表']
let res=options.id
this.current=res
// 点击的那个页面的时候显示修改标题 就不要频繁为app修添加页面 增加内存消耗
dict.forEach((v,index)=>{
if(index==res){
uni.setNavigationBarTitle({
title:v
})
}
})
},
3重难点map地图的实现
//写入map便签
<map style="width: 100%; height: 100%" id="map1" ref="map1" :show-location="true"
:latitude="map.latitude" :longitude="map.longitude" :scale="map.scale" :markers="map.covers"
@markertap="markertap"
@regionchange="regionchange" @updated="mapUpdated">
</map>
data(){
return{
map: {
title: 'map',
latitude: 22.862277018229168,
longitude: 108.27395046657986,
scale:10.5,
covers: [],
updated: false,
oldMarker: undefined
},
QueryDeviceList: {
input: {
value: {
"organizationId": undefined, // id
"searchKey": undefined, // 关键字搜索
"southwest": [],
"northeast": []
}
},
output: {
value: [],
currentDataShow: false,
currentData: {},
organizationName: undefined
}
}
}
}
created(){
//uniapp获取经纬度
uni.getLocation({
type: 'gcj02', success: (res) => {
console.log(res)
this.setLocation(res) //获取经纬度之后可以进行转换 然后据此搜索一波数据
this.getRegion() //获取视野
}
})
},
mounted(){
//刚进页面设置一波显示的东西
this.methods('setDeviceShow', false)
this.methods('QueryDeviceListReset')
//
this.methods('setLocation', {latitude:this.map.longitude:this.map.longitude})
await this.methods('getRegion')
this.methods('setMoveToLocation') //移动
let data = await this.methods('queryFaultList',true)
if (data) {
this.methods('setCovers', data)
}
}
},
methods:{
//地图加载完毕的事件
async mapUpdated () {
if (this.map.updated) return
this.methods('setUpdated')
this.methods('setMapContext')
this.methods('setMoveToLocation')
await this.methods('getRegion')
console.log('queryFaultList------------------')
let data = await this.methods('queryFaultList')
console.log(data)
if (data) {
this.methods('setCovers', data)
}
},
setUpdated(){
this.map.updated = true
},
//算是绑定对象吧
setMapContext () {
this.mapContext = uni.createMapContext('map1')
console.log(this.mapContext)
},
//设置中心点
setLocation (data) {
this.map.latitude = data.latitude
this.map.longitude = data.longitude
},
//获取视野
getRegion(){
await new Promise(resolve => {
this.mapContext.getRegion({
success: (res) => {
let southwest = [res.southwest.latitude, res.southwest.longitude]
let northeast = [res.northeast.latitude, res.northeast.longitude]
this.QueryDeviceList.input.value.southwest = southwest
this.QueryDeviceList.input.value.northeast = northeast
resolve()
}
})
})
},
//设置视野
setRegion (res) {
let southwest = [res.southwest.latitude, res.southwest.longitude]
let northeast = [res.northeast.latitude, res.northeast.longitude]
this.QueryDeviceList.input.value.southwest = southwest
this.QueryDeviceList.input.value.northeast = northeast
},
//移动
setMoveToLocation () {
this.mapContext.moveToLocation({
latitude: this.map.latitude,
longitude: this.map.longitude
})
},
//根据请求的数据设置标记点的位置以及图标
setCovers (data) {
data.forEach((i, index) => {
i.id = index
i.width = 80
i.height = 80
i.deviceLabel = deviceLabel
i.iconPath = '../../static/img/ddd.png'
}
})
this.map.covers = data
},
//拖动视野
async regionchange(data) {
// if (data.detail.type === 'end' && data.detail.causedBy === "drag") {
if (data.type === 'end' && data.causedBy === "drag") {
console.log('地图视图改变')
this.methods('setRegion', data.detail.region)
//请求数据
let list = await this.methods('queryFaultList')
if (list) {
this.methods('setDeviceShow', false)
// 设置选中状态的markerId
this.methods('setMarkerNoSelected')
this.methods('setOldMarker', -1)
this.methods('setCovers', list)
}
}
},
//点击标记点
markertap(data) {
console.log(data)
let markerId = data.detail.markerId
if (this.map.oldMarker === markerId) return
// 显示标记详情
this.methods('setDeviceShow', true) //是否显示详情弹框
this.methods('setDeviceData', markerId) //显示的数据
// 把上一次选中状态变成未选中
this.methods('setMarkerNoSelected')
// 图标改成选中状态
this.methods('setMarkerSelected', markerId)
// 设置选中状态的markerId
this.methods('setOldMarker', markerId)
},
// 把图标上一次选中状态变成选中
setMarkerSelected (markerId) {
let markers = this.map.covers[markerId]
if (markers && markers.iconPath) {
let iconPath = markers.iconPath.split('.png')
markers.iconPath = iconPath[0] + '_selected.png'
}
}
// 把图标上一次选中状态变成未选中
setMarkerNoSelected () {
if (this.map.oldMarker || this.map.oldMarker === 0) {
let oldMarker = this.map.covers[this.map.oldMarker]
if (oldMarker && oldMarker.iconPath) {
oldMarker.iconPath = oldMarker.iconPath.replace('_selected', '')
}
}
}
}
2.总结一个一般初学者常见的遇到的问题
1对一些需求需要用到下载模板的api处理
// 下载
export function downloadExcel() {
return request({
url: 'api/projectCost/downloadExcel',//自己的下载api接口
method: 'get',
responseType: 'blob',
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
// new Blob([res])中不加data就会返回下图中[objece objece]内容(少取一层)
const blob = new Blob([res], { type: 'application/vnd.ms-excel' })
const fileName = '项目电费管理模板.xlsx'// 下载文件名称
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // 释放URL 对象
document.body.removeChild(elink)
})
}
//写好了就在需要用的地方调用这个函数就行
data(){
return{
..........
methods:{
click(){
downloadExcel()//api
}
}
}
}
2把文件上传到后台的代码做一个详情的讲解
<template>
<el-form :model="form">
<el-form-item label="上传文件" :label-width="formLabelWidth">
<el-upload
ref="uploadExcel"
class="upload-demo"
:limit="limitNum"//文件的限制 比如设置5就是只能最大限制5个
:auto-upload="false"
accept=".xlsx"
:before-upload="beforeUploadFile"//文件上传之前的回调
:on-change="fileChange"//文件改变触发的回调
:on-exceed="exceedFile"//超出限制的回调
:on-success="handleSuccess"//成功的回调
:on-error="handleError"//失败的回调
:file-list="fileList"
:headers="headers"
:action="CostApi"//您要上传的api接口
>
<i class="el-icon-upload" />
<el-button size="small" plain>选择文件</el-button>
<div slot="tip" class="el-upload__tip">只能选择excel文件</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button type="success" @click="download">下载模板</el-button>
</el-form-item>
<el-form-item>
<div class="foot">
<el-button size="small" @click="cancel">取消</el-button>
<el-button size="small" type="primary" @click="uploadFile">立即上传</el-button>
</div>
</el-form-item>
</el-form>
</template>
<script>
import { getToken } from '@/utils/auth'
import { mapGetters } from 'vuex'
import { downloadExcel } from '@/api/operation/cost.js'
import { header, crud } from '@crud/crud'
export default {
mixins: [header(), crud()],
data() {
return {
headers: { 'Authorization': getToken() },
limitNum: 5,
formLabelWidth: '80px',
form: {
file: ''
},
fileList: []
}
},
computed: {
...mapGetters([
'baseApi',
'CostApi'
])
},
methods: {
// 下载模板
download() {
downloadExcel()
},
// 取消
cancel() {
this.$emit('cancel')
},
// 文件超出个数限制时的钩子
exceedFile(files, fileList) {
this.$notify.warning({
title: '警告',
message: `只能选择 ${this.limitNum} 个文件,当前共选择了 ${files.length + fileList.length} 个`
})
},
// 文件状态改变时的钩子
fileChange(file, fileList) {
this.form.file = file.raw
console.log(this.form.file)
},
// 上传文件之前的钩子, 参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传
beforeUploadFile(file) {
this.$emit('upload')
// console.log('before upload')
// console.log(file)
const extension = file.name.substring(file.name.lastIndexOf('.') + 1)
const size = file.size / 1024 / 1024
if (extension !== 'xlsx') {
this.$notify.warning({
title: '警告',
message: `只能上传Excel2017(即后缀是.xlsx)的文件`
})
}
if (size > 10) {
this.$notify.warning({
title: '警告',
message: `文件大小不得超过10M`
})
}
},
// 文件上传成功时的钩子
handleSuccess(res, file, fileList) {
this.$notify.success({
title: '成功',
message: `文件上传成功`
})
this.crud.refresh()
},
// 文件上传失败时的钩子
handleError(file, fileList) {
this.$notify.error({
title: '错误',
message: `文件上传失败`
})
},
uploadFile() {
this.$refs.uploadExcel.submit()
}
}
}
</script>
<style lang="scss" scoped>
.foot{
margin-left: 75%;
}
</style>
3自己简单的封装tabs
<template>
<view class="facilty">
<view class="map">
<map style="width: 93%;" :latitude="latitude" :longitude="longitude"></map>
</view>
<view class="tab">
<view class="tab-left" @click="change(0)" :class="[current==0?'activeTab':'text']">//点击哪个的时候自己就加样式
<view class="tab-title">
tabs1
</view>
</view>
<view @click="change(1)" class="tab-right" :class="[current==1?'activeTab':'text']">
<view class="tab-title" style="margin-left: 30rpx;">
tabs2
</view>
</view>
</view></view>
</block>
</view>
</template>
<style>
.text{
background-color: #FFFFFF;
}
.facilty {
background-color: #f2f2f2;
}
</style>
本地存储的使用
let a=localStorage.setItem('lin',JSON.stringify([1,2,3,6]))
console.log( JSON.parse(localStorage.getItem('lin')) );
小程序获得用户头像信息
login() {
uni.getUserProfile({
desc: '用于完善会员资料',//必须填
success: (res) => {
console.log(res)
}
})
},
vue实现文件上传
<template>
<el-form :model="form">
<el-form-item label="上传文件" :label-width="formLabelWidth">
<el-upload
ref="uploadExcel"
class="upload-demo"
:limit="limitNum"
:auto-upload="false"
accept=".xlsx"
:before-upload="beforeUploadFile"
:on-change="fileChange"
:on-exceed="exceedFile"
:on-success="handleSuccess"
:on-error="handleError"
:file-list="fileList"
:headers="headers"
:action="CostApi"
>
<i class="el-icon-upload" />
<el-button size="small" plain>选择文件</el-button>
<div slot="tip" class="el-upload__tip">只能选择excel文件</div>
</el-upload>
</el-form-item>
</el-form>
</template>
<script>
import { getToken } from '@/utils/auth'
import { mapGetters } from 'vuex'
import { downloadExcel } from '@/api/operation/cost.js'
import { header, crud } from '@crud/crud'
export default {
mixins: [header(), crud()],
data() {
return {
headers: { 'Authorization': getToken() },
limitNum: 5,
formLabelWidth: '80px',
form: {
file: ''
},
fileList: []
}
},
computed: {
...mapGetters([
'baseApi',
'CostApi'
])
},
methods: {
// 文件超出个数限制时的钩子
exceedFile(files, fileList) {
this.$notify.warning({
title: '警告',
message: `只能选择 ${this.limitNum} 个文件,当前共选择了 ${files.length + fileList.length} 个`
})
},
// 文件状态改变时的钩子
fileChange(file, fileList) {
this.form.file = file.raw
console.log(this.form.file)
},
// 上传文件之前的钩子, 参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传
beforeUploadFile(file) {
this.$emit('upload')
// console.log('before upload')
// console.log(file)
const extension = file.name.substring(file.name.lastIndexOf('.') + 1)
const size = file.size / 1024 / 1024
if (extension !== 'xlsx') {
this.$notify.warning({
title: '警告',
message: `只能上传Excel2017(即后缀是.xlsx)的文件`
})
}
if (size > 10) {
this.$notify.warning({
title: '警告',
message: `文件大小不得超过10M`
})
}
},
// 文件上传成功时的钩子
handleSuccess(res, file, fileList) {
this.$notify.success({
title: '成功',
message: `文件上传成功`
})
this.crud.refresh()
},
// 文件上传失败时的钩子
handleError(file, fileList) {
this.$notify.error({
title: '错误',
message: `文件上传失败`
})
},
uploadFile() {
this.$refs.uploadExcel.submit()
}
}
}
</script>
<style lang="scss" scoped>
.foot{
margin-left: 75%;
}
</style>
nodejs
1.获得请求报文 不写则获取不到值
// 定义内置中间件来获取请求报文
app.use(express.urlencoded())
app.use(express.json())
//get 拿到请求报文是req.query
//这种的get /delectHero/:id拿取是req.params
//post是req.boby
2.在全局注册mysql
let execSql=require("./util/sql")
// 注入全局
app.use((req,res,next)=>{
res.execSql=execSql
// 触发下一个函数的执行
next()
})
// 导入并注册中间件
const heroRouter=require("./Router/heroRoter")
app.use("/hero",heroRouter) 需要拼接/hero
3生成token
下载第三方包
const josnwebtoken=require('jsonwebtoken')
function creattoken(username){
// 定义一个加密的密码
// jsonwebtoken.sign(表示你要给token添加的数据对象,加密的密码,token的过期时间)
let tokenpwd=TOKENPW
let expiresIn={ expiresIn: 60*60 }
return "Bearer "+ josnwebtoken.sign({username},tokenpwd,expiresIn)
}
用法
Router.get("/login",(req,res)=>{
let {username,password}=req.query
if(!username||!password){
res.send({code:201,mag:"请输入账号或者密码"})
return
}
let sql=`select * from user where username = '${username}' and password ='${password}'`
res.execSql(sql,(err,result)=>{
if(err){
res.send({ code: 500, msg: err.message })
return
}
if(result.length<=0){
res.send({code:201,mag:'该账号不存在'})
return
}
res.send({code:200,msg:"登入成功",token:creattoken(username)})
})
})