系统分析设计期末大项目——闲得一币TimeForCoin小程序前端
小程序项目地址_闲得一币
小程序实现
页面的划分
根据需求文档以及UI设计文档,并结合实际情况,将页面划分为如下四个大页:
- 主页
- 发布页
- 消息页
- 个人信息页
接下来依照页面的顺序进行小程序实现过程的简介。
主页
主页可以划分为三个区域:主页图像展示区、搜索区、推荐任务区。
主页图像展示区
图像展示区使用swiper
标签实现滑动图片显示。其中的indicator-active=color
以及indicator-color
分别调节底部小圆点的初始颜色以及相对应图片显示时候的颜色。
对于图像,其src是双向绑定的,即通过Controller
的代码进行控制。
内容在线获取
无论是顶部图像、文字内容等都是在线获取的(因为小程序只能最大2MB),所以单独将server
封装成类并进行调用(在此感谢zhenly强大的后端以及帮助实现server
类的封装)。调用时需要输入请求的类型(PUT、POST、DELETE、GET等)、url以及参数。除此之外,还需要对返回的数据进行处理。如果状态码不为200(res.statusCode !== 200
)需要进行异常处理。
举例:首页图片的获取
app.editTabbar();this.loadTaskList(1);const res = await server.request('GET', 'article',{page: 1,size: 3})if(res.statusCode === 200){if(res.data.data.length === 0){this.setData({ showImg: false }) } else{var index = 0for(var val of res.data.data){var tmp = {index: index,url:val.images[0].url}index = index + 1top_images.push(tmp)}this.setData({top_images: this.data.top_images,showImg: true})}
<swiper class="top_image" indicator-dots="true" autoplay="true" interval="5000" duration="1000" indicator-color="white" indicator-active-color="#ff7e67" hidden="{{top_images.length === 0 ? true : false}}" wx:if="{{showImg}}"><block wx:for="{{top_images}}" wx:key="index"><swiper-item><image src="{{item.url}}" class="slide-image" mode="aspectFill" data-previewurl='{{top_images.url}}' data-currenturl='{{item}}' /></swiper-item></block></swiper>
因为没有获取到图片,所以不进行显示。
搜索区域
如果直接在主页中实现搜索,需要通过遮罩mask对后面的内容进行遮挡。除此之外,因为还需要进行任务类型、状态、报酬类型的筛选,所以还需要加上新的标签,特别麻烦。所以在主页中不进行搜索,而跳转新的页面。跳转通过点击主页的搜索栏完成(主页的搜索栏是假的)。
<view id="search_view" bindtap="navigateToResult"><image src="/images/icons/search.png" class="search_logo" /><input name="search_input" disabled = "{{true}}" class="input_title" placeholder="搜索 任务" maxlength='20' /></view>
跳转:
// 点击搜索栏跳转navigateToResult: function(e) {wx.navigateTo({url: '/pages/SearchResult/SearchResult',})},
推荐区域
在推荐区域中,需要对问卷进行分类。共有四类:最新、跑腿、信息、问卷。通过滑动标签实现不同类的选择。选择对应的类底部滑块滑动到该类下方,并且字的颜色变成主题色。
<!--标签页--><view class="swiper-tab"><view class="swiper-tab-list {{currentTab==0 ? 'on' : ''}}" data-current="0" bindtap="swichNav">最新</view><view class="swiper-tab-list {{currentTab==1 ? 'on' : ''}}" data-current="1" bindtap="swichNav">跑腿</view><view class="swiper-tab-list {{currentTab==2 ? 'on' : ''}}" data-current="2" bindtap="swichNav">信息</view><view class="swiper-tab-list {{currentTab==3 ? 'on' : ''}}" data-current="3" bindtap="swichNav">问卷</view></view>
/*标签页切换*/
.swiper-tab {background-color:#fff;width:90%;margin-top:20rpx;text-align:center;line-height:70rpx;margin-left:auto;margin-right:auto;
}.swiper-tab-list {font-size: 32rpx;display: inline-block;width: 25%;color: #797979;
}.on {color: #ff7e67;border-bottom: 5rpx solid #ff7e67;
}
列表
列表的实现比较复杂。这里参照的是闲鱼的左右并列的列表。其中项目的长度仅会影响其下面的项目而不会对左右的项目产生影响。为了实现这种效果,添加的是左右两个列表。
列表实现如下:
<view class="list_container"><view style="display:flex; flex-direction: row;"><view style="display:flex; flex-direction: column;"><!--left--><view class="list_left" wx:for="{{show_list_left}}" wx:key="id"><view class="list_item" bindtap="navigateToDetail" data-item="{{item.id}}"><image src="{{item.images[0].url}}" class="item_img" mode="aspectFill" /><view class="item_img_front" /><text class="info_title">{{item.title}}</text><view class="info"><!--金额--><view class="detail_top"><view class="money_type"><text class="money_text">{{item.reward == 'object' ? item.reward_object : '? ' + item.reward_value}}</text></view><view class="money_type"><text class="payType">{{item.reward == 'rmb' ? '人民币' : item.reward == 'object'? '实物' : '闲币' }}</text><text class="payType">{{item.type == 'run' ? '跑腿' : item.type == 'questionnaire'? '问卷' : '信息' }}</text></view></view><view class="band1" /><view class="detail_bottom"><image src="{{item.publisher.avatar}}" class="item_touxiang" /><text class="item_owner">{{item.publisher.nickname}}</text></view></view></view></view></view><!--right--><view style="display:flex; flex-direction: column; "><view class="list_right" wx:for="{{show_list_right}}" wx:key="id"><view class="list_item" bindtap="navigateToDetail" data-item="{{item.id}}"><image src="{{item.images[0].url}}" class="item_img" mode="aspectFill" /><view class="item_img_front" /><text class="info_title">{{item.title}}</text><view class="info"><!--金额--><view class="detail_top"><view class="money_type"><text class="money_text">{{item.reward == 'object' ? item.reward_object : '? ' + item.reward_value}}</text></view><view class="money_type"><text class="payType">{{item.reward == 'rmb' ? '人民币' : item.reward == 'object'? '实物' : '闲币' }}</text><text class="payType">{{item.type == 'run' ? '跑腿' : item.type == 'questionnaire'? '问卷' : '信息' }}</text></view></view><view class="band1" /><view class="detail_bottom"><image src="{{item.publisher.avatar}}" class="item_touxiang" /><text class="item_owner">{{item.publisher.nickname}}</text></view></view></view></view></view></view>
如何进行内容的处理?对于获取的内容,设定一个flag
指示添加到左列表还是右列表,轮流进行添加操作。
for (let task of res.data.tasks) {// 添加默认图片if (task.images.length == 0) {task.images.push({id: 0,url: '/images/icon.png'})}if (isLeft) {this.data.show_list_left.push(task)} else {this.data.show_list_right.push(task)}isLeft = !isLeft}this.setData({show_list_left: this.data.show_list_left,show_list_right: this.data.show_list_right,isLoading: false,noMore: res.data.pagination.total <= this.data.currentPage * this.data.pageSize})
加载中与没有内容
在加载的时候应该显示加载中
,而在没有更多内容的时候应该显示没有更多内容了
。但是如果没有更多内容,依旧应该允许再次加载,因为可能有新的任务发布。内容是分页获取的。所以使用页面Controller
中的全局变量currentPage
得知当前的页数。如果是1表示只加载首页,如果大于1表示刷新的结果,显示的内容应该是叠加的。
// 加载任务列表 loadTaskList: async function(page, type = 'all') {this.setData({isLoading: true,noMore: false,});if (page !== undefined) {this.data.currentPage = pagethis.setData({show_list_left: [],show_list_right: []});} else {this.data.currentPage++}let res = await server.request('GET', 'tasks', {page: this.data.currentPage,size: this.data.pageSize,type: type})if(!res.data.tasks || res.data.tasks.length === 0){this.setData({noMore: true,isLoading: false})return}
衍生:搜索页
刚才说到了,搜索页只有在主页点击搜索框的时候才会调用。搜索页也是分为三个部分,其中的最顶部是搜索栏,中间是类型选择以及排序选择。
搜索栏
搜索栏由两部分组成:输入以及清空。搜索使用回车调用的方式。
输入框
输入框的最大长度为20,如果回车则进行搜索。通过标签添加属性bindconfirm
即可。
清空按钮在搜索输入的时候才进行显示,点击清空搜索框内输入的内容,在清空后也不进行显示。
<view id = "search_view"><image src = "/images/icons/search.png" class = "search_logo" /><input name="search_input" class="input_title" placeholder="搜索 任务" maxlength='20' focus = "true" bindinput='typing' value = "{{typing_content}}" bindconfirm = 'searchResult'/><image src = "/images/icons/empty.png" class = "search_logo" wx:if = "{{isFocus}}" catchtap='cleaning'/></view>
// 检测用户输入typing: function(e) {this.setData({typing_content: e.detail.value});this.setData({isFocus: true});},// 用户点击回车键进行搜索searchResult: function(e) {this.logicalJudge()},
筛选和排序
这里使用piker
标签进行筛选,而排序使用之前主页使用过的滑动的view
。每次选择都需要进行重新获取, 获取方式与主页的任务获取是类似的,只是增加了筛选和排序的参数,以及输入的内容。
下方搜索结果
与主页的推荐任务列表类似,这里使用加载中
和没有更多
这两段文字进行状态的显示。与首页不同的是这里的列表是一列的,而不是左右的。
获取的时候还需要考虑标题的长度问题,防止标题比界面长。
// 缩减字数reduce: function() {var arr = [];for (var value of this.data.testList.data) {if (value.title.length >= 10) {value.title = value.title.substring(0, 10);value.title = value.title + "...";}if (value.content.length > 12) {value.content = value.content.substring(0, 12);value.content = value.content + "...";}arr.push(value);}this.data.testList.data = arr;this.setData({testList: this.data.testList});},
详情页
详情页是其中最复杂的页面。包括详情和评论两大模块。
详情
详情页顶部显示任务的详情,包括发布者的信息以及任务信息。
发布者信息
对于发布者,需要显示其昵称nickname
、头像以及发布日期。信息直接从后端进行获取。需要注意的是,通过moment.js
进行时间(或日期)的获取。
moment
使用方式如下:
moment.locale('zh-cn', {longDateFormat: {l: "YYYY-MM-DD",L: "YYYY-MM-DD HH:mm"}})
res.data.data = arrfor (let i in res.data.data) {res.data.data[i].time = moment(new Date(res.data.data[i].time * 1000)).locale('zh-cn').startOf('minute').fromNow()}
价格、阅读量、收藏数、点赞数
这些比较杂乱的信息,通过 一栏view
进行显示。实现方法太简单了不说了。
任务内容
任务内容包括标题、图片、标签、地点信息,除此之外还有开始、结束时间等。在下方还有人员列表。这些都是需要显示的内容。
标题
标题原本想使用text
但是效果不好,而使用textarea
有很大的上下间距。所以使用textarea
并调整其高度。高度进行双向绑定。
标签、地点
标签和地点返回的是一个数组,所以需要进行分开显示。这里通过display:flex; flex-direction: row
进行行的显示。并且在每一个标签周围加上边框。(参与人员和参与状态也是类似的。)
点赞、收藏、聊天按钮
这三个按钮使用固定位置进行显示,因为按钮数量比较多,所以使用更多
按钮将它们整合在一起。而聊天按钮进行单独显示。当然,在没有加入任务的时候聊天
按钮不进行显示。
<!--发消息--><view class="float_button" style='bottom: {{showButtons ? 300 : 500}}rpx;' wx:if="{{taskDetail.data.played && !isMine}}" bindtap='clickConversation'><image src="/images/icons/conversation.png" class="btn_img" /></view><!--收藏--><view class="float_button" style="bottom: 400rpx;" wx:if="{{showButtons}}" bindtap='clickCollect'><image src="/images/icons/{{isCollected ? 'collected' : 'collect'}}.png" class="btn_img" /></view><!--喜欢--><view class="float_button" style='bottom: 500rpx;' wx:if="{{showButtons}}" bindtap='clickLove'><image src="/images/icons/{{isLove? 'liked' : 'like'}}.png" class="btn_img" /></view><view class="float_button" style="bottom: 600rpx;" bindtap='clickMore'><image src="/images/icons/more.png" class="btn_img" /></view>
评论
在加载内容后,根据任务id可以加载评论。注意一点,在设计中,回复是嵌套在评论中的,所以回复是不可以被回复的,需要单独处理。除此之外,用户只可以删除自己的评论而不能删除别人的评论,还需要实现评论的点赞和回复功能。
<!--其他留言--><view style="display:flex; flex-direction:column"><view class="comment_user_info" wx:for="{{commentData.data}}" wx:key="item.id"><view class="content_list"><image src="{{item.user.avatar}}" class="comment_ava" /><text class="subtitle" style=" margin-left:10rpx; margin-top: 25rpx;">{{item.user.nickname}}</text><view style="display:flex; flex-direction:row; margin-left: auto; align-items:flex-end; length: 100%;"><text class="subtitle" style="margin-top:10rpx; margin-left: 20rpx; margin-right: 20rpx;">{{item.time}}</text><view class="comment_type_view" wx:if = '{{!item.is_delete}}'><!--删除--><view bindtap="clickDelete" style="display:{{item.canDelete ? 'flex' : 'none'}}; flex-direction: row; margin-right: 30rpx;" data-item="{{item.id}}"><image src="/images/icons/delete_comment.png" style="width: 45rpx; height:45rpx; margin-top: 10rpx;" /></view><!--回复--><view bindtap="clickReply" style="display:{{item.isReply ? 'none' : 'flex'}}; flex-direction: row; margin-right: 20rpx;" data-item="{{item.id}}"><image src="/images/icons/comment_reply.png" style="width: 45rpx; height:45rpx; margin-top: 10rpx;" /><text class="subtitle" style="margin-left: 10rpx; margin-top: 15rpx;">{{item.reply_count}}</text></view><!--喜欢--><view bindtap="clickCommentLove" style="margin-right: 0rpx; display:flex; flex-direction: row;" bindtap='clickCommentLike' data-item='{{item.id}}' data-liked='{{item.liked}}'><image src="/images/icons/{{item.liked ? 'comment_liked' : 'liked_top'}}.png" style="width: 45rpx; height:45rpx; margin-top: 10rpx;" /><text class="subtitle" style="margin-left: 10rpx; margin-top: 15rpx;">{{item.like_count}}</text></view></view></view></view><text class="comment_text title" style="margin-top:10rpx; margin-left:20rpx;">{{item.content}}</text><view class="band1" /></view></view><view class="loading_view" wx:if="{{isLoading}}"><image class="loading" src="/images/icons/loading.png"></image><text class="subtext">正在载入更多...</text></view><text id="bottom" wx:if="{{noMoreComment}}">没有更多评论了~</text></view>
评论的加载
// 重构回复格式,并添加for(let j in res.data.data[i].reply){if (res.data.data[i].reply[j].user.id === app.globalData.userInfo.id && res.data.data[i].reply[j].is_delete === false) {res.data.data[i].reply[j].canDelete = true} else {res.data.data[i].reply[j].canDelete = false}var content = res.data.data[i].reply[j].contentcontent = '@' + res.data.data[i].user.nickname + ' : ' + contentres.data.data[i].reply[j].content = contentif (res.data.data[i].reply[j].is_delete) {res.data.data[i].reply[j].content = '该评论已删除'}res.data.data[i].reply[j].isReply = truearr.push(res.data.data[i].reply[j])}}}res.data.data = arr
评论的输入框是在页面内,而回复输入框在点击评论的时候才会显示在屏幕的底部。这里通过评论中的dataset
存储对应评论的id。在标签中添加data-item
,赋值为id,而在Controller
代码中使用e.curretTarget.dataset.item
可以提取出对应的id。
// 回复评论clickReply: function(e) {this.data.replyCommentID = e.currentTarget.dataset.item;this.setData({isReplying: true});},
回复框:
<!--自己的留言--><view class="mycomment comment_bottom" wx:if="{{isReplying}}"><textarea class="comment_input" placeholder='输入你的回复吧~' bindinput='replyInputChange' value='{{reply_content}}' style="width: 70%;" cursor-spacing="40rpx" /><button class="commit_btn" bindtap='submitRely' cursor-spacing="40rpx">发送</button></view><view class="mask" catchtouchmove="preventTouchMove" bindtap="cancelReplying" wx:if="{{isReplying}}" />
</view>
css:
/*弹窗*/.mask {width: 100%;height: 100%;position: fixed;top: 0;left: 0;background: gray;z-index: 480;opacity: 0.7;
}.comment_bottom {background: #fff;position: fixed;bottom: 0rpx;width: 100%;z-index: 9999;padding-left: 20rpx;padding-right: 30rpx;padding-top: 40rpx;padding-bottom: 40rpx;
}
发布页
发布页很多功能前面已经提及,这里选比较重点的一些内容。
图片添加
这里使用flex
让添加的图片水平排布,并且通过+
符号添加图片,默认第一张图片为封面图片。点击图片删除。
图片添加过程如下:
// 添加图片addImage: async function(e) {try {const res = await util.as(wx.chooseImage, {count: 4 - this.data.addedImages.length,sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有sourceType: ['album', 'camera'] // 可以指定来源是相册还是相机,默认二者都有})wx.showLoading({title: '正在上传',mask: true})// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片let tempFilePaths = res.tempFilePaths;for (var value of tempFilePaths) {if (this.data.addedImages.length >= 4) {wx.showToast({title: '最多上传四张',image: '/images/icons/error.png',mask: true,duration: 1000})break;} else {try {const resImage = await server.uploadFile(value, 'image')const resData = JSON.parse(resImage.data)this.data.addedImages.push({id: resData.id,url: value})} catch (err) {wx.showToast({title: '上传失败',image: '/images/icons/error.png'})}wx.hideLoading()}}this.setData({addedImages: this.data.addedImages})} catch (err) {console.log(err)}},
图片删除过程如下:
// 删除图片deleteImage: function(e) {var id = e.currentTarget.dataset.index;this.setData({isDelete: true,delete_id: id});},
picker初始化
在每次提交后,需要对piker
标签的信息重新赋值,否则会出现保存草稿后自动同意加入的picker
被默认初始化。
if (this.data.auto_accept === true) {this.setData({auto_in: [{val: '是',checked: 'true'},{val: '否'},],})} else {this.setData({auto_in: [{val: '是'},{val: '否',checked: 'true'},],})}
消息页
消息页面的实现比较复杂,这里拆开来讲。欢迎大家来看我的博客:
如何在微信小程序中实现在线会话(聊天)系统
个人信息设置
个人消息页除了闲币和个人信息设置之外就是导航了。这里只讲个人消息设置。
首先需要添加遮罩,使得后面的内容失效。
其次添加一个固定位置的view
,通过Scroll-view
实现滚动效果。
<!--对话框-->
<view class="modalDialog" wx:if="{{hasUserInfo && isEditInfo}}"><image src="/images/icons/close.png" class="close" bindtap="closeEditPage" /><scroll-view scroll-y style="height:800rpx;"><view id="insideContainer"><view style="margin: 10rpx auto 0rpx auto;"><image src="/images/icons/up.png" class="downImg"></image><text style="font-size: 20rpx;color: gray; margin-left : 10rpx;">上滑</text></view><image src="{{userInfo.info.avatar}}" class="ava" /><text class="title">昵称</text><input name="username" class="input_setting" placeholder="请输入昵称" maxlength='25' value="{{nickname}}" bindinput="changeNickname" /><view class="band1" /><text class="title">性别</text><radio-group name="radio-group" class="info_text" style="margin-left: 60rpx; display:flex; flex-direction:row;" bindchange="changeGender"><view class="radio" wx:for="{{gender_items}}" wx:key="val"><radio color="#ff7e67" value="{{item.val}}" checked="{{gender == item.val}}" style="margin-left:50rpx;" />{{item.name}}</view></radio-group><text class="title">个人简介</text><textarea name="bio" class="input_setting" placeholder="请输入个人简介" maxlength='128' value="{{bio}}" bindinput="changeBio" style="height: 120rpx; width:80%; word-wrap:break-word" /><view class="band1" /><text class="title">邮箱</text><input name="email" class="input_setting" placeholder="请输入邮箱" maxlength='25' value="{{email}}" bindinput="changeEmail" /><view class="band1" /><text class="title">手机号码</text><input name="phone" class="input_setting" type='digit' placeholder="手机号码" maxlength='15' value="{{phone}}" bindinput="changePhone" /><view class="band1" /><text class="title">学校</text><input name="school" class="input_setting" placeholder="请输入您的学校" maxlength='25' value="{{school}}" bindinput="changeSchool" /><view class="band1" /><text class="title">地址</text><textarea name="address" class="input_setting" placeholder="请输入您的地址" maxlength='32' value="{{location}}" bindinput="changeLocation" style="height: 80rpx; width:80%; word-wrap:break-word" /><view class="band1" /><button class='submit_btn' bindtap="submit">提交</button></view></scroll-view>
</view>
<!--蒙板-->
<view class="mask" catchtouchmove="preventTouchMove" wx:if="{{hasUserInfo && isEditInfo}}" />
需要注意的是,通过正则表达式判断空与数据合法性。在AddItem
发布页也需要用到。
submit: async function(e) {// 合法性判断if (!/[^\s]+/.test(this.data.nickname)) {this.showToast("昵称为空", '/images/icons/error.png');return;}if (!/[^\s]+/.test(this.data.gender)) {this.showToast("性别为空", '/images/icons/error.png');return;}if (!/[^\s]+/.test(this.data.email)) {this.showToast("邮箱为空", '/images/icons/error.png');return;}if (!/[^\s]+/.test(this.data.phone)) {this.showToast("手机为空", '/images/icons/error.png');return;}if (!/[^\s]+/.test(this.data.school)) {this.showToast("学校为空", '/images/icons/error.png');return;}if (!/[^\s]+/.test(this.data.location)) {this.showToast("地址为空", '/images/icons/error.png');return;}// 验证合法性var isemail = /^\w+([-\.]\w+)*@\w+([\.-]\w+)*\.\w{2,4}$/;if (!isemail.test(this.data.email)) {this.showToast("邮箱格式错误", '/images/icons/error.png');return;}var isPhone = /^1[3456789]\d{9}$/;if (!isPhone.test(this.data.phone)) {this.showToast("手机格式错误", '/images/icons/error.png');return;}file = {"bio": this.data.bio,"school": this.data.school,"email": this.data.email,"gender": this.data.gender,"location": this.data.location,"nickname": this.data.nickname,"phone": this.data.phone,};try {await server.request('PUT', 'users/info', file)await app.getUserInfo()this.showToast("修改成功", "");this.setData({userInfo: app.globalData.userInfo,hasUserInfo: app.globalData.hasUserInfo,isEditInfo: false})this.setFormData()} catch (err) {console.log(err)}},
已发布页和草稿页
已发布页和草稿页都可以对于之前发布/草稿状态的任务进行操作。已发布页点击跳转到任务详情页,而草稿页跳转到发布页。两者都可以对任务进行关闭(删除)。所以通过AddedTaskPage
统一而传入不同的参数即可。
这里德实现与之前搜索页类似,但需要注意的是,因为发布页是无法直接通过navigateTo
跳转的(为什么?因为它是和底部导航栏捆在一起的,跳不了)。所以需要通过switchTab
跳转。
wx.switchTab({url: '/pages/AddItem/AddItem'})
参与过任务页
对于参与过任务页中的每一项,我们可以提交其反馈。
在反馈页中,大部分功能与申请备注页类似(这个页面功能太少不单独讲),所以复用。不过反馈页可以进行打分。这是申请备注页没有的。通过slider
实现打分。
<slider bindchange='onChangeScore' data-index="{{index}}" min="0" max="100" value="{{score}}" backgroundColor='' activeColor='#ff7e67'></slider>
收藏页
这个页面与搜索页太相似了,只是少了个搜索框,应该与搜索页复用的,之前没想到,所以多弄了个页面。
导航栏的添加
导航栏的添加需要在app.js
中添加如下的导航栏信息即可。
tabBar: {"list": [{"text": "首页","iconPath": "images/icons/home.png","selectedIconPath": "images/icons/home_selected.png","pagePath": "pages/index/index"},{"text": "发布","iconPath": "images/icons/add.png","selectedIconPath": "images/icons/add_selected.png","pagePath": "pages/AddItem/AddItem"},{"text": "消息","iconPath": "images/icons/message.png","selectedIconPath": "images/icons/message_selected.png","pagePath": "pages/Message/Message"},{"text": "我的","iconPath": "images/icons/user.png","selectedIconPath": "images/icons/user_selected.png","pagePath": "pages/userInfo/userInfo"}],"backgroundColor": "#fff","color": "#404969","selectedColor": "#ff7e67"},
系统分析设计期末大项目——闲得一币TimeForCoin小程序前端相关推荐
- html网页设计期末大作业——化妆品展示静态模板(9页) web前端开发技术 web课程设计 网页规划与设计
html网页设计期末大作业--化妆品展示静态模板(9页) web前端开发技术 web课程设计 网页规划与设计 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶 ...
- DIV+CSS进行布局 HTML+CSS+JS大作业——汽车销售网站模板(7页) html网页设计期末大作业_网页设计平时作业模板下载
HTML+CSS+JS大作业--汽车销售网站模板(7页) html网页设计期末大作业_网页设计平时作业模板下载 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. ...
- matlab gui期末设计,MATLABGUI课程设计期末大作业湖南理工学院
MATLABGUI课程设计期末大作业湖南理工学院 <数字图像处理>期末大作业暨课程考核报告姓名:邓巧灵学号:24112200002序号:02湖南理工学院南湖学院2014 年 6 月2目录一 ...
- php网站开发期末大作业,网页设计期末大作业报告..doc
网页设计期末大作业报告. 南开大学滨海学院 本 科 生 论 文(设 计) 中文题目:外文题目:Website design and implementation based on Web develo ...
- 网页课程设计-期末大作业-简单设计【原神狂喜】
网页课程设计-期末大作业[原神狂喜] 课程设计-涵盖内容 代码部分 登录页 1.index.html 2.index.css 博客首页 1.home.html 2.banner.css 公共样式 3. ...
- html网页设计期末大作业——化妆品html+div商城(19页)
html网页设计期末大作业--化妆品html+div商城(19页) 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶叶. 家居. 酒店. 舞蹈. 动漫. 明星 ...
- HTML期末作业课程设计期末大作业——体育排球5页面带注册HTML+CSS+JS(学生网页设计作业源码)...
HTML期末作业课程设计期末大作业--体育排球5页面带注册HTML+CSS+JS(学生网页设计作业源码) 临近期末, 你还在为HTML网页设计结课作业,老师的作业要求感到头大?HTML网页作业无从下手 ...
- html网页设计期末大作业——生物科技化妆品网页(6页) HTML+CSS+JS网页设计期末课程大作业
html网页设计期末大作业--生物科技化妆品网页(6页) HTML+CSS+JS网页设计期末课程大作业 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶叶. ...
- html网页设计期末大作业——绿色化妆品网页设计(4页)
html网页设计期末大作业--绿色化妆品网页设计(4页) 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶叶. 家居. 酒店. 舞蹈. 动漫. 明星. 服装. ...
最新文章
- percent之集合
- 计算机中英语GAI缩写,等等英语_英语中“等等”缩写成为etc吗要加一点吗全拼是什么谢谢大家_淘题吧...
- ActiveMQ activemq web管理界面介绍
- 完美脱壳组装PE的一般步骤(Obsidium1.3.6.4 DEMO 主程序)
- 如何快速在oracle内生成数据,[Oracle]快速生成大量模拟数据的方法
- matlab交替隐式迭代,jQuery关于隐式迭代的个人理解~
- c++ primer 练习题4.34:
- linux远程挽救模式,linux – 如何在远程重启Redhat后进入SSH并进入恢复模式?
- popwindow弹窗
- 程序员如何探索新技术
- php生成gif动态图片_PHP绘制GIF动态图片
- 移动通信网络架构 1G-5G
- AcrelEMS-IDC数据中心综合能效管理解决方案
- Spring Aop详解(无参和带参)
- 分布式定时任务框架---Uncode Schedule
- java下一页按钮_如何仅使用Spring在Java中单击提交按钮后才能转到下一页
- 6-1 读文章(*)
- Android夜神模拟器
- XML系列(一)------初识XML
- ed302安兔兔版main.31.com.antutu.ABenchMark.fix23下载安装使用教程
热门文章
- 工业路由器厂家,工业路由器品牌.工业CPE厂家,工业CPE品牌
- 一个大学教授在美国的生活
- 斯密的绝对优势理论和李嘉图的比较优势理论的区别与联系
- 【翻译大老外的文】信息平台与数据科学的兴起
- 基于YOLOv5的口罩佩戴检测方法
- java-php-net-python-四六级考试报名系统计算机毕业设计程序
- android报错必须64位,64位系统使用Android虚拟机问题
- (Python)从零开始,简单快速学机器仿人视觉Opencv---第九节:颜色空间转换
- RabbitMQ问题排解
- JS 判断客户端是iOS还是Android