目录
1 引言 6
1.1 小程序简介
1.2 研究背景
1.3 国内外现状
1.4 主要采用的技术
2 需求分析 8
2.1 可行性分析
2.1.1技术可行性: 8
2.1.2运行可行性: 8
2.1.3 微信小程序的现状 8
2.1.4小程序的进入方式 9
2.2 SWOT分析
2.2.1 优势Strenths 9
2.2.2 劣势Threats 9
2.2.3 机会Opportunities 9
2.2.4 威胁Threats 10
2.2.5 前景预测 10
2.3 微信小程序定制开发功能设计
2.4 游戏需求:
2.4.1 2048游戏算法
3 系统设计 11
3.1小程序功能分析
3.1.1 算法 11
3.1.2难点 11
3.1.3视图实现 11
3.1.4逻辑实现 11
3.2 E-R流程图:
3.3 游戏主界面
4 系统实现 13
4.1游戏页面代码4x4.WXML
4.2 小程序触摸方向核心代码4x4.js
4.3 小程序构建矩阵代码grid.js
4.4 小程序创建棋盘game_manager.js
4.5主页排行榜rank.wxml
结束语 35
致 谢 36
参考文献 37
附录 38
1 微信开发者平台
(1) 微信开发者工具的概览 38
(2) 开发指南 38
3 系统设计
3.1小程序功能分析
本系统数据库采用 AccessWeb 服务器采用 IIS系统结构图如图 3-1 所示
根据系统的功能分析可以画出系统的功能结构图分别从客户界面、管理界
面对功能模块图加以描述。客户界面的系统功能模块如图 3-2 所示。
(1)实体
实体是客观世界存在的且可相互区分的事物。它可以是人也可以是动物;可
以是具体事物也可以是抽象概念。
联系是指客观世界中各事物彼此间的联系。联系分为三类:一对一的关系,一对多的关系,多对多的关系。
属性是实体或联系所具有的性质,通常一个实体用若干属性来刻画。人们通常就是用实体、联系和属性这三个概念来理解现实问题,因此,ER模型比较接近人的思维方式。此外,ER模型使用简单的图形符号表示系统分析员对间题的理解,不熟悉计算机的人也能理解它,因此,ER模型可以作为用户与系统分析员之间的交流工具。
根据系统功能,确定实体、联系、属性,构建E-R图,把概念结构设计转化为逻辑结构设计为:
3.2文章列表浏览
文章列表显示的是某个时间段中博客主人发表的所有文章标题。
当用户找到该管理员的博客地址后需要打开该管理员所发表的文章并进行浏览。此过程中用户一般都会以文章标题为准考虑自己是否有浏览该篇文章的必要当用户找到文章标题后只需要单击该标题便可直接链接到该标题下的文章内容进行浏览。
3.3文章详细查看
当用户点击某个标题后,可以详细查看这边文章的所有信息,浏览文章里面的图片,已经视频等信息。
3.4文章评论
当用户浏览完一篇文章后一般都会因为文章的内容而产生自己独有的想法进而想与博客主人以及其他浏览该篇文章的用户分享与交流自己的想法。要能让博客主人和其他用户知晓自己的想法就需要用文字表达出来并记录下来发表出来以便于供博客主人和其他用户交流此时就需要提供“评论文章”的功能。
3.5发表文章
在个人信息界面,用户可以通过点击新增按钮来发布新的文章,通过编写文章标贴,文章内容,还可以添加图片、视频等信息,来发表文章。
3.6数据分析
用户为核心,所以用户对象必须要有一张数据表,字段包含包含一些常用信息,比如用户ID、昵称、登录名、密码,为了不让一张表的数据过于繁杂和便于查询,我们又设计出了一张用户信息表,本文转载自http://www.biyezuopin.vip/onews.asp?id=14945保存一些不常用的用户信息如生日、爱好、教育信息等;
内容为主体,所以文章对象要有一张数据表,字段包含用户ID、文章分类、文章标题、关键字等;
文章分类表,字段类别、类别名称等’,
好友表,用来存储互加好友的两个人之间的关系,字段包含用户id和好友id以及自增的主键等;
用户关注表,字段包含用户id以及被关注人id和自增的主键;
私信表, 字段包含自增私信ID,发信者ID,收信者ID, 私信内容;
系统通知表,字段包含系统通知ID,接受者ID,通知内容,自增通知内容id;
评论表,字段包含评论自增ID号,收到评论的用户ID,评论内容的ID,评论内容,评论者ID,评论时间等;
评论回复表,字段包含回复自增ID号,收到回复的用户ID,回复评论的ID,回复内容,回复者ID,回复时间,回复等级等;
留言表,字段包含留言自增ID号,收到留言的用户ID,留言内容的ID,留言内容,留言者ID,留言时间等;
// new.js
// TODO 并不是所有非中文字符宽度都为中文字符宽度一半,需特殊处理
// TODO 由于文本框聚焦存在bug,故编辑模式待实现
const input = require('../../utils/input');
const config = require('../../config');
const geo = require('../../services/geo');
const util = require('../../utils/util');
const RESOLUTION = 750; // 微信规定屏幕宽度为750rpx
const MARGIN = 10; // 写字面板左右margin
const ROW_CHARS = Math.floor((RESOLUTION - 2 * MARGIN) / config.input.charWidth);
const MAX_CHAR = 1000; // 最多输1000字符
// 内容布局
const layoutColumnSize = 3;
// 博客内容类型
const TEXT = 'TEXT';
const IMAGE = 'IMAGE';
const VIDEO = 'VIDEO';
const mediaActionSheetItems = ['拍照', '选择照片', '选择视频'];
const mediaActionSheetBinds = ['chooseImage', 'chooseImage', 'chooseVideo'];
var 小程序 = get小程序();
Page({
data: {
// 博客对象
diary: {
meta: {},
list: [],
},
// 博客内容布局列表(2x2矩阵)
layoutList: [],
// 是否显示loading
showLoading: false,
// loading提示语
loadingMessage: '',
// 页面所处模式
showMode: 'common',
// 输入框状态对象
inputStatus: {
row: 0,
column: 0,
lines: [''],
mode: 'INPUT',
auto: false, // 是否有自动换行
},
// 当前位置信息
poi: null,
// 点击`图片`tab的action-sheet
mediaActionSheetHidden: true,
// 多媒体文件插入action-sheet
mediaActionSheetItems: mediaActionSheetItems,
// 多媒体文件插入项点击事件
mediaActionSheetBinds: mediaActionSheetBinds,
// 是否显示底部tab栏
showTab: true,
},
// 显示底部tab
showTab() {
this.setData({showTab: true});
},
// 隐藏底部tab
hideTab() {
this.setData({showTab: false});
},
// 显示loading提示
showLoading(loadingMessage) {
this.setData({showLoading: true, loadingMessage});
},
// 隐藏loading提示
hideLoading() {
this.setData({showLoading: false, loadingMessage: ''});
},
// 数据初始化
init() {
this.getPoi();
this.setMeta();
},
// 设置博客数据
setDiary(diary) {
let layout = util.listToMatrix(diary.list, layoutColumnSize);
this.setData({diary: diary, layoutList: layout});
this.saveDiary(diary);
},
// 保存博客
// TODO sync to server
saveDiary(diary) {
const key = config.storage.diaryListKey;
小程序.getLocalDiaries(diaries => {
diaries[diary.meta.title] = diary;
wx.setStorage({key: key, data: diaries});
})
},
// 页面初始化
onLoad: function(options) {
if (options) {
let title = options.title;
if (title) {this.setData({
'diary.meta.title': title,
'diary.meta.create_time': util.formatTime(new Date()),
'diary.meta.cover': ''
});}
}
this.init();
},
// 页面渲染完成
onReady: function(){
wx.setNavigationBarTitle({title: '编辑博客'});
},
onShow:function(){
// 页面显示
},
onHide:function(){
// 页面隐藏
},
onUnload:function(){
// 页面关闭
console.log('页面跳转中...');
},
// 清除正在输入文本
clearInput() {
this.setData({inputStatus: {
row: 0,
common: 0,
lines: [''],
mode: 'INPUT',
auto: false,
}});
},
// 结束文本输入
inputDone() {
let text = this.data.inputStatus.lines.join('\n');
let diary = this.data.diary;
if (text) {
diary.list.push(this.makeContent(TEXT, text, ''));
this.setDiary(diary);
}
this.inputCancel();
},
// 进入文本编辑模式
inputTouch(event) {
this.setData({showMode: 'inputText'});
},
// 取消文本编辑
inputCancel() {
this.setData({showMode: 'common'});
this.clearInput();
},
// 文本输入
textInput(event) {
console.log(event);
let context = event.detail;
// 输入模式
if (this.data.inputStatus.mode === 'INPUT') {
if (context.value.length != context.cursor) {
console.log('用户输入中...');
} else {
let text = context.value;
let len = input.strlen(text);
let lines = this.data.inputStatus.lines;
let row = this.data.inputStatus.row;
let [extra, extra_index] = [[['']], 0];
let hasNewLine = false;
console.log('当前文本长度: ' + len);
// 当前输入长度超过规定长度
if (len >= ROW_CHARS) {
// TODO 此处方案不完善
// 一次输入最好不超过两行
hasNewLine = true;
while (input.strlen(text) > ROW_CHARS) {
let last = text[text.length - 1];
if (input.strlen(extra[extra_index] + last) > ROW_CHARS) {
extra_index += 1;
extra[extra_index] = [''];
}
extra[extra_index].unshift(last);
text = text.slice(0, -1);
}
}
lines[lines.length - 1] = text;
if (hasNewLine) {
extra.reverse().forEach((element, index, array) => {
lines.push(element.join(''));
row += 1;
});
}
let inputStatus = {
lines: lines,
row: row,
mode: 'INPUT',
auto: true, // // 自动换行的则处于输入模式
};
this.setData({inputStatus});
}
}
},
// 文本框获取到焦点
focusInput(event) {
let isInitialInput = this.data.inputStatus.row == 0 &&
this.data.inputStatus.lines[0].length == 0;
let isAutoInput = this.data.inputStatus.mode == 'INPUT' &&
this.data.inputStatus.auto == true;
let mode = 'EDIT';
if (isInitialInput || isAutoInput) {
mode = 'INPUT';
}
this.setData({'inputStatus.mode': mode});
},
// 点击多媒体插入按钮
mediaTouch() {
this.setData({
showTab: false,
mediaActionSheetHidden: false,
});
},
mediaActionSheetChange(event) {
this.setData({
showTab: true,
mediaActionSheetHidden: true,
})
},
// 将内容写入至博客对象
writeContent(res, type) {
let diary = this.data.diary;
if (type === IMAGE) {
res.tempFilePaths.forEach((element, index, array) => {
// TODO 内容上传至服务器
diary.list.push(this.makeContent(type, element, ''))
});
}
if (type === VIDEO) {
// TODO 内容上传至服务器
diary.list.push(this.makeContent(type, res.tempFilePath, ''))
}
// 设置博客封面
if (type === IMAGE && !this.data.diary.meta.cover) {
this.setData({'diary.meta.cover': res.tempFilePaths[0]});
}
this.setDiary(diary);
this.hideLoading();
this.showTab();
},
// 从相册选择照片或拍摄照片
chooseImage() {
let that = this;
wx.chooseImage({
count: 9, // 最多选9张
sizeType: ['origin', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.setData({mediaActionSheetHidden: true});
this.showLoading('图片处理中...');
that.writeContent(res, IMAGE);
}
})
},
// 从相册选择视频文件
chooseVideo() {
let that = this;
wx.chooseVideo({
sourceType: ['album'], // 仅从相册选择
success: (res) => {
this.setData({mediaActionSheetHidden: true});
this.showLoading('视频处理中...');
that.writeContent(res, VIDEO);
}
})
},
// 获得当前位置信息
getPoi() {
var that = this;
wx.getLocation({
type: 'gcj02',
success: function(res) {
geo.mapRequest(
'geocoder',
{'location': geo.formatLocation(res)},
loc => {
let poi = {
'latitude': res.latitude,
'longitude': res.longitude,
'name': loc.result.address,
};
that.setData({poi: poi});
})
}
})
},
// 构造博客内容对象
makeContent(type, content, description) {
return {
type: type,
content: content,
description: description,
poi: this.data.poi,
};
},
// 构造博客meta信息
setMeta() {
var that = this;
小程序.getUserInfo(info => {
that.setData({
'diary.meta.avatar': info.avatarUrl,
'diary.meta.nickName': info.nickName,
})
})
},
})