微信小程序——个人相册(前端)

项目效果图




1.项目功能

1)用户管理,信息包括:头像、昵称,功能包括:获取微信用户信息、验证用户是否存在、修改头像、修改昵称
2)上传相片:上传图片
3)照册列表:封面图(轮播图)、照片列表、照片选择、删除照片
4)照片信息:照片信息包括 显示照片、大小(字节)、上传时间

2.项目结构

3.数据库

此项目使用MySQL8.0,创建数据库myphoto,SQL文件我已放在文章顶部。


4.创建项目

  1. 小程序项目名:myphoto
  2. 使用2.30.0版本的基础库以及不启用远程校验
  3. 准备好appid和appsecret,后端需要用到

5.代码

5.1 项目准备

  1. app.js

// app.js
App({async onLaunch(){let res=await wx.login();let code=res.code;wx.request({url: this.globalData.rootPath+'/account/getUserInfo',data:{code},success:(result)=>{let userInfo=result.data;if (userInfo) {this.globalData.userInfo = userInfo;wx.redirectTo({url: '/pages/index/index',})} else {wx.redirectTo({url: '/pages/mine/mine'});}}})},globalData: {rootPath:"http://localhost:8089/myhome_war_exploded",//请修改为自己的服务根路径userInfo: null,},
})
  1. app.json

{"entryPagePath": "pages/index/index","pages": ["pages/index/index","pages/File/File","pages/mine/mine","pages/detail/datail"],"window": {"backgroundTextStyle": "light","navigationBarBackgroundColor": "#fff","navigationBarTitleText": "Weixin","navigationBarTextStyle": "black"},"tabBar": {"selectedColor": "#33a3dc","list": [{"pagePath": "pages/index/index","text": "首页","iconPath": "./pages/images/1.png","selectedIconPath": "./pages/images/1-1.png"},{"pagePath": "pages/File/File","text": "文件","iconPath": "./pages/images/2.png","selectedIconPath": "./pages/images/2-2.png"},{"pagePath": "pages/mine/mine","text": "我的","iconPath": "./pages/images/3.png","selectedIconPath": "./pages/images/3-3.png"}]},"style": "v2","sitemapLocation": "sitemap.json"
}

5.2 mine(用户登录界面)

页面需求:
① 打开小程序时,先从服务端根据用户的code获取用户信息,如果用户存在,打开index,不存在,跳转到mine
② 把从服务端获取的用户信息保存到全局对象中

获取用户信息
① 进入mine.js时判断全局对象中是否有用户信息的缓存,如果有,显示用户信息,如果没有,显示“获取用户信息”按钮。
② 点击按钮,获取微信用户信息,获取成功后隐藏“获取用户信息按钮”,并显示用户头像和昵称
③ 保存用户信息到全局对象中
保存用户信息
① 如果是获取的微信用户信息,把用户信息保存到服务端
② 在服务端保存用户信息前需通过小程序传入的code来获取openId(用于唯一标识 每一个微信用户)。
③ 数据库的主键使用UUID生成,长度为32位(去掉“-”字符)
服务端返回生成的用户ID,在小程序端,把返回的用户id添加到缓存的用户信息中
修改昵称
① 点击my视图中“修改昵称”按钮,昵称从显示状态切换为可编辑状态,同时按钮的文字修改为“确定修改”
② 修改昵称后,点击“确定修改”按钮,把修改后的昵称提交到服务端保存,并修改小程序中缓存用户信息的昵称
修改头像
① 点击头像,从本地相册中选择一张照片,确定后先替换本地头像
② 把新头像上传到服务端,并保存存放路径和访问路径保存
服务端返回访问路径到小程序,更新缓存中用户头像的路径和my中头像的路径

  1. mine.wxml

<!--pages/mine/mine.wxml-->
<view class="container">
<block wx:if="{{!hasUserInfo}}">
<button bindtap="getUserInfo">获取用户信息</button>
</block>
<block wx:else>
<image src="{{userInfo.avatarUrl}}" style="border-radius: 50%; height:400rpx;width:400rpx;" bindtap="modifyAvatar"/>
<text style="padding-top: 100rpx; font-size: 50rpx;" hidden="{{isModify}}">{{userInfo.nickName}}</text>
<form bindsubmit="modifynickname"><input type="text" name="nickname" style="margin-top: 100rpx; padding: 10rpx;font-size: 50rpx; border:2rpx solid #ddd;" hidden="{{!isModify}}" value="{{userInfo.nickName}}"/>
<button form-type="submit" type="primary" style="margin-top: 100rpx;">
{{btnTitle}}
</button>
</form>
</block>
</view>
  1. mine.js

const app=getApp();
const {req} =require("../../utils/repuest")
Page({data:{hasUserInfo:false,isModify:false,btnTitle:'修改昵称',userInfo:{avatarUrl:'http://localhost:8089/myhome/photo/1.png',nickName:'当前用户昵称'}},onLoad(){let userInfo=app.globalData.userInfo;if(userInfo){if(!userInfo.avatarUrl.startsWith("http")){userInfo.avatarUrl=app.globalData.rootPath+userInfo.avatarUrl;}this.setData({userInfo:userInfo,hasUserInfo:true})}},
getUserInfo(){wx.getUserProfile({desc: 'desc',success:(res)=>{let userinfo=res.userInfo;//  userinfo.accId='';this.setData({userinfo:userinfo,hasUserInfo:true,nickName:this.data.nickName})this.saveUserInfo();}})},async saveUserInfo(){let userInfo=this.data.userinfo;let code=(await(await wx.login())).code;req({url:"/account/save",method:"POST",header:{'content-type':'application/x-www-form-urlencoded'},data:{...userInfo,code},}).then((res)=>{let accId=res.data;if(accId){userInfo.accId=accId;app.globalData.getUserInfo=userInfo;this.setData({userinfo:userInfo})}});},modifynickname(e){let isModify=this.data.isModify;if(!isModify){this.setData({isModify: true,inputPlaceholder: "请输入新昵称",btnTitle: "确定修改"})}else{this.setData({isModify: false,inputPlaceholder: "当前昵称",btnTitle: "修改昵称"})this.updateNickName;}},updateNickName(e){let userInfo=this.data.userInfo;let nickName = e.detail.value.nickName; // 假设昵称输入框的值为nickNamelet accId = userInfo.accId; // 假设用户的accid存在于userInfo中// 调用req方法访问服务器实现昵称修改req({url: 'http://localhost:8089/myhome/account/modifyNickName',//请修改为自己的实际端口号method: 'POST',data: {nickName: nickName,accId:accId},success: function(response) {// 根据服务器返回的结果进行相应的处理console.log('昵称修改成功', response);// 可以根据需要更新页面的数据或进行其他操作},fail: function(error) {console.log('昵称修改失败', error);// 可以根据需要进行错误处理或提示用户}});},uploadAvatar(filePath){wx.uploadFile({filePath: filePath,name: 'avatar',url:"http://localhost:8089/myhome/account/uploadAvatar",formData:{accId:this.data.userinfo.accId},success(res){let avatarUrl=res.data;if(avatarUrl){this.setData({"userinfo.avatarUrl":app.globalData.rootPath+avatarUrl})}}})},modifyAvatar(){wx.chooseMedia({count:1,mediaType:'image',sourceType:'album'}).then(res=>{let avatarUrl=res.tempFiles[0].tempFilePath;this.setData({'userinfo.avatarUrl':avatarUrl})this.uploadAvatar(avatarUrl);}).catch(res=>{})}
})

5.3 File(文件上传界面)

页面需求
① 在upload视图中“上传”按钮默认为禁用,点击按钮上方的大图标,可在本地相册中选择要上传的照片(一次只能选择一张)
② 确定后,用选中的照片替换大图标,“上传”按钮设为可用
③ 点击“上传”,把照片上传到服务端
④ 上传成功后,照片区恢复显示大图标,“上传”按钮为禁用

  1. File.wxml

<!--pages/File/File.wxml-->
<view style="padding:40rpx;box-sizing: border-box;display: block;">
<view style="text-align: center;color: #f00;">点击以下图片选择相片</view><image src="{{filePath}}" style="width: 100%;" mode="{{mode}}" bindtap="selectPhoto"></image><button bindtap="uploadPhoto" disabled="{{!isSelectImg}}" type="primary">上传</button>
</view>
  1. File.js

//获取应用实例
var app = getApp();
Page({data: {filePath:"/pages/images/2-2.png",mode:"center",isSelectImg:false},selectPhoto(){wx.chooseMedia({count:1,mediaType:'image',sourceType:'album'}).then(res=>{let filePath = res.tempFiles[0].tempFilePath;this.setData({filePath:filePath,isSelectImg:true})}).catch(res=>{})
},
uploadPhoto(){let _this = this;wx.uploadFile({filePath: this.data.filePath,name: 'file',url: getApp().globalData.rootPath+'/photo/upload',formData:{accId: getApp().globalData.userInfo.accId},success:res=>{console.log(res);if(res.data.replaceAll("\"","") =="1"){wx.showToast({title: '上传成功',})}else{wx.showToast({title: '上传失败',icon:"error"})}this.setData({filePath:"/pages/images/2-2.png",mode:"center",isSelectImg:false})}})
},
});

5.4 index(首页)

页面需求:
① 在进入index页面时首先判断缓存中是否有用户信息
② 如果不没有,页面跳转到my,并每隔1秒轮询一次,直到用户信息存在后停止轮询
③ 如果用户存在,从服务端此用户的照片(一次最多10张,最后上传的照片显示在最前面)
在页面中显示(一排显示两张)
触底分页
① 当照片滚动到视图最下方时,从服务端加载新的照片(和上一轮的照片数量相同)
② 新加载的照片追加到已显示照片的后方
开始加载前显示加载动画,加载完成后结束加载动画
下拉刷新
① 开启index页面的下拉刷新功能
在页面的下拉刷新事件回调中清空已显示的所有照片,重新从服务端加载照片
长按显示复选框和操作栏
① 在照片上长按,在每个照片的左上角显示复选框,并在视图的底部显示操作栏
② 复选框默认时都为未选中
③ 操作栏中的操作包括“取消”、“全选”、“删除”三项
取消操作
① 点击操作栏中的“取消”完成以下操作
② 清空所有选中
③ 隐藏复选框
④ 隐藏操作栏
勾选和取消勾选、全选操作
① 点击照片上的复选框,如果勾选,记录选中的照片ID
② 如果是取消勾选,从记录中删除对应的照片ID
③ 点击“全选”,勾选所有复选框,并记录所有被选中的照片ID
删除
① 点击操作栏中“删除”,删除所有被勾选的照片
② 如果未选择要删除的照片,提示“请勾选要删除的照片”
③ 删除前需询问“确定要删除选中的照片吗?”,确定后方能删除
删除成功后刷新照片

  1. index.wxml

<swiper indicator-dots="{{true}}" current="{{currentIndex}}" autoplay>
<swiper-item wx:for="{{covers}}" wx:key="*this">
<image src="{{item.accessUrl}}" style="width: 100%;" mode="widthFix"></image>
</swiper-item>
</swiper>
<scroll-view style="padding: 60rpx 40rpx;box-sizing: border-box;">
<view style="display: flex;flex-wrap: wrap;">
<view wx:for="{{photoList}}" wx:key="*this" wx:for-item="photo" style="flex:none;width:50%;height:300rpx;box-sizing:border-box;padding:6rpx;position:relative;">
<checkbox mark:photoId="{{photo.photoId}}" mark:index="{{index}}" style="position: absolute;" hidden="{{!delSelect}}" bindtap="checkedPhoto" checked="{{selectList[index]}}"/>
<image src="{{photo.photoAccessUrl}}" mode="" style="width: 100%;height: 100%;" bindlongpress="enabelDelSelect" bindtap="showImage" mark:index="{{index}}"/>
</view>
</view>
</scroll-view><button loading="{{loadding}}" style="background: #fff;"></button>
<view style="position: fixed;bottom: 0;width: 100%;display: flex;justify-content: space-between;box-sizing: border-box;padding: 10rpx 20rpx;" wx:if="{{delSelect}}">
<text bindtap="cancelSelect" style="color: #00f;text-decoration: underline;">取消</text>
<text bindtap="selectAll" style="color: #00f;text-decoration: underline;">全选</text>
<text bindtap="deletePhoto" style="color: #00f;text-decoration: underline;">删除</text></view>
  1. index.js

// index.js
// 获取应用实例
const app = getApp()
const {req}=require("../../utils/repuest")Page({data: {covers:[],photoList:[],delSelect:false,selectList:[],pageNum:1,loadding:true,currentIndex:0},selectPhotoList(){this.setData({loadding:true});req({url:"/photo/list",data:{accId:app.globalData.userInfo.accId,pageNum:this.data.pageNum}}).then(res=>{let photoList = res.data;if(photoList.length > 0){for(let photo of photoList){photo.photoAccessUrl = app.globalData.rootPath+photo.photoAccessUrl;}this.setData({photoList:this.data.photoList.concat(photoList),pageNum:this.data.pageNum+1})}else{// wx.showToast({//   title: '图片以全部加载',// })}this.setData({loadding:false})})},onLoad() {let time = setInterval(()=>{let userInfo = app.globalData.userInfo;if(userInfo){clearInterval(time);this.selectCover();this.selectPhotoList();}},1000);},onPullDownRefresh(){this.setData({covers:[],photoList:[],pageNum:1,currentIndex:0});this.selectCover();this.selectPhotoList();},//在照片上长按,在每个照片的左上角显示复选框,并在视图的底部显示操作栏enabelDelSelect(){this.setData({delSelect:true});},/*
① 点击操作栏中的“取消”完成以下操作
② 清空所有选中
③ 隐藏复选框
隐藏操作栏
*/cancelSelect(){this.setData({selectList:[],delSelect:false});},onReachBottom(){console.log(this.data.pageNum);this.setData({loadding:true});this.selectPhotoList();},checkedPhoto(e){let index = e.mark.index;let photoId = e.mark.photoId;let selectList = this.data.selectList;if(selectList[index]){selectList[index]=undefined;}else{selectList[index]=photoId;}this.setData({selectList:selectList})},selectAll(){let selectList = [];let photoList = this.data.photoList;for(let photo of photoList){selectList.push(photo.photoId);}this.setData({selectList:selectList});},deletePhoto(){let selectList = this.data.selectList;let deleteList = '';for(let id of selectList){if(id){deleteList+='&photoIds='+id;}}if(deleteList == ''){wx.showToast({title: '请勾选要删除的照片',})}else{wx.showModal({title: '确定要删除选中的照片吗?',content: '',complete: (res) => {if (res.cancel) {}if (res.confirm) {req({url:"/photo/delete?photoIds=''"+deleteList,method:"GET"}).then(res=>{this.setData({photoList:[],pageNum:1});this.cancelSelect();this.selectPhotoList();})}}})}},showImage(e){let index = e.mark.index;let photoList = this.data.photoList;wx.navigateTo({url: '/pages/detail/datail?photoList='+JSON.stringify(photoList)+"&index="+index+"&coverLength="+this.data.covers.length,})},selectCover(){req({url:'/cover/list?accId='+app.globalData.userInfo.accId,method:'GET',}).then(res=>{let cover = res.data;console.log(cover);this.setData({covers:cover,currentIndex:0})})},onShow(){// this.selectCover();// this.selectPhotoList();}
})

5.5 detail(照片详情界面)

页面需求:
① 在index中点击照片,跳转到照片详情页
② 显示点击的照片和照片的大小和上传时间
③ 可通过在照片上左、右滑动切换照片(以及照片的信息),可切换的照片范围为index中所显示的所有照片
设置封面
① 在detail视图中,如果上方显示的照片是封面,默认勾选“设为封面”复选框;
② 取消复选框的勾选,把状态传递到服务端,删除对应的封面设置
③ 点击复选框勾选,把状态传递到服务端,添加此照片为封面
④ 最多只能添加四个封面,如果勾选后此照片为第五个封面,提示“最多只能设置四个封面”,自动取消选中
如果设置和取消成功提示“封面设置成功”
查询封面
① 进入index页面时查询此用户的封面(轮播图),并显示,封面最多四张,自动播放。
② 在detail页面中设置封面时,更新此页面的封面列表

  1. detail.wxml

<view class="container">
<image src="{{photo.photoAccessUrl}}" style="padding: 60rpx;width: 100%;height: 400rpx;" bindtouchstart="start" bindtouchmove="end"/>
</view>
<view style="text-align: left;padding-top: 10rpx;">
<checkbox bindtap="fm" checked="{{isFm}}">设置封面</checkbox>
<text style="text-align: left;">\n图片大小:{{photo.photoSize}}</text>
<text>\n上传时间:{{uploadTime}}</text>
</view>
  1. detail.js

const app = getApp()
const {req}=require("../../utils/repuest")
Page({/*** 页面的初始数据*/data: {photoList:[],index:0,photo:{},isFm:false,uploadTime:'',startPoint:0,startFlag:false,cover:{},coverLength:0},/*** 生命周期函数--监听页面加载*/onLoad:function(res) {let photoList = JSON.parse(res.photoList);let index = res.index;let coverLength = res.coverLength;let photo = photoList[index];let uploadTime = new Date(Number.parseInt(photo.uploadTimestamp));let s = uploadTime.toLocaleString();this.setData({photoList:photoList,photo:photo,uploadTime:s,index:index,coverLength:coverLength});this.selectCover();},selectCover(){req({url:'/cover/getOne?accId='+app.globalData.userInfo.accId+"&photoId="+this.data.photo.photoId,method:'GET'}).then(res=>{if(res.data){this.setData({cover:res.data,isFm:true});}else{this.setData({isFm:false});}})},fm(){console.log(this.data.isFm);if(!this.data.isFm){console.log(this.data.coverLength);if(this.data.coverLength<4){req({url:'/cover/save',method:'POST',header:{"Content-Type":"application/x-www-form-urlencoded"},data:{accId:app.globalData.userInfo.accId,photoId:this.data.photo.photoId,accessUrl:this.data.photo.photoAccessUrl}}).then(res=>{this.setData({coverLength:Number.parseInt(this.data.coverLength)+1});this.selectCover();})}else{wx.showM({title: '封面只能设置四个',})}}else{req({url:'/cover/delete?coverId='+this.data.cover.coverId,method:'GET'}).then(res=>{this.setData({coverLength:Number.parseInt(this.data.coverLength)-1});this.selectCover();})}},start(e){this.setData({startPoint:e.touches[0].clientX,startFlag:true})},end(e){let point = this.data.startPoint;if(((point-e.touches[e.touches.length-1].clientX)>50) && this.data.startFlag){let index = this.data.index;console.log(index);if(index-1<0){index = this.data.photoList.length-1;}else{index = index-1;}let photo = this.data.photoList[index];let uploadTime = new Date(Number.parseInt(photo.uploadTimestamp));let s = uploadTime.toLocaleString();this.setData({photo:photo,index:index,uploadTime:s,startFlag:false,startPoint:0});}else if(((point-e.touches[e.touches.length-1].clientX)<-50) && this.data.startFlag) {let index = this.data.index;console.log(index);if(index+1>this.data.photoList.length-1){index = 0;}else{index = index+1;}let photo = this.data.photoList[index];let uploadTime = new Date(Number.parseInt(photo.uploadTimestamp));let s = uploadTime.toLocaleString();this.setData({photo:photo,index:index,uploadTime:s,startFlag:false,startPoint:0});}this.selectCover();},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {},/*** 生命周期函数--监听页面显示*/onShow() {},/*** 生命周期函数--监听页面隐藏*/onHide() {},/*** 生命周期函数--监听页面卸载*/onUnload() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {},/*** 页面上拉触底事件的处理函数*/onReachBottom() {},/*** 用户点击右上角分享*/onShareAppMessage() {}
})

总结

作为一个微信小程序个人相册的开发者,我对这个项目有一些自我总结和反思。

首先,我认为成功的部分在于我能够充分理解用户的需求,并且提供了一个简单易用的相册功能。用户可以方便地上传照片、创建相册、查看照片信息等等。

其次,我采用了适当的设计和布局,使得小程序界面美观而且用户友好。我尽量减少了复杂的操作流程,让用户能够轻松地浏览和管理他们的照片。同时,我也在界面上保持了一定的一致性,使得用户可以很快熟悉和掌握小程序的使用方式。

然而,也存在一些需要改进的地方。首先,我认识到在小程序的功能方面还有一些局限性。例如,目前我只实现了基本的照片管理功能,但是还有很多其他可能的功能可以加入,比如照片编辑、滤镜效果、批量操作、分享照片、下载照片等等。这些功能可以提升用户的体验,并增加小程序的吸引力。

其次,我需要进一步改进小程序的性能和加载速度。尽管我尽力优化了代码和资源,但是随着用户上传的照片数量增加,小程序的加载速度可能会变慢。我需要继续寻找更好的方法来提高性能,以确保用户能够流畅地使用小程序。

最后,用户反馈对于改进小程序也非常重要。我会积极收集用户的意见和建议,并根据他们的反馈来改进小程序的功能和界面设计。通过不断改进和迭代,我相信我可以打造出一个更好的微信小程序个人相册。

总而言之,开发微信小程序个人相册是一次很有意义的经历。我学到了很多关于用户需求、设计和性能优化的知识,并且锻炼了我的开发技能。通过不断改进和学习,我希望能够为用户提供更好的相册体验,并且不断完善和扩展这个小程序。

后端介绍
最后献上整体项目源码:个人相册(前端)

微信小程序——个人相册(前端)相关推荐

  1. vue和小程序哪个好学一点_litemall,Spring Boot后端,微信小程序用户前端 + Vue用户移动端...

    litemall 又一个小商场系统. litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端 注意: 由于第一次加载数据量较大,建议wif ...

  2. 通过微信小程序看前端

    前言 2016年9月22日凌晨,微信官方通过"微信公开课"公众号发布了关于微信小程序(微信应用号)的内测通知.整个朋友圈瞬间便像炸开了锅似的,各种揣测.介绍性文章在一夜里诞生.而真 ...

  3. 微信小程序的开发:通过微信小程序看前端

    前言 2016年9月22日凌晨,微信官方通过"微信公开课"公众号发布了关于微信小程序(微信应用号)的内测通知.整个朋友圈瞬间便像炸开了锅似的,各种揣测.介绍性文章在一夜里诞生.而真 ...

  4. 微信小程序uni-app前端应用框架和HBuilderX工具使用及Git管理项目

    微信小程序uni-app前端应用框架和HBuilderX工具使用及Git管理项目 官方地址:https://uniapp.dcloud.io/ 1.起步 1.1.简介 uni-app 是一个使用 Vu ...

  5. 云答题微信小程序 实现 前端加后台管理

    1.为什么要使用微信云开发 微信云开发自己是不需要域名(备案的域名),服务器,搭建数据库等. 2.使用技术 微信小程序+相关云接口 java (SpringBoot+Maven) 后台管理使用 VUE ...

  6. 微信小程序纯前端生成海报并保存本地

    需求 公司开发微信小程序,有一个海报页面,需要用户点击生成海报,可以将该该swipe-item 生成一个带二维码的图片,最终由纯前端实现! 技术调研 因为小程序的打包限制,不可能将所有的图片都放在代码 ...

  7. 从微信小程序看前端代码安全

    原文链接:https://share.whuboy.com/weapp.html 感觉写的很好,学习了,转载保存. 起初在研究对移动网络传输进行功耗优化,在一次意外的监听网络传输包中截获了微信小程序的 ...

  8. 微信小程序之前端与java后台进行数据交互

    后台是用SpringBoot+SSM来写的,整体上来说,和普通的Web没什么区别 要注意的是,对于小程序访问的控制层方法,需要返回一个Map类型 @RequestMapping(value = &qu ...

  9. 微信小程序,前端大梦想(七)

    微信小程序之数据缓存实例-备忘录 数据缓存在移动端的使用是非常重要的,既可以减少用户的流量支出又可以提高程序的访问速度和用户体验.每个微信小程序都可以有自己的本地缓存,可以通过 wx.setStora ...

最新文章

  1. Microbiome:芝麻菜中肠杆菌科主导核心微生物组并贡献抗生素抗性组
  2. java 用户登录token_Java,SpringBoot采用token方式实现登录认证
  3. 【网址收藏】Docker中ADD和COPY的区别
  4. 【Linux】一步一步学Linux——paste命令(58)
  5. SAP Spartacus 服务器端渲染优化引擎的参数 SsrOptimizationOptions
  6. 网络七层协议之物理层
  7. 01.神经网络和深度学习 W3.浅层神经网络(作业:带一个隐藏层的神经网络)
  8. numpy matrix 矩阵对象
  9. 【转】Java中的关键字 transient
  10. 成功解决不能完成“视频帧到图层”的命令,因为需要QuickTime7.1或者更高版本
  11. 计算机专业毕设java选题参考
  12. 4场直播丨EsgynDB连接服务层、Oracle、openGauss
  13. 流体力学有限元法(二)
  14. Web语音播报提示音
  15. VR乒乓球项目Unity3D 开发经验整理,1总纲
  16. Eclipse相关技术总结
  17. IDEA 设置单行注释格式化时不换行
  18. DICOM 图像传输:使用 LeadTools 实现 C-Store SCP 服务
  19. xmind各版本区别_制图心法 | 一文读懂XMind 8 和 XMind: ZEN的真正区别
  20. JVM——引用计数算法与可达性分析算法

热门文章

  1. 《ReactNative》app调用小程序的分享
  2. 计算机的3种端口,端口是什么 端口分为几种
  3. 2022-2028全球冷却开关阀行业调研及趋势分析报告
  4. webgl室内3d场景
  5. 【基础】Unity:欧拉角与万向节死锁(图文版)
  6. vivo x70pro和vivo x70proplus 的区别 vivo x70pro和vivo x70proplus哪个好
  7. Top-1 Accuracy和Top-5 Accuracy
  8. 移通创联MODBUS转PROFIBUS网关在高炉上配料系统中的案例
  9. 3dsmax模型和导入模型到unity3d 设置
  10. 河北经贸大学计算机类专业评价报告,河北经贸大学经济管理学院计算机专业怎么样...