├── .eslintrc.js ├── LICENSE ├── README.md ├── cloudfunctions ├── statisticRouter │ ├── config.json │ ├── index.js │ ├── package.json │ └── utils │ │ └── response_content.js ├── userRouter │ ├── config.json │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ ├── default_avatar_pic.js │ │ ├── init_of_matrix.js │ │ └── response_content.js └── wordRouter │ ├── config.json │ ├── index.js │ ├── package.json │ └── utils │ ├── format_time.js │ ├── get_all_sort_list.js │ ├── jstat.min.js │ ├── response_content.js │ └── sm-5.js ├── images ├── after_login_index.jpg ├── after_login_overview_1.jpg ├── after_login_overview_2.jpg ├── after_login_user.jpg ├── before_login_index.jpg ├── before_login_user.jpg ├── learning_1.jpg ├── learning_3.jpg ├── learning_4.jpg ├── learning_5.jpg ├── learning_6.jpg ├── learning_8.jpg ├── login.jpg ├── miniprogram_QRcode.jpg ├── search_1.jpg ├── search_translation.jpg ├── search_word_big.jpg ├── search_word_small.jpg ├── settings.jpg ├── title_1.png ├── title_2.png ├── title_3.png ├── title_4.png ├── word_detail.jpg ├── word_list.jpg └── 整体框架图.png ├── miniprogram ├── app.js ├── app.json ├── app.wxss ├── components │ ├── cloudTipModal │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ ├── ec-canvas │ │ ├── ec-canvas.js │ │ ├── ec-canvas.json │ │ ├── ec-canvas.wxml │ │ ├── ec-canvas.wxss │ │ ├── echarts.js │ │ ├── echartsForBar.js │ │ └── wx-canvas.js │ ├── image-cropper │ │ ├── image-cropper.js │ │ ├── image-cropper.json │ │ ├── image-cropper.wxml │ │ └── image-cropper.wxss │ └── mp-progress │ │ ├── mp-progress.js │ │ ├── mp-progress.json │ │ ├── mp-progress.wxml │ │ ├── progress.js │ │ └── progress.min.js ├── envList.js ├── lib │ ├── jstat.min.js │ ├── runtime │ │ └── runtime.js │ └── sm-5.js ├── pages │ ├── image_cropper │ │ ├── image_cropper.js │ │ ├── image_cropper.json │ │ ├── image_cropper.less │ │ ├── image_cropper.wxml │ │ └── image_cropper.wxss │ ├── index │ │ ├── index.js │ │ ├── index.json │ │ ├── index.less │ │ ├── index.wxml │ │ └── index.wxss │ ├── learning │ │ ├── learning.js │ │ ├── learning.json │ │ ├── learning.less │ │ ├── learning.wxml │ │ └── learning.wxss │ ├── login │ │ ├── login.js │ │ ├── login.json │ │ ├── login.less │ │ ├── login.wxml │ │ └── login.wxss │ ├── overview │ │ ├── overview.js │ │ ├── overview.json │ │ ├── overview.less │ │ ├── overview.wxml │ │ └── overview.wxss │ ├── review │ │ ├── review.js │ │ ├── review.json │ │ ├── review.less │ │ ├── review.wxml │ │ └── review.wxss │ ├── search │ │ ├── search.js │ │ ├── search.json │ │ ├── search.less │ │ ├── search.wxml │ │ └── search.wxss │ ├── user │ │ ├── user.js │ │ ├── user.json │ │ ├── user.less │ │ ├── user.wxml │ │ └── user.wxss │ ├── user_settings │ │ ├── user_settings.js │ │ ├── user_settings.json │ │ ├── user_settings.less │ │ ├── user_settings.wxml │ │ └── user_settings.wxss │ ├── word_detail │ │ ├── word_detail.js │ │ ├── word_detail.json │ │ ├── word_detail.less │ │ ├── word_detail.wxml │ │ └── word_detail.wxss │ └── word_list │ │ ├── word_list.js │ │ ├── word_list.json │ │ ├── word_list.less │ │ ├── word_list.wxml │ │ └── word_list.wxss ├── sitemap.json ├── static │ ├── color.wxss │ ├── iconfont.wxss │ └── images │ │ ├── logo.png │ │ ├── tab-learn-007BFF.png │ │ ├── tab-learn-3880B7.png │ │ ├── tab-learn-62BEFF.png │ │ ├── tab-learn-A6D6FA.png │ │ ├── tab-learn-CDCDCD.png │ │ ├── tab-learn-F6F6F6.png │ │ ├── tab-learn-FFFFFF.png │ │ ├── tab-overview-007BFF.png │ │ ├── tab-overview-3880B7.png │ │ ├── tab-overview-62BEFF.png │ │ ├── tab-overview-A6D6FA.png │ │ ├── tab-overview-CDCDCD.png │ │ ├── tab-user-007BFF.png │ │ ├── tab-user-3880B7.png │ │ ├── tab-user-62BEFF.png │ │ ├── tab-user-A6D6FA.png │ │ └── tab-user-CDCDCD.png └── utils │ ├── color.js │ ├── format_time.js │ ├── response_content.js │ ├── userApi.js │ ├── wordApi.js │ └── word_utils.js ├── project.config.json └── project.private.config.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Eslint config file 3 | * Documentation: https://eslint.org/docs/user-guide/configuring/ 4 | * Install the Eslint extension before using this feature. 5 | */ 6 | module.exports = { 7 | env: { 8 | es6: true, 9 | browser: true, 10 | node: true, 11 | }, 12 | ecmaFeatures: { 13 | modules: true, 14 | }, 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | sourceType: 'module', 18 | }, 19 | globals: { 20 | wx: true, 21 | App: true, 22 | Page: true, 23 | getCurrentPages: true, 24 | getApp: true, 25 | Component: true, 26 | requirePlugin: true, 27 | requireMiniProgram: true, 28 | }, 29 | // extends: 'eslint:recommended', 30 | rules: {}, 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Mint-green 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 学不会单词 2 | 3 | 一个背单词小程序 4 | 5 | 6 | 7 | 8 | ### 词汇数据来源 9 | [ECDICT](https://github.com/skywind3000/ECDICT) 10 | 11 | 12 | 13 | ### 简介 14 | 这是一个背单词小程序,是仿**不背单词**App做的(因为不背的UI真的太好看了),词库是刚好找到了大佬的**ECDICT**项目,把这些数据稍微做了些处理导入了数据库。 15 | 主要实现搜索,学习单词,复习单词,统计,登录等功能。 16 | 17 | 18 | 19 | 20 | ### 整体结构 21 | ![框架](./images/整体框架图.png) 22 | 23 | 24 | 25 | ### 功能模块及页面 26 | - [x] 登录模块(支持账号密码、微信登录&注册) 27 | 28 | - [x] 主页 29 | - [x] 每日一句(获取&发音) 30 | - [x] 主页显示需要背以及复习的量 31 | 32 | - [x] 概述页 33 | - [x] 显示相关基础及统计数据(词书、已背数量等) 34 | - [x] 切换词书 35 | - [x] 查看所有学过/未学习的单词等各项统计的单词队列 36 | - [x] 收藏夹 37 | - [x] 每日任务 38 | - [x] ECharts显示历史学习记录 39 | 40 | - [x] 个人主页 41 | - [x] 个人信息更改(头像、昵称、密码) 42 | 43 | - [x] 单词详情页 44 | 45 | - [x] 搜索模块 46 | - [x] 用英文搜索(前缀、搜原型、空格模糊搜索) 47 | - [x] 中文释义进行搜索(直接当空格模糊使,近义词替代和自动分词太难了没做) 48 | - [x] 历史搜索 49 | - [x] 切换大小词库(小的快/大的全) 50 | 51 | - [x] 学习/复习单词 52 | - [x] 三种题型(看词选义、看词识义、看义识词) 53 | - [x] 遮挡单词or词义样式(倒计时自动取消or遮挡条点击取消) 54 | - [x] 循环逻辑及实现 55 | - [x] 跳过or设置为已掌握 56 | - [x] 复习时间间隔算法(参考SuperMemo系列SM-5算法) 57 | - [ ] 拼写页面 58 | 59 | - [x] 设置页 60 | 61 | 62 | 63 | ### 效果图 64 | 65 | 66 | 67 | 首页登录前  个人页登录前  登录页  首页登录后  个人页登录后 68 | 图名1 69 | 70 | 概览页登录后1  概览页登录后2  单词列表  学习/复习页1  学习/复习页2 71 | 图名2 72 | 73 | 学习/复习页3  学习/复习页4  学习/复习页5  学习/复习页6  搜索页 74 | 图名3 75 | 76 | 小词库搜索  大词库搜索  释义搜索  单词详情  设置页 77 | 图名4 78 | 79 | 80 | 81 | ### 体验 82 | 83 | ~~想要玩一下的可以扫描以下二维码~~: 84 | 85 | 小程序二维码 86 | 87 | 由于微信要取消云开发基础套餐的免费使用了,而本人暂无精力完善此项目,这个月(22.10)20号会清除本项目的云开发数据,目前已将已有数据备份,有机会会再放出来给大家体验的! 88 | 不过还是老样子,大家有什么需求或问题都可以提一下issue,我会竭力帮大家解决的~ 89 | 90 | 91 | 92 | ### 自行部署 93 | 94 | 1. 由于本项目依托微信小程序提供的云开发能力,因此需要一些注册等的基本操作,可以参考我的另一个项目...的指引,如果会申请小程序使用云开发能力的可以朋友可以略过这一步:[GuGuMusic的使用方法](https://github.com/Mint-green/GuGumusic#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95) 95 | 2. 下载基础数据库的文件,最近还是没能力完善说明各个表格的具体字段等,大家可以查看数据后大致判断,[度盘链接](https://pan.baidu.com/s/1LR6Q6BojBTQ0ywWiJVFX6w),提取码:dddd 96 | 3. cloudfunctions文件夹在的云函数右键部署,在云开发服务的地方也按照2中的文档建好并导入需要的数据后,应该就可以用了 97 | 98 | 99 | 100 | ### 更多 101 | 102 | 最近比较忙,先简单列列已完成的and放放效果图(请原谅我放那么多图),详细的介绍之后再上,持续更新ing~ 103 | 有问题都可以提问,有什么想法也可以提一提呀~ 104 | 105 | 106 | 107 | ### 更新日志 108 | 109 | **22.10.02** 修复第一个用户(普通/微信)无法创建成功问题 110 | 111 | **22.10.02** 由于微信调整云开发计费规则,本项目小程序测试版将于22年10月中旬停止开放 -------------------------------------------------------------------------------- /cloudfunctions/statisticRouter/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/statisticRouter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "statisticRouter", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.5.3", 13 | "tcb-router": "^1.1.2" 14 | } 15 | } -------------------------------------------------------------------------------- /cloudfunctions/statisticRouter/utils/response_content.js: -------------------------------------------------------------------------------- 1 | const SUCCESS = { errorcode: 100, errormsg: "success" } //成功 2 | const LOGINOK = { errorcode: 1, errormsg: "Login successfully" } //登录成功 3 | const REGISTEROK= { errorcode: 2, errormsg: "Register successfully" } //注册成功 4 | const DBERR = { errorcode: -1, errormsg: "Database error!" } //数据库操作失败 5 | const ROUTERERR = { errorcode: -2, errormsg: "Wrong router name" } //路由名字有误 6 | const LOGINERR = { errorcode: -3, errormsg: "Wrong username or pwd" } //登录信息有误 7 | const DATAERR = { errorcode: -4, errormsg: "Wrong data!" } //数据有误 8 | const UNKOWNERR = { errorcode: -100, errormsg: "Unkown error!" } //出现未知错误 9 | 10 | 11 | module.exports={ 12 | SUCCESS: SUCCESS, 13 | LOGINOK: LOGINOK, 14 | REGISTEROK: REGISTEROK, 15 | DBERR: DBERR, 16 | ROUTERERR: ROUTERERR, 17 | LOGINERR: LOGINERR, 18 | DATAERR: DATAERR, 19 | UNKOWNERR: UNKOWNERR, 20 | } 21 | 22 | -------------------------------------------------------------------------------- /cloudfunctions/userRouter/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/userRouter/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "userRouter", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "tcb-router": { 8 | "version": "1.1.2", 9 | "resolved": "https://registry.npmjs.org/tcb-router/-/tcb-router-1.1.2.tgz", 10 | "integrity": "sha512-VB+83paVdYG0LWaodh73JUy660te2oleM5gETslbCHLnhTtgXXYfAR0dlHBU5dIhhH47V1nKp43lZUo6Xm9O4g==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cloudfunctions/userRouter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "userRouter", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "tcb-router": "^1.1.2", 13 | "wx-server-sdk": "~2.5.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cloudfunctions/userRouter/utils/default_avatar_pic.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 'https://pic2.zhimg.com/50/v2-34395fd10798f4b5bad583d61f98c849_hd.jpg?source=1940ef5c', 3 | 'https://pic2.zhimg.com/50/v2-b1e4eb7f72908a04306958f13ce45d94_hd.jpg?source=1940ef5c', 4 | 'https://inews.gtimg.com/newsapp_bt/0/13804696252/1000', 5 | 'https://inews.gtimg.com/newsapp_bt/0/13808742009/1000' 6 | ] -------------------------------------------------------------------------------- /cloudfunctions/userRouter/utils/init_of_matrix.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | '1.3': [5], 4 | '1.4': [5], 5 | '1.5': [5], 6 | '1.6': [5], 7 | '1.7': [5], 8 | '1.8': [5], 9 | '1.9': [5], 10 | '2.0': [5], 11 | '2.1': [5], 12 | '2.2': [5], 13 | '2.3': [5], 14 | '2.4': [5], 15 | '2.5': [5], 16 | '2.6': [5], 17 | '2.7': [5], 18 | '2.8': [5], 19 | } -------------------------------------------------------------------------------- /cloudfunctions/userRouter/utils/response_content.js: -------------------------------------------------------------------------------- 1 | const SUCCESS = { errorcode: 100, errormsg: "success" } //成功 2 | const LOGINOK = { errorcode: 1, errormsg: "Login successfully" } //登录成功 3 | const REGISTEROK= { errorcode: 2, errormsg: "Register successfully" } //注册成功 4 | const DBERR = { errorcode: -1, errormsg: "Database error!" } //数据库操作失败 5 | const ROUTERERR = { errorcode: -2, errormsg: "Wrong router name" } //路由名字有误 6 | const LOGINERR = { errorcode: -3, errormsg: "Wrong username or pwd" } //登录信息有误 7 | const DATAERR = { errorcode: -4, errormsg: "Wrong data!" } //数据有误 8 | const UNKOWNERR = { errorcode: -100, errormsg: "Unkown error!" } //出现未知错误 9 | 10 | 11 | module.exports={ 12 | SUCCESS: SUCCESS, 13 | LOGINOK: LOGINOK, 14 | REGISTEROK: REGISTEROK, 15 | DBERR: DBERR, 16 | ROUTERERR: ROUTERERR, 17 | LOGINERR: LOGINERR, 18 | DATAERR: DATAERR, 19 | UNKOWNERR: UNKOWNERR, 20 | } 21 | 22 | -------------------------------------------------------------------------------- /cloudfunctions/wordRouter/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/wordRouter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wordRouter", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "tcb-router": "^1.1.2", 13 | "wx-server-sdk": "~2.5.3", 14 | "bent": "<=7.3.12" 15 | } 16 | } -------------------------------------------------------------------------------- /cloudfunctions/wordRouter/utils/format_time.js: -------------------------------------------------------------------------------- 1 | // 传入时间的毫秒数(date.getTime())获取时间详情 2 | 3 | const formatTime = (time) => { 4 | var date = new Date(time) 5 | var y = date.getFullYear() 6 | var m = date.getMonth() + 1 7 | var d = date.getDate() 8 | var h = date.getHours() 9 | var min = date.getMinutes() 10 | var s = date.getSeconds() 11 | var timeStr = y + "-" + enterZero(m) + "-" + enterZero(d) + " " + enterZero(h) + ":" + enterZero(min) + ":" + enterZero(s) 12 | return timeStr 13 | } 14 | 15 | const formatDate = (time) => { 16 | var date = new Date(time) 17 | var y = date.getFullYear() 18 | var m = date.getMonth() + 1 19 | var d = date.getDate() 20 | var dateStr = y + "-" + enterZero(m) + "-" + enterZero(d) 21 | return dateStr 22 | } 23 | 24 | const dateNum = (time) => { 25 | var date = new Date(time) 26 | var y = date.getFullYear() 27 | var m = date.getMonth() + 1 28 | var d = date.getDate() 29 | var num = y *10000 + m*100 + d 30 | return num 31 | } 32 | 33 | const enterZero = (num) => { 34 | num = Math.abs(num) 35 | if (num <= 9) { 36 | num = "0" + num 37 | } 38 | return num 39 | } 40 | 41 | module.exports = { 42 | formatTime: formatTime, 43 | formatDate: formatDate, 44 | dateNum: dateNum, 45 | } -------------------------------------------------------------------------------- /cloudfunctions/wordRouter/utils/get_all_sort_list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {*} source 源数组 4 | * @param {*} count 要取出多少项 5 | * @param {*} isPermutation 是否使用排列的方式 6 | * @return {any[]} 所有排列组合,格式为 [ [1,2], [1,3]] ... 7 | */ 8 | const getAllSortList = (source, count, isPermutation = true) => { 9 | //如果只取一位,返回数组中的所有项,例如 [ [1], [2], [3] ] 10 | let currentList = source.map((item) => [item]); 11 | if (count === 1) { 12 | return currentList; 13 | } 14 | let result = []; 15 | //取出第一项后,再取出后面count - 1 项的排列组合,并把第一项的所有可能(currentList)和 后面count-1项所有可能交叉组合 16 | for (let i = 0; i < currentList.length; i++) { 17 | let current = currentList[i]; 18 | //如果是排列的方式,在取count-1时,源数组中排除当前项 19 | let children = []; 20 | if (isPermutation) { 21 | children = getAllSortList(source.filter(item => item !== current[0]), count - 1, isPermutation); 22 | } 23 | //如果是组合的方法,在取count-1时,源数组只使用当前项之后的 24 | else { 25 | children = getAllSortList(source.slice(i + 1), count - 1, isPermutation); 26 | } 27 | for (let child of children) { 28 | result.push([...current, ...child]); 29 | } 30 | } 31 | return result; 32 | } 33 | 34 | // let arr = [1, 2, 3]; 35 | // const result = getNumbers(arr, 2, false); 36 | // console.log(result); 37 | // //[ [ 1, 2 ], [ 1, 3 ], [ 2, 3 ] ] 38 | 39 | // const result2 = getNumbers(arr, 2); 40 | // console.log(result2); 41 | // //[ [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 3 ], [ 3, 1 ], [ 3, 2 ] ] 42 | 43 | module.exports = { 44 | getAllSortList: getAllSortList, 45 | } 46 | -------------------------------------------------------------------------------- /cloudfunctions/wordRouter/utils/response_content.js: -------------------------------------------------------------------------------- 1 | const SUCCESS = { errorcode: 100, errormsg: "success" } //成功 2 | const LOGINOK = { errorcode: 1, errormsg: "Login successfully" } //登录成功 3 | const REGISTEROK= { errorcode: 2, errormsg: "Register successfully" } //注册成功 4 | const DBERR = { errorcode: -1, errormsg: "Database error!" } //数据库操作失败 5 | const ROUTERERR = { errorcode: -2, errormsg: "Wrong router name" } //路由名字有误 6 | const LOGINERR = { errorcode: -3, errormsg: "Wrong username or pwd" } //登录信息有误 7 | const DATAERR = { errorcode: -4, errormsg: "Wrong data!" } //数据有误 8 | const UNKOWNERR = { errorcode: -100, errormsg: "Unkown error!" } //出现未知错误 9 | 10 | 11 | module.exports={ 12 | SUCCESS: SUCCESS, 13 | LOGINOK: LOGINOK, 14 | REGISTEROK: REGISTEROK, 15 | DBERR: DBERR, 16 | ROUTERERR: ROUTERERR, 17 | LOGINERR: LOGINERR, 18 | DATAERR: DATAERR, 19 | UNKOWNERR: UNKOWNERR, 20 | } 21 | 22 | -------------------------------------------------------------------------------- /cloudfunctions/wordRouter/utils/sm-5.js: -------------------------------------------------------------------------------- 1 | // SM-5算法 2 | // 计算下一个最优间隔的同时更新OF矩阵,从而单词在学习的时候不是一个个体,而是 3 | 4 | // 用于生成最佳区间的随机散布 NOI--near-optimal intervals 5 | // ------------------------------------------------------------- 6 | // 优点1: 通过一些差异值来加速OF矩阵优化过程 7 | // 优点2: 消除复习的块状问题,将同一时期学习的内容适当分散进行复习 8 | // 公式: NOI=PI+(OI-PI)*(1+m) m∈(-0.5, 0.5) 9 | // m需满足(设概率密度函数为f(x)): 10 | // (0, 0.5)内的概率为0.5,即 ∫[0, 0.5]f(x)dx=0.5 11 | // m=0的概率为m=0.5的概率的100倍 即 f(0)/f(0.5)=100 12 | // 假设概率密度函数为 f(x)=a*exp(-b*x) 13 | // ------------------------------------------------------------- 14 | // Piotr Wozniak求得 a=0.047; b=0.092; 15 | // 从0到m的积分记为概率p,对于每一个p都有一个对应的m存在,p∈(0, 0.5) 16 | // 生成一个(0, 1)之间的随机数,减去0.5得p,则|p|∈(0, 0.5),而p的符号可以控制m的符号 17 | // 则 ∫[0, m]f(x)dx=|p| => ∫[0, m]d( a*exp(-b*x) / (-b) )=|p| => m=-1/b*ln(1-b/a*|p|)) 18 | // 19 | // const createNOI = (PI, OI) => { 20 | // let a = 0.047 21 | // let b = 0.092 22 | // let randNum = Math.random() 23 | // let p = randNum - 0.5 24 | // console.log('random p', p) 25 | // let m = -1 / b * (Math.log((1 - b / a * Math.abs(p)))) 26 | // m = m * Math.sign(p) 27 | // console.log('random m', m) 28 | // let NOI = PI + (OI - PI) * (1 + m) 29 | // NOI = Math.round(NOI) 30 | // return NOI 31 | // } 32 | 33 | // ------------------------------------------------------------- 34 | // 由于作者给出的参数带入是有误的,采用类正态分布实现分布函数 35 | // 原型(标准正态分布):f(x) = 1/(√(2π)*Ω) * e(-x^2/(2Ω^2)) 36 | // 简化:f(x) = a*e^(-b*x^2) 37 | // f(0) = 100*f(0.5) 可求得 b = -18.420680743952367 38 | // ∫[0, 0.5]f(x)dx = 0.5 可求得 a = 2.4273047133848933 39 | // 积分计算器网址: https://zh.numberempire.com/definiteintegralcalculator.php 40 | // 画函数图像网址:https://www.desmos.com/calculator?lang=zh-CN 41 | // 这里使用能解正态分布分位数的库进行运算 42 | // f(0) = 100*f(0.5) 按正态分布算,可求得 std=0.1647525572455652 43 | // X ~ N(0,0.1647525572455652) 从0~0.5的累计分布值为0.4987967402705885 44 | // 故若要满足∫[0, 0.5]f(x)dx = 0.5,要在前面再乘上 45 | // JStat库的jStat.normal.inv( p, mean, std )可以求出N(mean,std)分布从负无穷开始累计分布为p的分位点 46 | // 因此思路转变为,首先随机获取[0, 1)的数r, r-0.5得到[-0.5, 0.5)的数m,(m*0.4987967402705885/0.5+0.5)得到累计值 47 | // 即jStat.normal.inv(abs(m*0.4987967402705885/0.5)+0.5, 0, 0.1647525572455652) 可得到分位点 48 | 49 | const jStat = require("./jstat.min.js") 50 | const createNOI = (PI, OI) => { 51 | let mean = 0 52 | let std = 0.1647525572455652 53 | let randNum = Math.random() 54 | // console.log('randNum', randNum) 55 | let p = Math.abs((randNum - 0.5) * 0.4987967402705885 / 0.5) + 0.5 56 | // console.log('random p', p) 57 | let inv_cdf = jStat.normal.inv(p, mean, std) 58 | let m = inv_cdf * Math.sign(randNum - 0.5) 59 | // console.log('random m', m) 60 | let NOI = PI + (OI - PI) * (1 + m) 61 | NOI = Math.round(NOI) 62 | return NOI 63 | } 64 | 65 | 66 | // 符号函数 67 | const sgn = (num) => { 68 | if (num < 0) { 69 | return -1 70 | } else if (num == 0) { 71 | return 0 72 | } else { 73 | return 1 74 | } 75 | } 76 | 77 | // 计算新的OF矩阵对应项 78 | // 输入: 79 | // last_i - 用于相关项目的最后(上一个)间隔(原文描述为the last interval used for the item in question) 80 | // q - 重复响应的质量 81 | // used_OF - 用于计算相关项目的最后一个间隔时使用的最佳因子 82 | // old_OF - 与项目的相关重复次数和电子因子相对应的 OF 条目的前一个值 83 | // fraction - 属于确定修改速率的范围 (0,1) 的数字 (OF矩阵的变化越快) 84 | // 输出: 85 | // new_OF - 考虑的 OF 矩阵条目的新计算值 86 | // 局部变量: 87 | // modifier - 确定 OF 值将增加或减少多少次的数字 88 | // mod5 - 在 q=5 的情况下为修饰符建议的值 89 | // mod2 - 在 q=2 的情况下为修饰符建议的值 90 | const calculateNewOF = (last_i, q, used_OF, old_OF, fraction = 0.8) => { 91 | let modifier 92 | let mod5 = (last_i + 1) / last_i 93 | if (mod5 < 1.05) mod5 = 1.05 94 | let mod2 = (last_i - 1) / last_i 95 | if (mod2 > 0.75) mod2 = 0.75 96 | if (q > 4) { 97 | modifier = 1 + (mod5 - 1) * (q - 4) 98 | } else { 99 | modifier = 1 - (1 - mod2) / 2 * (4 - q) 100 | } 101 | if (modifier < 0.05) modifier = 0.05 102 | let new_OF = used_OF * modifier 103 | if (q > 4) if (new_OF < old_OF) new_OF = old_OF 104 | if (q < 4) if (new_OF > old_OF) new_OF = old_OF 105 | new_OF = new_OF * fraction + old_OF * (1 - fraction) 106 | if (new_OF < 1.2) new_OF = 1.2 107 | new_OF = new_OF.toFixed(4) 108 | new_OF = parseFloat(new_OF) 109 | return new_OF 110 | } 111 | 112 | // 单词记录提供数据:循环次数,上次的EF,上次的间隔时间(/天), q(quality,回忆质量) 113 | // 其他:OF矩阵 114 | const sm_5 = (OF, wd_learning_record) => { 115 | let EF = wd_learning_record.EF 116 | let q = wd_learning_record.q 117 | let last_NOI = wd_learning_record.NOI 118 | let n = wd_learning_record.next_n 119 | let last_l = wd_learning_record.last_l 120 | let next_l = wd_learning_record.next_l 121 | let master = wd_learning_record.master 122 | 123 | if (master) { 124 | return { 125 | wd_learning_record: { 126 | word_id: wd_learning_record.word_id, 127 | last_l, 128 | next_l, 129 | NOI: last_NOI, 130 | EF, 131 | next_n: n, 132 | master, 133 | }, 134 | OF, 135 | } 136 | } 137 | 138 | // 计算此时与上次复习/学习的时间差(/天) 139 | let now = new Date() 140 | now.setMilliseconds(0) 141 | now.setSeconds(0) 142 | now.setMinutes(0) 143 | now.setHours(0) 144 | let last_i = Math.ceil((now.getTime() - last_l) / 86400000) 145 | // console.log('word', wd_learning_record.word_id, 'last interval', last_i) 146 | 147 | // 更改EF(由于作为键,EF规定为一位小数转换成的字符串) 148 | EF = parseFloat(EF) + (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02)) 149 | if (EF < 1.3) EF = 1.3 150 | if (EF > 2.8) EF = 2.8 151 | EF = EF.toFixed(1) 152 | 153 | // 更改矩阵对应项,这里认为若实际间隔时间超过所需间隔时间的1.5倍 154 | // 则视为极大异常值,规整为1.5倍,且不更改矩阵 155 | let used_OF = OF[EF][n - 1] 156 | if (!used_OF) used_OF = 1.2 157 | n++ 158 | if (!OF[EF][n - 1]) OF[EF][n - 1] = 1.2 159 | if (last_i <= 1.5 * last_NOI) { 160 | let old_OF = OF[EF][n - 1] 161 | let new_OF = calculateNewOF(last_i, q, used_OF, old_OF) 162 | // console.log('new_OF of', 'OF[', EF, '][', n - 1, ']:', new_OF) 163 | OF[EF][n - 1] = new_OF 164 | } else { 165 | // console.log('last_i', last_i, 'is longer than 1.5 expected interval :', last_NOI) 166 | last_i = Math.round(last_NOI * 1.5) 167 | } 168 | 169 | // 计算最优间隔时长并进行指定分布的随机分散 170 | // 同时计算下次需要复习的时间(1970.1.1至今毫秒数表示) 171 | let NOI 172 | if (q < 2) { 173 | n = 0 174 | NOI = 1 175 | } else if (q < 3) { 176 | n = 1 177 | let interval = OF[EF][0] 178 | NOI = Math.round(interval) 179 | } else { 180 | let interval = n == 1 ? 5 : OF[EF][n - 1] * last_i 181 | // 若下个最优间隔时间大于100天,则将单词标记为已掌握 182 | if (interval > 100) master = true 183 | console.log('next optimal interval', interval) 184 | NOI = Math.round(createNOI(last_i, interval)) 185 | if (NOI > 100 && !master) NOI = 100 186 | if (NOI < 0 && !master) NOI = 1 187 | } 188 | last_l = now.getTime() 189 | next_l = last_l + NOI * 86400000 190 | 191 | return { 192 | wd_learning_record: { 193 | word_id: wd_learning_record.word_id, 194 | last_l, 195 | next_l, 196 | NOI, 197 | EF, 198 | next_n: n, 199 | master, 200 | }, 201 | OF, 202 | } 203 | } 204 | 205 | module.exports = { 206 | sm_5: sm_5, 207 | } -------------------------------------------------------------------------------- /images/after_login_index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/after_login_index.jpg -------------------------------------------------------------------------------- /images/after_login_overview_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/after_login_overview_1.jpg -------------------------------------------------------------------------------- /images/after_login_overview_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/after_login_overview_2.jpg -------------------------------------------------------------------------------- /images/after_login_user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/after_login_user.jpg -------------------------------------------------------------------------------- /images/before_login_index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/before_login_index.jpg -------------------------------------------------------------------------------- /images/before_login_user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/before_login_user.jpg -------------------------------------------------------------------------------- /images/learning_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/learning_1.jpg -------------------------------------------------------------------------------- /images/learning_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/learning_3.jpg -------------------------------------------------------------------------------- /images/learning_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/learning_4.jpg -------------------------------------------------------------------------------- /images/learning_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/learning_5.jpg -------------------------------------------------------------------------------- /images/learning_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/learning_6.jpg -------------------------------------------------------------------------------- /images/learning_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/learning_8.jpg -------------------------------------------------------------------------------- /images/login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/login.jpg -------------------------------------------------------------------------------- /images/miniprogram_QRcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/miniprogram_QRcode.jpg -------------------------------------------------------------------------------- /images/search_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/search_1.jpg -------------------------------------------------------------------------------- /images/search_translation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/search_translation.jpg -------------------------------------------------------------------------------- /images/search_word_big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/search_word_big.jpg -------------------------------------------------------------------------------- /images/search_word_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/search_word_small.jpg -------------------------------------------------------------------------------- /images/settings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/settings.jpg -------------------------------------------------------------------------------- /images/title_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/title_1.png -------------------------------------------------------------------------------- /images/title_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/title_2.png -------------------------------------------------------------------------------- /images/title_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/title_3.png -------------------------------------------------------------------------------- /images/title_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/title_4.png -------------------------------------------------------------------------------- /images/word_detail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/word_detail.jpg -------------------------------------------------------------------------------- /images/word_list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/word_list.jpg -------------------------------------------------------------------------------- /images/整体框架图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/images/整体框架图.png -------------------------------------------------------------------------------- /miniprogram/app.js: -------------------------------------------------------------------------------- 1 | // app.js 2 | const rescontent = require('./utils/response_content.js') 3 | const { formatTime } = require('./utils/format_time.js') 4 | const userApi = require("./utils/userApi.js") 5 | 6 | App({ 7 | onLaunch: function () { 8 | if (!wx.cloud) { 9 | console.error('请使用 2.2.3 或以上的基础库以使用云能力'); 10 | } else { 11 | wx.cloud.init({ 12 | // env 参数说明: 13 | // env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源 14 | // 此处请填入环境 ID, 环境 ID 可打开云控制台查看 15 | // 如不填则使用默认环境(第一个创建的环境) 16 | // env: 'my-env-id', 17 | traceUser: true, 18 | }); 19 | } 20 | 21 | this.checkLogin() 22 | wx.disableAlertBeforeUnload() 23 | }, 24 | 25 | globalData: { 26 | isLogin: false, 27 | tryingLogin: true, 28 | userInfo: { 29 | // user_id: 2, 30 | // l_book_id: 2, 31 | settings: { 32 | // learn_repeat_t: 3, 33 | // group_size: 10, 34 | // learn_first_m: 'chooseTrans', 35 | // learn_second_m: 'recallTrans', 36 | // learn_third_m: 'recallWord', 37 | // learn_fourth_m: 'recallTrans', 38 | // timing: true, 39 | // timing_duration: 1000, 40 | // autoplay: false, 41 | // type: 1, 42 | // review_repeat_t: 2, 43 | // review_first_m: 'recallTrans', 44 | // review_second_m: 'chooseTrans', 45 | // review_second_m: 'recallWord', 46 | // review_third_m: 'recallTrans', 47 | } 48 | }, 49 | updatedForIndex: false, 50 | updatedForOverview: false, 51 | forChangeAvatar: { 52 | change: false, 53 | tempImgSrc: '', 54 | imgSrc: '', 55 | } 56 | }, 57 | 58 | checkLogin: async function () { 59 | this.globalData.tryingLogin = true 60 | // let history = wx.getStorageSync('history') 61 | // wx.clearStorageSync() 62 | // wx.setStorageSync('history', history) 63 | // console.log('checkLogin') 64 | // console.log('this.globalData.tryingLogin ', this.globalData.tryingLogin) 65 | let storageContent = wx.getStorageSync('userInfo') 66 | if (storageContent && (new Date().getTime() - storageContent.time) < 86400000 * 2) { 67 | let res = await userApi.getUserInfoViaId({ user_id: storageContent.info.user_id }) 68 | if (res.errorcode == rescontent.SUCCESS.errorcode) { 69 | this.globalData.isLogin = true 70 | this.globalData.userInfo = res.data 71 | let lastlogin = formatTime(res.data.last_login) 72 | wx.showToast({ 73 | title: `自动登录成功,上次登录时间 ${lastlogin}`, 74 | icon: 'none', 75 | duration: 1500, 76 | }) 77 | storageContent.info = res.data 78 | wx.setStorageSync('userInfo', storageContent) 79 | } else { 80 | wx.showToast({ 81 | title: '自动登录失败,请重新登录', 82 | icon: 'none', 83 | duration: 1500, 84 | }) 85 | wx.removeStorageSync('userInfo') 86 | } 87 | } else if (storageContent) { 88 | wx.showToast({ 89 | title: '登录已过期,请重新登录', 90 | icon: 'none', 91 | duration: 1500, 92 | }) 93 | wx.removeStorageSync('userInfo') 94 | } 95 | this.globalData.tryingLogin = false 96 | // console.log('this.globalData.tryingLogin ', this.globalData.tryingLogin) 97 | }, 98 | }); 99 | -------------------------------------------------------------------------------- /miniprogram/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/user/user", 5 | "pages/overview/overview", 6 | "pages/login/login", 7 | "pages/search/search", 8 | "pages/word_detail/word_detail", 9 | "pages/learning/learning", 10 | "pages/review/review", 11 | "pages/word_list/word_list", 12 | "pages/image_cropper/image_cropper", 13 | "pages/user_settings/user_settings" 14 | ], 15 | "window": { 16 | "backgroundColor": "#FFFFFF", 17 | "backgroundTextStyle": "light", 18 | "navigationBarBackgroundColor": "#FFFFFF", 19 | "navigationBarTitleText": "学不会单词", 20 | "navigationBarTextStyle": "black" 21 | }, 22 | "tabBar": { 23 | "color": "#F0F0F0", 24 | "backgroundColor": "#FFFFFF", 25 | "selectedColor": "#6DAFFE", 26 | "borderStyle": "white", 27 | "position": "bottom", 28 | "list": [ 29 | { 30 | "pagePath": "pages/index/index", 31 | "text": " ", 32 | "iconPath": "static/images/tab-learn-CDCDCD.png", 33 | "selectedIconPath": "static/images/tab-learn-A6D6FA.png" 34 | }, 35 | { 36 | "pagePath": "pages/overview/overview", 37 | "text": " ", 38 | "iconPath": "static/images/tab-overview-CDCDCD.png", 39 | "selectedIconPath": "static/images/tab-overview-A6D6FA.png" 40 | }, 41 | { 42 | "pagePath": "pages/user/user", 43 | "text": " ", 44 | "iconPath": "static/images/tab-user-CDCDCD.png", 45 | "selectedIconPath": "static/images/tab-user-A6D6FA.png" 46 | } 47 | ] 48 | }, 49 | "sitemapLocation": "sitemap.json", 50 | "style": "v2", 51 | "lazyCodeLoading": "requiredComponents" 52 | } -------------------------------------------------------------------------------- /miniprogram/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | 3 | @import './static/iconfont.wxss'; 4 | @import './static/color.wxss'; 5 | 6 | .container { 7 | display: flex; 8 | flex-direction: column; 9 | align-items: center; 10 | box-sizing: border-box; 11 | } 12 | 13 | button { 14 | background: initial; 15 | } 16 | 17 | button:focus { 18 | outline: 0; 19 | } 20 | 21 | button::after { 22 | border: none; 23 | } 24 | 25 | 26 | page { 27 | background: #f6f6f6; 28 | display: flex; 29 | flex-direction: column; 30 | justify-content: flex-start; 31 | /* overflow: hidden; */ 32 | } -------------------------------------------------------------------------------- /miniprogram/components/cloudTipModal/index.js: -------------------------------------------------------------------------------- 1 | // miniprogram/components/cloudTipModal/index.js 2 | const { isMac } = require('../../envList.js'); 3 | 4 | Component({ 5 | 6 | /** 7 | * 页面的初始数据 8 | */ 9 | data: { 10 | showUploadTip: false, 11 | tipText: isMac ? 'sh ./uploadCloudFunction.sh' : './uploadCloudFunction.bat' 12 | }, 13 | properties: { 14 | showUploadTipProps: Boolean 15 | }, 16 | observers: { 17 | showUploadTipProps: function(showUploadTipProps) { 18 | this.setData({ 19 | showUploadTip: showUploadTipProps 20 | }); 21 | } 22 | }, 23 | methods: { 24 | onChangeShowUploadTip() { 25 | this.setData({ 26 | showUploadTip: !this.data.showUploadTip 27 | }); 28 | }, 29 | 30 | copyShell() { 31 | wx.setClipboardData({ 32 | data: this.data.tipText, 33 | }); 34 | }, 35 | } 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /miniprogram/components/cloudTipModal/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "component": true 4 | } -------------------------------------------------------------------------------- /miniprogram/components/cloudTipModal/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 体验前需部署云资源 6 | 请开启调试器进入终端窗口,复制并运行以下命令 7 | 8 | {{tipText}} 9 | 复制 10 | 11 | 已执行命令 12 | 13 | 14 | -------------------------------------------------------------------------------- /miniprogram/components/cloudTipModal/index.wxss: -------------------------------------------------------------------------------- 1 | .install_tip_back { 2 | position: fixed; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | background-color: rgba(0,0,0,0.4); 8 | z-index: 1; 9 | } 10 | 11 | .install_tip_detail { 12 | position: fixed; 13 | background-color: white; 14 | right: 0; 15 | bottom: 0; 16 | left: 0; 17 | top: 60%; 18 | border-radius: 40rpx 40rpx 0 0; 19 | padding: 50rpx; 20 | z-index: 9; 21 | } 22 | 23 | .install_tip_detail_title { 24 | font-weight: 400; 25 | font-size: 40rpx; 26 | text-align: center; 27 | } 28 | 29 | .install_tip_detail_tip { 30 | font-size: 25rpx; 31 | color: rgba(0,0,0,0.4); 32 | margin-top: 20rpx; 33 | text-align: center; 34 | } 35 | 36 | .install_tip_detail_shell { 37 | margin: 70rpx 0; 38 | display: flex; 39 | justify-content: center; 40 | } 41 | 42 | .install_tip_detail_copy { 43 | color: #546488; 44 | margin-left: 10rpx; 45 | } 46 | 47 | .install_tip_detail_button { 48 | color: #07C160; 49 | font-weight: 500; 50 | background-color: rgba(0,0,0,0.1); 51 | width: 60%; 52 | text-align: center; 53 | height: 90rpx; 54 | line-height: 90rpx; 55 | border-radius: 10rpx; 56 | margin: 0 auto; 57 | } -------------------------------------------------------------------------------- /miniprogram/components/ec-canvas/ec-canvas.js: -------------------------------------------------------------------------------- 1 | import WxCanvas from './wx-canvas'; 2 | import * as echarts from './echarts'; 3 | // import * as echarts from './echartsForBar'; 4 | 5 | let ctx; 6 | 7 | function compareVersion(v1, v2) { 8 | v1 = v1.split('.') 9 | v2 = v2.split('.') 10 | const len = Math.max(v1.length, v2.length) 11 | 12 | while (v1.length < len) { 13 | v1.push('0') 14 | } 15 | while (v2.length < len) { 16 | v2.push('0') 17 | } 18 | 19 | for (let i = 0; i < len; i++) { 20 | const num1 = parseInt(v1[i]) 21 | const num2 = parseInt(v2[i]) 22 | 23 | if (num1 > num2) { 24 | return 1 25 | } else if (num1 < num2) { 26 | return -1 27 | } 28 | } 29 | return 0 30 | } 31 | 32 | Component({ 33 | properties: { 34 | canvasId: { 35 | type: String, 36 | value: 'ec-canvas' 37 | }, 38 | 39 | ec: { 40 | type: Object 41 | }, 42 | 43 | forceUseOldCanvas: { 44 | type: Boolean, 45 | value: false 46 | } 47 | }, 48 | 49 | data: { 50 | isUseNewCanvas: false 51 | }, 52 | 53 | ready: function () { 54 | // Disable prograssive because drawImage doesn't support DOM as parameter 55 | // See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html 56 | echarts.registerPreprocessor(option => { 57 | if (option && option.series) { 58 | if (option.series.length > 0) { 59 | option.series.forEach(series => { 60 | series.progressive = 0; 61 | }); 62 | } 63 | else if (typeof option.series === 'object') { 64 | option.series.progressive = 0; 65 | } 66 | } 67 | }); 68 | 69 | if (!this.data.ec) { 70 | console.warn('组件需绑定 ec 变量,例:'); 72 | return; 73 | } 74 | 75 | if (!this.data.ec.lazyLoad) { 76 | this.init(); 77 | this.triggerEvent('initok', {isinit: true}, {}) 78 | } 79 | }, 80 | 81 | methods: { 82 | init: function (callback) { 83 | const version = wx.getSystemInfoSync().SDKVersion 84 | 85 | const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0; 86 | const forceUseOldCanvas = this.data.forceUseOldCanvas; 87 | const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas; 88 | this.setData({ isUseNewCanvas }); 89 | 90 | if (forceUseOldCanvas && canUseNewCanvas) { 91 | console.warn('开发者强制使用旧canvas,建议关闭'); 92 | } 93 | 94 | if (isUseNewCanvas) { 95 | // console.log('微信基础库版本大于2.9.0,开始使用'); 96 | // 2.9.0 可以使用 97 | this.initByNewWay(callback); 98 | } else { 99 | const isValid = compareVersion(version, '1.9.91') >= 0 100 | if (!isValid) { 101 | console.error('微信基础库版本过低,需大于等于 1.9.91。' 102 | + '参见:https://github.com/ecomfe/echarts-for-weixin' 103 | + '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82'); 104 | return; 105 | } else { 106 | console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能'); 107 | this.initByOldWay(callback); 108 | } 109 | } 110 | }, 111 | 112 | initByOldWay(callback) { 113 | // 1.9.91 <= version < 2.9.0:原来的方式初始化 114 | ctx = wx.createCanvasContext(this.data.canvasId, this); 115 | const canvas = new WxCanvas(ctx, this.data.canvasId, false); 116 | 117 | echarts.setCanvasCreator(() => { 118 | return canvas; 119 | }); 120 | // const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr 121 | const canvasDpr = 1 122 | var query = wx.createSelectorQuery().in(this); 123 | query.select('.ec-canvas').boundingClientRect(res => { 124 | if (typeof callback === 'function') { 125 | this.chart = callback(canvas, res.width, res.height, canvasDpr); 126 | } 127 | else if (this.data.ec && typeof this.data.ec.onInit === 'function') { 128 | this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr); 129 | } 130 | else { 131 | this.triggerEvent('init', { 132 | canvas: canvas, 133 | width: res.width, 134 | height: res.height, 135 | canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init 136 | }); 137 | } 138 | }).exec(); 139 | }, 140 | 141 | initByNewWay(callback) { 142 | // version >= 2.9.0:使用新的方式初始化 143 | const query = wx.createSelectorQuery().in(this) 144 | query 145 | .select('.ec-canvas') 146 | .fields({ node: true, size: true }) 147 | .exec(res => { 148 | const canvasNode = res[0].node 149 | this.canvasNode = canvasNode 150 | 151 | const canvasDpr = wx.getSystemInfoSync().pixelRatio 152 | const canvasWidth = res[0].width 153 | const canvasHeight = res[0].height 154 | 155 | const ctx = canvasNode.getContext('2d') 156 | 157 | const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode) 158 | echarts.setCanvasCreator(() => { 159 | return canvas 160 | }) 161 | 162 | if (typeof callback === 'function') { 163 | this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr) 164 | } else if (this.data.ec && typeof this.data.ec.onInit === 'function') { 165 | this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr) 166 | } else { 167 | this.triggerEvent('init', { 168 | canvas: canvas, 169 | width: canvasWidth, 170 | height: canvasHeight, 171 | dpr: canvasDpr 172 | }) 173 | } 174 | }) 175 | }, 176 | canvasToTempFilePath(opt) { 177 | if (this.data.isUseNewCanvas) { 178 | // 新版 179 | const query = wx.createSelectorQuery().in(this) 180 | query 181 | .select('.ec-canvas') 182 | .fields({ node: true, size: true }) 183 | .exec(res => { 184 | const canvasNode = res[0].node 185 | opt.canvas = canvasNode 186 | wx.canvasToTempFilePath(opt) 187 | }) 188 | } else { 189 | // 旧的 190 | if (!opt.canvasId) { 191 | opt.canvasId = this.data.canvasId; 192 | } 193 | ctx.draw(true, () => { 194 | wx.canvasToTempFilePath(opt, this); 195 | }); 196 | } 197 | }, 198 | 199 | touchStart(e) { 200 | if (this.chart && e.touches.length > 0) { 201 | var touch = e.touches[0]; 202 | var handler = this.chart.getZr().handler; 203 | handler.dispatch('mousedown', { 204 | zrX: touch.x, 205 | zrY: touch.y 206 | }); 207 | handler.dispatch('mousemove', { 208 | zrX: touch.x, 209 | zrY: touch.y 210 | }); 211 | handler.processGesture(wrapTouch(e), 'start'); 212 | } 213 | }, 214 | 215 | touchMove(e) { 216 | if (this.chart && e.touches.length > 0) { 217 | var touch = e.touches[0]; 218 | var handler = this.chart.getZr().handler; 219 | handler.dispatch('mousemove', { 220 | zrX: touch.x, 221 | zrY: touch.y 222 | }); 223 | handler.processGesture(wrapTouch(e), 'change'); 224 | } 225 | }, 226 | 227 | touchEnd(e) { 228 | if (this.chart) { 229 | const touch = e.changedTouches ? e.changedTouches[0] : {}; 230 | var handler = this.chart.getZr().handler; 231 | handler.dispatch('mouseup', { 232 | zrX: touch.x, 233 | zrY: touch.y 234 | }); 235 | handler.dispatch('click', { 236 | zrX: touch.x, 237 | zrY: touch.y 238 | }); 239 | handler.processGesture(wrapTouch(e), 'end'); 240 | } 241 | } 242 | } 243 | }); 244 | 245 | function wrapTouch(event) { 246 | for (let i = 0; i < event.touches.length; ++i) { 247 | const touch = event.touches[i]; 248 | touch.offsetX = touch.x; 249 | touch.offsetY = touch.y; 250 | } 251 | return event; 252 | } 253 | -------------------------------------------------------------------------------- /miniprogram/components/ec-canvas/ec-canvas.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /miniprogram/components/ec-canvas/ec-canvas.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /miniprogram/components/ec-canvas/ec-canvas.wxss: -------------------------------------------------------------------------------- 1 | .ec-canvas { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /miniprogram/components/ec-canvas/wx-canvas.js: -------------------------------------------------------------------------------- 1 | export default class WxCanvas { 2 | constructor(ctx, canvasId, isNew, canvasNode) { 3 | this.ctx = ctx; 4 | this.canvasId = canvasId; 5 | this.chart = null; 6 | this.isNew = isNew 7 | if (isNew) { 8 | this.canvasNode = canvasNode; 9 | } 10 | else { 11 | this._initStyle(ctx); 12 | } 13 | 14 | // this._initCanvas(zrender, ctx); 15 | 16 | this._initEvent(); 17 | } 18 | 19 | getContext(contextType) { 20 | if (contextType === '2d') { 21 | return this.ctx; 22 | } 23 | } 24 | 25 | // canvasToTempFilePath(opt) { 26 | // if (!opt.canvasId) { 27 | // opt.canvasId = this.canvasId; 28 | // } 29 | // return wx.canvasToTempFilePath(opt, this); 30 | // } 31 | 32 | setChart(chart) { 33 | this.chart = chart; 34 | } 35 | 36 | attachEvent() { 37 | // noop 38 | } 39 | 40 | detachEvent() { 41 | // noop 42 | } 43 | 44 | _initCanvas(zrender, ctx) { 45 | zrender.util.getContext = function () { 46 | return ctx; 47 | }; 48 | 49 | zrender.util.$override('measureText', function (text, font) { 50 | ctx.font = font || '12px sans-serif'; 51 | return ctx.measureText(text); 52 | }); 53 | } 54 | 55 | _initStyle(ctx) { 56 | ctx.createRadialGradient = () => { 57 | return ctx.createCircularGradient(arguments); 58 | }; 59 | } 60 | 61 | _initEvent() { 62 | this.event = {}; 63 | const eventNames = [{ 64 | wxName: 'touchStart', 65 | ecName: 'mousedown' 66 | }, { 67 | wxName: 'touchMove', 68 | ecName: 'mousemove' 69 | }, { 70 | wxName: 'touchEnd', 71 | ecName: 'mouseup' 72 | }, { 73 | wxName: 'touchEnd', 74 | ecName: 'click' 75 | }]; 76 | 77 | eventNames.forEach(name => { 78 | this.event[name.wxName] = e => { 79 | const touch = e.touches[0]; 80 | this.chart.getZr().handler.dispatch(name.ecName, { 81 | zrX: name.wxName === 'tap' ? touch.clientX : touch.x, 82 | zrY: name.wxName === 'tap' ? touch.clientY : touch.y 83 | }); 84 | }; 85 | }); 86 | } 87 | 88 | set width(w) { 89 | if (this.canvasNode) this.canvasNode.width = w 90 | } 91 | set height(h) { 92 | if (this.canvasNode) this.canvasNode.height = h 93 | } 94 | 95 | get width() { 96 | if (this.canvasNode) 97 | return this.canvasNode.width 98 | return 0 99 | } 100 | get height() { 101 | if (this.canvasNode) 102 | return this.canvasNode.height 103 | return 0 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /miniprogram/components/image-cropper/image-cropper.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } -------------------------------------------------------------------------------- /miniprogram/components/image-cropper/image-cropper.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /miniprogram/components/image-cropper/image-cropper.wxss: -------------------------------------------------------------------------------- 1 | .image-cropper { 2 | background: rgba(14, 13, 13, .8); 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | width: 100vw; 7 | height: 100vh; 8 | z-index: 1; 9 | } 10 | 11 | .image-cropper .main { 12 | position: absolute; 13 | width: 100vw; 14 | height: 100vh; 15 | overflow: hidden; 16 | } 17 | 18 | .image-cropper .content { 19 | z-index: 9; 20 | position: absolute; 21 | width: 100vw; 22 | height: 100vh; 23 | display: flex; 24 | flex-direction: column; 25 | pointer-events: none; 26 | } 27 | 28 | .image-cropper .bg_black { 29 | background: rgba(0, 0, 0, 0.8) !important; 30 | } 31 | 32 | .image-cropper .bg_gray { 33 | background: rgba(0, 0, 0, 0.45); 34 | transition-duration: .35s; 35 | } 36 | 37 | .image-cropper .content>.content_top { 38 | pointer-events: none; 39 | } 40 | 41 | .image-cropper .content>.content_middle { 42 | display: flex; 43 | height: 200px; 44 | width: 100%; 45 | } 46 | 47 | .image-cropper .content_middle_middle { 48 | width: 200px; 49 | box-sizing: border-box; 50 | position: relative; 51 | transition-duration: .3s; 52 | } 53 | 54 | .image-cropper .content_middle_right { 55 | flex: auto; 56 | } 57 | 58 | .image-cropper .content>.content_bottom { 59 | flex: auto; 60 | } 61 | 62 | .image-cropper .img { 63 | z-index: 2; 64 | top: 0; 65 | left: 0; 66 | position: absolute; 67 | border: none; 68 | width: 100%; 69 | backface-visibility: hidden; 70 | transform-origin: center; 71 | } 72 | 73 | .image-cropper .image-cropper-canvas { 74 | position: fixed; 75 | background: white; 76 | width: 150px; 77 | height: 150px; 78 | z-index: 10; 79 | top: -200%; 80 | pointer-events: none; 81 | } 82 | 83 | .image-cropper .border { 84 | background: white; 85 | pointer-events: auto; 86 | position: absolute; 87 | } 88 | 89 | .image-cropper .border-top-left { 90 | left: -2.5px; 91 | top: -2.5px; 92 | height: 2.5px; 93 | width: 33rpx; 94 | } 95 | 96 | .image-cropper .border-top-right { 97 | right: -2.5px; 98 | top: -2.5px; 99 | height: 2.5px; 100 | width: 33rpx; 101 | } 102 | 103 | .image-cropper .border-right-top { 104 | top: -1px; 105 | width: 2.5px; 106 | height: 30rpx; 107 | right: -2.5px; 108 | } 109 | 110 | .image-cropper .border-right-bottom { 111 | width: 2.5px; 112 | height: 30rpx; 113 | right: -2.5px; 114 | bottom: -1px; 115 | } 116 | 117 | .image-cropper .border-bottom-left { 118 | height: 2.5px; 119 | width: 33rpx; 120 | bottom: -2.5px; 121 | left: -2.5px; 122 | } 123 | 124 | .image-cropper .border-bottom-right { 125 | height: 2.5px; 126 | width: 33rpx; 127 | bottom: -2.5px; 128 | right: -2.5px; 129 | } 130 | 131 | .image-cropper .border-left-top { 132 | top: -1px; 133 | width: 2.5px; 134 | height: 30rpx; 135 | left: -2.5px; 136 | } 137 | 138 | .image-cropper .border-left-bottom { 139 | width: 2.5px; 140 | height: 30rpx; 141 | left: -2.5px; 142 | bottom: -1px; 143 | } -------------------------------------------------------------------------------- /miniprogram/components/mp-progress/mp-progress.js: -------------------------------------------------------------------------------- 1 | // import MpProgress from "../progress.min.js"; 2 | import MpProgress from "./progress.js"; 3 | 4 | Component({ 5 | options: { 6 | addGlobalClass: true, 7 | }, 8 | properties: { 9 | config: { 10 | type: Object, 11 | value: {} 12 | }, 13 | percentage: { 14 | type: Number, 15 | value: 0 16 | }, 17 | reset: { 18 | type: Boolean, 19 | value: false 20 | }, 21 | isStop: { 22 | type: Boolean, 23 | value: false 24 | } 25 | }, 26 | data: { 27 | customOptions: { 28 | // canvasSize: { 29 | // width: 100, 30 | // height: 100 31 | // }, 32 | percent: 100 33 | }, 34 | percentage: 100, 35 | canvasId: `mp_progress_${new Date().getTime()}` 36 | }, 37 | attached() { 38 | // const customOptions = Object.assign({}, this.data.customOptions, this.data.config); 39 | // this.setData({ 40 | // customOptions, 41 | // }); 42 | // let canvasId = `mp_progress_${new Date().getTime()}`; 43 | // this.setData({ 44 | // canvasId, 45 | // }); 46 | }, 47 | ready() { 48 | // this._mpprogress = new MpProgress(Object.assign({}, this.data.customOptions, { canvasId: this.data.canvasId, target: this })); 49 | // this._mpprogress.draw(this.data.percentage || 0); 50 | }, 51 | observers: { 52 | 'config': function (config) { 53 | // console.log('Get Config') 54 | if (JSON.stringify(config) == "{}") return; 55 | // console.log("go init"); 56 | // const customOptions = Object.assign({}, this.data.customOptions, this.data.config); 57 | const customOptions = config; 58 | // let canvasId = `mp_progress_${new Date().getTime()}`; 59 | this.setData({ 60 | customOptions, 61 | // canvasId, 62 | }); 63 | let options = JSON.parse(JSON.stringify(this.data.customOptions)); 64 | options.canvasId = this.data.canvasId; 65 | options.target = this; 66 | this._mpprogress = new MpProgress(options); 67 | // this._mpprogress = new MpProgress(Object.assign({}, this.data.customOptions, { canvasId: this.data.canvasId, target: this })); 68 | this._mpprogress.draw(this.data.percentage || 0); 69 | }, 70 | 'reset': function (reset) { 71 | if (reset) { 72 | if (this._mpprogress) { 73 | this._mpprogress.stopAnimation(true); 74 | } 75 | // let canvasId = `mp_progress_${new Date().getTime()}`; 76 | // this.setData({ 77 | // canvasId, 78 | // }); 79 | let options = JSON.parse(JSON.stringify(this.data.customOptions)); 80 | options.canvasId = this.data.canvasId; 81 | options.target = this; 82 | this._mpprogress = new MpProgress(options); 83 | // this._mpprogress = new MpProgress(Object.assign({}, this.data.customOptions, { canvasId: this.data.canvasId, target: this })); 84 | // delete this._mpprogress 85 | this._mpprogress.draw(this.data.percentage || 0); 86 | } 87 | }, 88 | 'isStop': function (isStop) { 89 | if (isStop && this._mpprogress) { this._mpprogress.stopAnimation(isStop); } 90 | // this._mpprogress.stopAnimation(); 91 | }, 92 | // 'percentage': function (percentage) { 93 | // if (this._mpprogress) { 94 | // // 第一次进来的时候还没有初始化完成 95 | // this._mpprogress.draw(percentage); 96 | // } 97 | // }, 98 | } 99 | }); 100 | -------------------------------------------------------------------------------- /miniprogram/components/mp-progress/mp-progress.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /miniprogram/components/mp-progress/mp-progress.wxml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /miniprogram/components/mp-progress/progress.min.js: -------------------------------------------------------------------------------- 1 | /*! mp-progress.js v1.2.13 https://www.npmjs.com/package/mp-progress */ 2 | var t,e;t=window,e=function(){return i={},o.m=n=[function(t,e,n){"use strict";function i(e,t){var n,i=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),i.push.apply(i,n)),i}function o(o){for(var t=1;t100]: 已自动调整为100")),this._options.percentage=+t||0,this._context)this.drawFn();else try{var n=this._options.target,i=wx.createSelectorQuery().in(n);n.$wx&&n.$wx.$wepy&&(i=wx.createSelectorQuery()),i.select("#".concat(this._options.canvasId)).node(function(t){var e=t.node;o._requestAnimationFrame=e.requestAnimationFrame.bind(e);var n=e.getContext("2d"),i=wx.getSystemInfoSync().pixelRatio;e.width=e._width*i,e.height=e._height*i,n.scale(i,i),o._context=n,o.drawFn()}).exec()}catch(t){console.warn(t)}else console.warn("[绘图过程出现错误]: 调用draw方法必须传入百分比参数")}},{key:"drawFn",value:function(){var n=this;try{var t=this._options.barStyle;if(0 ∫[0, m]d( a*exp(-b*x) / (-b) )=|p| => m=-1/b*ln(1-b/a*|p|)) 18 | // 19 | // const createNOI = (PI, OI) => { 20 | // let a = 0.047 21 | // let b = 0.092 22 | // let randNum = Math.random() 23 | // let p = randNum - 0.5 24 | // console.log('random p', p) 25 | // let m = -1 / b * (Math.log((1 - b / a * Math.abs(p)))) 26 | // m = m * Math.sign(p) 27 | // console.log('random m', m) 28 | // let NOI = PI + (OI - PI) * (1 + m) 29 | // NOI = Math.round(NOI) 30 | // return NOI 31 | // } 32 | // ------------------------------------------------------------- 33 | // 由于作者给出的参数带入是有误的,采用类正态分布实现分布函数 34 | // 原型(标准正态分布):f(x) = 1/(√(2π)*Ω) * e(-x^2/(2Ω^2)) 35 | // 简化:f(x) = a*e(-b*x^2) 36 | // f(0) = 100*f(0.5) 可求得 b = -18.420680743952367 37 | // ∫[0, 0.5]f(x)dx = 0.5 可求得 a = 2.4273047133848933 38 | // 积分计算器网址: https://zh.numberempire.com/definiteintegralcalculator.php 39 | // 画函数图像网址:https://www.desmos.com/calculator?lang=zh-CN 40 | // 这里使用能解正态分布分位数的库进行运算 41 | // f(0) = 100*f(0.5) 按正态分布算,可求得 std=0.1647525572455652 42 | // X ~ N(0,0.1647525572455652) 从0~0.5的累计分布值为0.4987967402705885 43 | // 故若要满足∫[0, 0.5]f(x)dx = 0.5,要在前面再乘上 44 | // JStat库的jStat.normal.inv( p, mean, std )可以求出N(mean,std)分布从负无穷开始累计分布为p的分位点 45 | // 因此思路转变为,首先随机获取[0, 1)的数r, r-0.5得到[-0.5, 0.5)的数m,(m*0.4987967402705885/0.5+0.5)得到累计值 46 | // 即jStat.normal.inv(abs(m*0.4987967402705885/0.5)+0.5, 0, 0.1647525572455652) 可得到分位点 47 | 48 | const jStat = require("./jstat.min.js") 49 | const createNOI = (PI, OI) => { 50 | let mean = 0 51 | let std = 0.1647525572455652 52 | let randNum = Math.random() 53 | // console.log('randNum', randNum) 54 | let p = Math.abs((randNum - 0.5) * 0.4987967402705885 / 0.5) + 0.5 55 | // console.log('random p', p) 56 | let inv_cdf = jStat.normal.inv(p, mean, std) 57 | let m = inv_cdf * Math.sign(randNum - 0.5) 58 | // console.log('random m', m) 59 | let NOI = PI + (OI - PI) * (1 + m) 60 | NOI = Math.round(NOI) 61 | return NOI 62 | } 63 | 64 | 65 | // 符号函数 66 | const sgn = (num) => { 67 | if (num < 0) { 68 | return -1 69 | } else if (num == 0) { 70 | return 0 71 | } else { 72 | return 1 73 | } 74 | } 75 | 76 | // 计算新的OF矩阵对应项 77 | // 输入: 78 | // last_i - 用于相关项目的最后(上一个)间隔(原文描述为the last interval used for the item in question) 79 | // q - 重复响应的质量 80 | // used_OF - 用于计算相关项目的最后一个间隔时使用的最佳因子 81 | // old_OF - 与项目的相关重复次数和电子因子相对应的 OF 条目的前一个值 82 | // fraction - 属于确定修改速率的范围 (0,1) 的数字 (OF矩阵的变化越快) 83 | // 输出: 84 | // new_OF - 考虑的 OF 矩阵条目的新计算值 85 | // 局部变量: 86 | // modifier - 确定 OF 值将增加或减少多少次的数字 87 | // mod5 - 在 q=5 的情况下为修饰符建议的值 88 | // mod2 - 在 q=2 的情况下为修饰符建议的值 89 | const calculateNewOF = (last_i, q, used_OF, old_OF, fraction = 0.8) => { 90 | let modifier 91 | let mod5 = (last_i + 1) / last_i 92 | if (mod5 < 1.05) mod5 = 1.05 93 | let mod2 = (last_i - 1) / last_i 94 | if (mod2 > 0.75) mod2 = 0.75 95 | if (q > 4) { 96 | modifier = 1 + (mod5 - 1) * (q - 4) 97 | } else { 98 | modifier = 1 - (1 - mod2) / 2 * (4 - q) 99 | } 100 | if (modifier < 0.05) modifier = 0.05 101 | let new_OF = used_OF * modifier 102 | if (q > 4) if (new_OF < old_OF) new_OF = old_OF 103 | if (q < 4) if (new_OF > old_OF) new_OF = old_OF 104 | new_OF = new_OF * fraction + old_OF * (1 - fraction) 105 | if (new_OF < 1.2) new_OF = 1.2 106 | new_OF = new_OF.toFixed(4) 107 | new_OF = parseFloat(new_OF) 108 | return new_OF 109 | } 110 | 111 | // 单词记录提供数据:循环次数,上次的EF,上次的间隔时间(/天), q(quality,回忆质量) 112 | // 其他:OF矩阵 113 | const sm_5 = (OF, wd_learning_record) => { 114 | let EF = wd_learning_record.EF 115 | let q = wd_learning_record.q 116 | let last_NOI = wd_learning_record.NOI 117 | let n = wd_learning_record.next_n 118 | let last_l = wd_learning_record.last_l 119 | let next_l = wd_learning_record.next_l 120 | let master = wd_learning_record.master 121 | 122 | if (master) { 123 | return { 124 | wd_learning_record: { 125 | word_id: wd_learning_record.word_id, 126 | last_l, 127 | next_l, 128 | NOI: last_NOI, 129 | EF, 130 | next_n: n, 131 | master, 132 | }, 133 | OF, 134 | } 135 | } 136 | 137 | // 计算此时与上次复习/学习的时间差(/天) 138 | let now = new Date() 139 | now.setMilliseconds(0) 140 | now.setSeconds(0) 141 | now.setMinutes(0) 142 | now.setHours(0) 143 | let last_i = Math.ceil((now.getTime() - last_l) / 86400000) 144 | // console.log('word', wd_learning_record.word_id, 'last interval', last_i) 145 | 146 | // 更改EF(由于作为键,EF规定为一位小数转换成的字符串) 147 | EF = parseFloat(EF) + (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02)) 148 | if (EF < 1.3) EF = 1.3 149 | if (EF > 2.8) EF = 2.8 150 | EF = EF.toFixed(1) 151 | 152 | // 更改矩阵对应项,这里认为若实际间隔时间超过所需间隔时间的1.5倍 153 | // 则视为极大异常值,规整为1.5倍,且不更改矩阵 154 | let used_OF = OF[EF][n - 1] 155 | if (!used_OF) used_OF = 1.2 156 | n++ 157 | if (!OF[EF][n - 1]) OF[EF][n - 1] = 1.2 158 | if (last_i <= 1.5 * last_NOI) { 159 | let old_OF = OF[EF][n - 1] 160 | let new_OF = calculateNewOF(last_i, q, used_OF, old_OF) 161 | // console.log('new_OF of', 'OF[', EF, '][', n - 1, ']:', new_OF) 162 | OF[EF][n - 1] = new_OF 163 | } else { 164 | // console.log('last_i', last_i, 'is longer than 1.5 expected interval :', last_NOI) 165 | last_i = Math.round(last_NOI * 1.5) 166 | } 167 | 168 | // 计算最优间隔时长并进行指定分布的随机分散 169 | // 同时计算下次需要复习的时间(1970.1.1至今毫秒数表示) 170 | let NOI 171 | if (q < 2) { 172 | n = 0 173 | NOI = 1 174 | } else if (q < 3) { 175 | n = 1 176 | let interval = OF[EF][0] 177 | NOI = Math.round(interval) 178 | } else { 179 | let interval = n == 1 ? 5 : OF[EF][n - 1] * last_i 180 | // 若下个最优间隔时间大于100天,则将单词标记为已掌握 181 | if (interval > 100) master = true 182 | console.log('next optimal interval', interval) 183 | NOI = Math.round(createNOI(last_i, interval)) 184 | if (NOI > 100 && !master) NOI = 100 185 | if (NOI < 0 && !master) NOI = 1 186 | } 187 | last_l = now.getTime() 188 | next_l = last_l + NOI * 86400000 189 | 190 | return { 191 | wd_learning_record: { 192 | word_id: wd_learning_record.word_id, 193 | last_l, 194 | next_l, 195 | NOI, 196 | EF, 197 | next_n: n, 198 | master, 199 | }, 200 | OF, 201 | } 202 | } 203 | 204 | module.exports = { 205 | sm_5: sm_5, 206 | } -------------------------------------------------------------------------------- /miniprogram/pages/image_cropper/image_cropper.js: -------------------------------------------------------------------------------- 1 | //获取应用实例 2 | const app = getApp() 3 | import regeneratorRuntime, { async } from '../../lib/runtime/runtime'; 4 | const userApi = require("../../utils/userApi.js") 5 | 6 | Page({ 7 | data: { 8 | src: '', 9 | width: 250, //宽度 10 | height: 250, //高度 11 | max_width: 300, 12 | max_height: 300, 13 | }, 14 | cropper: undefined, 15 | 16 | onLoad: function (options) { 17 | this.cropper = this.selectComponent("#image-cropper") 18 | this.setData({ 19 | src: app.globalData.forChangeAvatar.tempImgSrc 20 | }) 21 | }, 22 | 23 | cropperload(e) { 24 | console.log('cropper加载完成') 25 | }, 26 | 27 | loadimage(e) { 28 | wx.hideLoading() 29 | console.log('图片') 30 | this.cropper.imgReset() 31 | }, 32 | 33 | clickcut(e) { 34 | console.log(e.detail) 35 | //图片预览 36 | wx.previewImage({ 37 | current: e.detail.url, // 当前显示图片的http链接 38 | urls: [e.detail.url] // 需要预览的图片http链接列表 39 | }) 40 | }, 41 | 42 | chooseImage() { 43 | let that = this; 44 | wx.chooseImage({ 45 | count: 1, 46 | sizeType: ['compressed'], 47 | sourceType: ['album', 'camera'], 48 | success(res) { 49 | wx.showLoading({ 50 | title: '加载中', 51 | }) 52 | const tempFilePaths = res.tempFilePaths[0] 53 | app.globalData.forChangeAvatar.tempImgSrc = tempFilePaths 54 | //重置图片角度、缩放、位置 55 | that.cropper.imgReset() 56 | that.setData({ 57 | src: tempFilePaths 58 | }) 59 | } 60 | }) 61 | }, 62 | 63 | submit() { 64 | this.cropper.getImg(this.uploadAndModify) 65 | }, 66 | 67 | async uploadAndModify(obj) { 68 | wx.showLoading({ 69 | title: '头像上传中...', 70 | mask: true, 71 | }) 72 | let res1 = await userApi.uploadFile(obj.url) 73 | let file = res1.fileID 74 | if (!file) { 75 | wx.hideLoading() 76 | wx.showToast({ 77 | title: '更改失败,请重试', 78 | icon: 'none', 79 | duration: 1500, 80 | }) 81 | return 82 | } 83 | console.log('file', file) 84 | this.changeAvatar(file) 85 | }, 86 | 87 | async uploadAndModify1(obj) { 88 | console.log(obj) 89 | let fileExtName = /\.\w+$/.exec(obj.url)[0] //获取文件格式(后缀名) 90 | let _this = this 91 | wx.cloud.uploadFile({ 92 | cloudPath: 'avatar_pic/' + Date.now() + '-' + Math.floor(Math.random() * 10000) + fileExtName, //生成添加时间戳后的随机序列作为文件名 93 | filePath: obj.url, 94 | success: res1 => { 95 | let file = res1.fileID 96 | console.log('file', file) 97 | _this.changeAvatar(file) 98 | }, 99 | fail: err => { 100 | console.log(err) 101 | wx.showToast({ 102 | title: '更改失败,请重试', 103 | icon: 'none', 104 | duration: 1500, 105 | }) 106 | } 107 | }) 108 | }, 109 | 110 | async changeAvatar(file) { 111 | let data = { 112 | user_id: app.globalData.userInfo.user_id, 113 | } 114 | if (app.globalData.userInfo.wx_user == true && app.globalData.userInfo.settings.auto_update_avatar == true) { 115 | data.type = ['avatar_pic', 'settings'] 116 | data.value = [file, { auto_update_avatar: false }] 117 | } else { 118 | data.type = 'avatar_pic' 119 | data.value = file 120 | } 121 | 122 | let res2 = await userApi.changeUserInfo(data) 123 | console.log(res2) 124 | wx.hideLoading() 125 | if (res2.data == true) { 126 | app.globalData.userInfo.avatar_pic = file 127 | app.globalData.forChangeAvatar.change = true 128 | app.globalData.forChangeAvatar.imgSrc = file 129 | if (app.globalData.userInfo.wx_user == true && app.globalData.userInfo.settings.auto_update_avatar == true) { 130 | app.globalData.userInfo.settings.auto_update_avatar = false 131 | } 132 | wx.navigateBack({ 133 | delta: -1 134 | }) 135 | } else { 136 | wx.showToast({ 137 | title: '更改失败,请重试', 138 | icon: 'none', 139 | duration: 1500, 140 | }) 141 | } 142 | }, 143 | 144 | rotate() { 145 | //在用户旋转的基础上旋转90° 146 | this.cropper.setAngle(this.cropper.data.angle += 90) 147 | }, 148 | 149 | setWidth(e) { 150 | this.setData({ 151 | width: e.detail.value < 10 ? 10 : e.detail.value 152 | }) 153 | this.setData({ 154 | cut_left: this.cropper.data.cut_left 155 | }) 156 | }, 157 | 158 | setHeight(e) { 159 | this.setData({ 160 | height: e.detail.value < 10 ? 10 : e.detail.value 161 | }) 162 | this.setData({ 163 | cut_top: this.cropper.data.cut_top 164 | }) 165 | }, 166 | 167 | setCutTop(e) { 168 | this.setData({ 169 | cut_top: e.detail.value 170 | }) 171 | this.setData({ 172 | cut_top: this.cropper.data.cut_top 173 | }) 174 | }, 175 | 176 | setCutLeft(e) { 177 | this.setData({ 178 | cut_left: e.detail.value 179 | }) 180 | this.setData({ 181 | cut_left: this.cropper.data.cut_left 182 | }) 183 | }, 184 | }) -------------------------------------------------------------------------------- /miniprogram/pages/image_cropper/image_cropper.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "裁切头像", 3 | "disableScroll": true, 4 | "navigationBarBackgroundColor": "#292929", 5 | "navigationBarTextStyle": "white", 6 | "backgroundColor": "#292929", 7 | "usingComponents": { 8 | "image-cropper": "../../components/image-cropper/image-cropper" 9 | } 10 | } -------------------------------------------------------------------------------- /miniprogram/pages/image_cropper/image_cropper.less: -------------------------------------------------------------------------------- 1 | /* pages/image_cropper/image_cropper.wxss */ 2 | // .top { 3 | // position: absolute; 4 | // width: 100%; 5 | // top: 10rpx; 6 | // display: flex; 7 | // flex-flow: wrap; 8 | // z-index: 10; 9 | // color: white; 10 | // justify-content: space-around; 11 | // } 12 | 13 | .hint { 14 | position: absolute; 15 | top: 10rpx; 16 | width: 100%; 17 | font-size: 33rpx; 18 | text-align: center; 19 | color: white; 20 | z-index: 10; 21 | } 22 | 23 | .bottom { 24 | position: absolute; 25 | width: 100%; 26 | height: 100rpx; 27 | bottom: 50rpx; 28 | display: flex; 29 | z-index: 10; 30 | justify-content: space-around; 31 | align-items: center; 32 | flex-wrap: wrap; 33 | font-weight: 600; 34 | 35 | .btnText { 36 | font-size: 32rpx; 37 | color: #ffffff; 38 | width: 200rpx; 39 | height: 80rpx; 40 | line-height: 80rpx; 41 | text-align: center; 42 | } 43 | 44 | .icon-rotate { 45 | font-size: 44rpx; 46 | font-weight: 300; 47 | } 48 | 49 | .button { 50 | font-size: 36rpx; 51 | padding: 0; 52 | margin: 0; 53 | z-index: 2; 54 | width: 200rpx; 55 | height: 80rpx; 56 | text-align: center; 57 | line-height: 80rpx; 58 | // color: white; 59 | // background-color: #757575; 60 | } 61 | } -------------------------------------------------------------------------------- /miniprogram/pages/image_cropper/image_cropper.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 点击中间裁剪框可查看裁剪后的图片 10 | 11 | 更换 12 | 13 | 14 | -------------------------------------------------------------------------------- /miniprogram/pages/image_cropper/image_cropper.wxss: -------------------------------------------------------------------------------- 1 | /* pages/image_cropper/image_cropper.wxss */ 2 | .hint { 3 | position: absolute; 4 | top: 10rpx; 5 | width: 100%; 6 | font-size: 33rpx; 7 | text-align: center; 8 | color: white; 9 | z-index: 10; 10 | } 11 | .bottom { 12 | position: absolute; 13 | width: 100%; 14 | height: 100rpx; 15 | bottom: 50rpx; 16 | display: flex; 17 | z-index: 10; 18 | justify-content: space-around; 19 | align-items: center; 20 | flex-wrap: wrap; 21 | font-weight: 600; 22 | } 23 | .bottom .btnText { 24 | font-size: 32rpx; 25 | color: #ffffff; 26 | width: 200rpx; 27 | height: 80rpx; 28 | line-height: 80rpx; 29 | text-align: center; 30 | } 31 | .bottom .icon-rotate { 32 | font-size: 44rpx; 33 | font-weight: 300; 34 | } 35 | .bottom .button { 36 | font-size: 36rpx; 37 | padding: 0; 38 | margin: 0; 39 | z-index: 2; 40 | width: 200rpx; 41 | height: 80rpx; 42 | text-align: center; 43 | line-height: 80rpx; 44 | } 45 | -------------------------------------------------------------------------------- /miniprogram/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/index/index.less: -------------------------------------------------------------------------------- 1 | .bgWrapper { 2 | width: 100%; 3 | height: 100%; 4 | position: absolute; 5 | z-index: -100; 6 | background-image: linear-gradient(to bottom, #86e3ce, #FFFFFF); 7 | 8 | // -webkit-filter: blur(10px); 9 | // filter: blur(10px); 10 | .bg { 11 | margin-left: -15%; 12 | margin-top: -15%; 13 | width: 130%; 14 | height: 130%; 15 | } 16 | } 17 | 18 | .wrapper { 19 | width: 100%; 20 | height: 100%; 21 | position: absolute; 22 | z-index: -1; 23 | } 24 | 25 | .searchBtn { 26 | position: absolute; 27 | top: 35rpx; 28 | right: 45rpx; 29 | height: 60rpx; 30 | width: 60rpx; 31 | border-radius: 34rpx; 32 | // background-color: #f6f6f6; 33 | // border: solid 3rpx rgba(250, 153, 93, 0.2); 34 | // border: solid 4rpx #fa995d; 35 | // border: solid 4rpx #f6f6f6; 36 | border: solid 4rpx #70ac9e; 37 | display: flex; 38 | justify-content: center; 39 | align-items: center; 40 | background: transparent; 41 | background: rgba(144, 194, 182, 0.2); 42 | 43 | .searchIcon { 44 | // font-size: 32rpx; 45 | font-size: 40rpx; 46 | background: transparent; 47 | font-weight: 600; 48 | // color: #e0e0e0; 49 | // color: #fa995d; 50 | // color: #f6f6f6; 51 | // color: #757575; 52 | color: #70ac9e; 53 | border-radius: 50%; 54 | } 55 | } 56 | 57 | // .wasTaped { 58 | // background: rgba(112, 172, 158, 0.2); 59 | // } 60 | 61 | .swiperContainer { 62 | margin-top: 300rpx; 63 | width: 100%; 64 | height: 500rpx; 65 | 66 | .dailySentenceWrapper { 67 | width: 90%; 68 | height: 500rpx; 69 | margin-left: auto; 70 | margin-right: auto; 71 | display: flex; 72 | flex-direction: column; 73 | justify-content: center; 74 | align-items: center; 75 | 76 | .content { 77 | width: 100%; 78 | font-size: 42rpx; 79 | font-weight: 800; 80 | font-family: 'Microsoft YaHei'; 81 | text-align: center; 82 | color: #333333; 83 | // color: #ee9c6c; 84 | // color: white; 85 | margin-bottom: 20rpx; 86 | } 87 | 88 | .voice { 89 | width: 50rpx; 90 | height: 50rpx; 91 | font-size: 40rpx; 92 | line-height: 50rpx; 93 | text-align: center; 94 | color: #8a8a8a; 95 | // color: white; 96 | margin-top: -10rpx; 97 | margin-bottom: 10rpx; 98 | } 99 | 100 | .translation { 101 | width: 100%; 102 | height: 60rpx; 103 | font-size: 32rpx; 104 | font-weight: 800; 105 | font-family: 'Microsoft YaHei'; 106 | color: #4A4A4A; 107 | // color: #f5b994; 108 | // color: white; 109 | text-align: center; 110 | } 111 | } 112 | } 113 | 114 | .btnWrapper { 115 | margin-top: 100rpx; 116 | width: 100%; 117 | height: 140rpx; 118 | display: flex; 119 | flex-direction: column; 120 | justify-content: center; 121 | align-items: center; 122 | 123 | .loginBtn { 124 | width: 50%; 125 | height: 100rpx; 126 | // background-color: #007bff; 127 | // background-color: #6fb3b8; 128 | background-color: #90ced3; 129 | color: #FFFFFF; 130 | font-size: 40rpx; 131 | line-height: 100rpx; 132 | text-align: center; 133 | border-radius: 10rpx; 134 | font-weight: 800; 135 | box-shadow: 4rpx 4rpx 4rpx #e6e6e6; 136 | } 137 | } 138 | 139 | .learnBtnWrapper { 140 | position: absolute; 141 | bottom: 100rpx; 142 | width: 100%; 143 | // left: 20rpx; 144 | height: 150rpx; 145 | display: flex; 146 | justify-content: space-between; 147 | // justify-content: space-around; 148 | align-items: center; 149 | 150 | .both { 151 | width: 38%; 152 | height: 135rpx; 153 | display: flex; 154 | justify-content: center; 155 | flex-direction: column; 156 | padding-left: 40rpx; 157 | border-radius: 10rpx; 158 | box-shadow: 4rpx 4rpx 4rpx #e6e6e6; 159 | 160 | .text { 161 | font-size: 42rpx; 162 | // color: #b3ddd1; 163 | // color: white; 164 | color: #515151; 165 | font-weight: 800; 166 | // margin-bottom: 10rpx; 167 | } 168 | 169 | .number { 170 | font-size: 32rpx; 171 | // color: #b3ddd1; 172 | // color: #f6f6f6; 173 | color: #fa995d; 174 | font-weight: 700; 175 | } 176 | } 177 | 178 | .forLearn { 179 | background-image: linear-gradient(to bottom, #ffc8cb, #FFFFFF); 180 | // background-color: rgba(150, 150, 150, 0.1);c6e5fa 181 | margin-left: 35rpx; 182 | } 183 | 184 | // .wasTaped { 185 | // opacity: 0.7; 186 | // } 187 | 188 | .forReview { 189 | background-image: linear-gradient(to bottom, #e4d1fe, #FFFFFF); 190 | background-image: linear-gradient(to bottom, #87cafe, #FFFFFF); 191 | margin-right: 35rpx; 192 | } 193 | } 194 | 195 | .wasTaped { 196 | // filter: grayscale(40%); 197 | opacity: 0.7; 198 | } 199 | 200 | .mask { 201 | position: absolute; 202 | top: 0; 203 | left: 0; 204 | width: 750rpx; 205 | height: 1200rpx; 206 | // width: 100%; 207 | // height: 100%; 208 | z-index: 80; 209 | background-color: rgba(0, 0, 0, 0.7); 210 | } 211 | 212 | .changeBookWrapper { 213 | width: 750rpx; 214 | height: 600rpx; 215 | margin-top: 50rpx; 216 | position: relative; 217 | z-index: 101; 218 | 219 | .book { 220 | width: 750rpx; 221 | height: 160rpx; 222 | display: flex; 223 | justify-content: center; 224 | align-items: center; 225 | position: relative; 226 | margin-bottom: 10rpx; 227 | 228 | .bookCover { 229 | // margin-top: 30rpx; 230 | margin-left: 20rpx; 231 | margin-right: 40rpx; 232 | width: 106rpx; //遵循A4纸21*27.9的比例 233 | height: 140rpx; 234 | background-color: rgb(37, 134, 229); 235 | background-color: rgb(105, 149, 194); 236 | border-radius: 10rpx; 237 | 238 | .name { 239 | width: 28rpx; 240 | height: 100rpx; 241 | font-size: 28rpx; 242 | // line-height: 50rpx; 243 | color: #ffffff; 244 | margin-left: 10rpx; 245 | margin-top: 10rpx; 246 | font-weight: 600; 247 | } 248 | } 249 | 250 | .info { 251 | width: 70%; 252 | height: 160rpx; 253 | position: relative; 254 | font-weight: 600; 255 | 256 | .bookName { 257 | color: #757575; 258 | font-size: 28rpx; 259 | margin-top: 10rpx; 260 | font-weight: 700; 261 | } 262 | 263 | .des { 264 | margin-top: 10rpx; 265 | font-size: 22rpx; 266 | color: #8a8a8a; 267 | } 268 | 269 | .total { 270 | position: absolute; 271 | bottom: 10rpx; 272 | font-size: 22rpx; 273 | color: #8a8a8a; 274 | 275 | .num { 276 | font-size: 26rpx; 277 | } 278 | } 279 | } 280 | 281 | } 282 | 283 | .wasTaped { 284 | opacity: 1; 285 | background-color: rgba(150, 150, 150, 0.1); 286 | } 287 | } -------------------------------------------------------------------------------- /miniprogram/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | {{item.content}} 13 | 14 | 15 | {{item.translation}} 16 | 17 | 18 | 19 | 20 | 21 | 登录 22 | 23 | 24 | 25 | 26 | 学习 27 | {{needToLearn}} 28 | 29 | 30 | 复习 31 | {{needToReview}} 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | {{item.name}} 43 | 44 | 45 | {{item.name}} 46 | {{item.description}} 47 | 词汇量 {{item.total}} 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /miniprogram/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | .bgWrapper { 2 | width: 100%; 3 | height: 100%; 4 | position: absolute; 5 | z-index: -100; 6 | background-image: linear-gradient(to bottom, #86e3ce, #FFFFFF); 7 | } 8 | .bgWrapper .bg { 9 | margin-left: -15%; 10 | margin-top: -15%; 11 | width: 130%; 12 | height: 130%; 13 | } 14 | .wrapper { 15 | width: 100%; 16 | height: 100%; 17 | position: absolute; 18 | z-index: -1; 19 | } 20 | .searchBtn { 21 | position: absolute; 22 | top: 35rpx; 23 | right: 45rpx; 24 | height: 60rpx; 25 | width: 60rpx; 26 | border-radius: 34rpx; 27 | border: solid 4rpx #70ac9e; 28 | display: flex; 29 | justify-content: center; 30 | align-items: center; 31 | background: transparent; 32 | background: rgba(144, 194, 182, 0.2); 33 | } 34 | .searchBtn .searchIcon { 35 | font-size: 40rpx; 36 | background: transparent; 37 | font-weight: 600; 38 | color: #70ac9e; 39 | border-radius: 50%; 40 | } 41 | .swiperContainer { 42 | margin-top: 300rpx; 43 | width: 100%; 44 | height: 500rpx; 45 | } 46 | .swiperContainer .dailySentenceWrapper { 47 | width: 90%; 48 | height: 500rpx; 49 | margin-left: auto; 50 | margin-right: auto; 51 | display: flex; 52 | flex-direction: column; 53 | justify-content: center; 54 | align-items: center; 55 | } 56 | .swiperContainer .dailySentenceWrapper .content { 57 | width: 100%; 58 | font-size: 42rpx; 59 | font-weight: 800; 60 | font-family: 'Microsoft YaHei'; 61 | text-align: center; 62 | color: #333333; 63 | margin-bottom: 20rpx; 64 | } 65 | .swiperContainer .dailySentenceWrapper .voice { 66 | width: 50rpx; 67 | height: 50rpx; 68 | font-size: 40rpx; 69 | line-height: 50rpx; 70 | text-align: center; 71 | color: #8a8a8a; 72 | margin-top: -10rpx; 73 | margin-bottom: 10rpx; 74 | } 75 | .swiperContainer .dailySentenceWrapper .translation { 76 | width: 100%; 77 | height: 60rpx; 78 | font-size: 32rpx; 79 | font-weight: 800; 80 | font-family: 'Microsoft YaHei'; 81 | color: #4A4A4A; 82 | text-align: center; 83 | } 84 | .btnWrapper { 85 | margin-top: 100rpx; 86 | width: 100%; 87 | height: 140rpx; 88 | display: flex; 89 | flex-direction: column; 90 | justify-content: center; 91 | align-items: center; 92 | } 93 | .btnWrapper .loginBtn { 94 | width: 50%; 95 | height: 100rpx; 96 | background-color: #90ced3; 97 | color: #FFFFFF; 98 | font-size: 40rpx; 99 | line-height: 100rpx; 100 | text-align: center; 101 | border-radius: 10rpx; 102 | font-weight: 800; 103 | box-shadow: 4rpx 4rpx 4rpx #e6e6e6; 104 | } 105 | .learnBtnWrapper { 106 | position: absolute; 107 | bottom: 100rpx; 108 | width: 100%; 109 | height: 150rpx; 110 | display: flex; 111 | justify-content: space-between; 112 | align-items: center; 113 | } 114 | .learnBtnWrapper .both { 115 | width: 38%; 116 | height: 135rpx; 117 | display: flex; 118 | justify-content: center; 119 | flex-direction: column; 120 | padding-left: 40rpx; 121 | border-radius: 10rpx; 122 | box-shadow: 4rpx 4rpx 4rpx #e6e6e6; 123 | } 124 | .learnBtnWrapper .both .text { 125 | font-size: 42rpx; 126 | color: #515151; 127 | font-weight: 800; 128 | } 129 | .learnBtnWrapper .both .number { 130 | font-size: 32rpx; 131 | color: #fa995d; 132 | font-weight: 700; 133 | } 134 | .learnBtnWrapper .forLearn { 135 | background-image: linear-gradient(to bottom, #ffc8cb, #FFFFFF); 136 | margin-left: 35rpx; 137 | } 138 | .learnBtnWrapper .forReview { 139 | background-image: linear-gradient(to bottom, #e4d1fe, #FFFFFF); 140 | background-image: linear-gradient(to bottom, #87cafe, #FFFFFF); 141 | margin-right: 35rpx; 142 | } 143 | .wasTaped { 144 | opacity: 0.7; 145 | } 146 | .mask { 147 | position: absolute; 148 | top: 0; 149 | left: 0; 150 | width: 750rpx; 151 | height: 1200rpx; 152 | z-index: 80; 153 | background-color: rgba(0, 0, 0, 0.7); 154 | } 155 | .changeBookWrapper { 156 | width: 750rpx; 157 | height: 600rpx; 158 | margin-top: 50rpx; 159 | position: relative; 160 | z-index: 101; 161 | } 162 | .changeBookWrapper .book { 163 | width: 750rpx; 164 | height: 160rpx; 165 | display: flex; 166 | justify-content: center; 167 | align-items: center; 168 | position: relative; 169 | margin-bottom: 10rpx; 170 | } 171 | .changeBookWrapper .book .bookCover { 172 | margin-left: 20rpx; 173 | margin-right: 40rpx; 174 | width: 106rpx; 175 | height: 140rpx; 176 | background-color: #2586e5; 177 | background-color: #6995c2; 178 | border-radius: 10rpx; 179 | } 180 | .changeBookWrapper .book .bookCover .name { 181 | width: 28rpx; 182 | height: 100rpx; 183 | font-size: 28rpx; 184 | color: #ffffff; 185 | margin-left: 10rpx; 186 | margin-top: 10rpx; 187 | font-weight: 600; 188 | } 189 | .changeBookWrapper .book .info { 190 | width: 70%; 191 | height: 160rpx; 192 | position: relative; 193 | font-weight: 600; 194 | } 195 | .changeBookWrapper .book .info .bookName { 196 | color: #757575; 197 | font-size: 28rpx; 198 | margin-top: 10rpx; 199 | font-weight: 700; 200 | } 201 | .changeBookWrapper .book .info .des { 202 | margin-top: 10rpx; 203 | font-size: 22rpx; 204 | color: #8a8a8a; 205 | } 206 | .changeBookWrapper .book .info .total { 207 | position: absolute; 208 | bottom: 10rpx; 209 | font-size: 22rpx; 210 | color: #8a8a8a; 211 | } 212 | .changeBookWrapper .book .info .total .num { 213 | font-size: 26rpx; 214 | } 215 | .changeBookWrapper .wasTaped { 216 | opacity: 1; 217 | background-color: rgba(150, 150, 150, 0.1); 218 | } 219 | -------------------------------------------------------------------------------- /miniprogram/pages/learning/learning.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "mpProgress": "../../components/mp-progress/mp-progress" 4 | } 5 | } -------------------------------------------------------------------------------- /miniprogram/pages/learning/learning.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{learnedNum}} / {{learnNum}} 7 | 8 | 9 | 10 | {{wordDetail.word}} 11 | 12 | 14 | 16 | 18 | 20 | 21 | / {{wordDetail.phonetic}} / 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | {{wrongTransWordList[item].translation.pos}} 39 | {{wrongTransWordList[item].translation.meaning}} 40 | 41 | 42 | 43 | 44 | {{item.pos}} 45 | {{item.meaning}} 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 66 | 答案 67 | 68 | 69 | 70 | 71 | 认识 72 | 73 | 74 | 75 | 不认识 76 | 77 | 78 | 79 | 80 | 81 | 下一个 82 | 83 | 84 | 85 | 记错了 86 | 87 | 88 | 89 | 91 | 下一个 92 | 93 | 94 | 95 | 99 | 100 | 102 | 104 | 106 | 107 | 108 | 109 | 110 | 本组单词学习已完成 111 | 完成学习 112 | 继续学习 113 | 114 | 115 | -------------------------------------------------------------------------------- /miniprogram/pages/login/login.js: -------------------------------------------------------------------------------- 1 | //login.js 2 | const app = getApp() 3 | import regeneratorRuntime, { async } from '../../lib/runtime/runtime.js'; 4 | const { formatTime } = require('../../utils/format_time.js') 5 | const error_message = [ 6 | '', 7 | '请完成填写再重试', 8 | '账号或密码错误', 9 | '该账号已被注册', 10 | '两次输入密码不同', 11 | '用户名仅能包含数字、中英文和下划线', 12 | '用户名不能以下划线开头或结尾', 13 | '密码仅能包含数字、英文字母和下划线', 14 | '密码不能以下划线开头或结尾', 15 | ] 16 | const userApi = require("../../utils/userApi.js") 17 | const rescontent = require('../../utils/response_content.js') 18 | 19 | Page({ 20 | data: { 21 | isregister: false, 22 | errmsg: error_message, 23 | errtype: 0, 24 | }, 25 | user: { 26 | username: '', 27 | pwd: '', 28 | confirm_pwd: '', 29 | }, 30 | // isUsernameChecked: false, 31 | 32 | onLoad(options) { 33 | wx.setNavigationBarColor({ 34 | backgroundColor: '#d0e6a5', 35 | frontColor: '#ffffff', 36 | }) 37 | 38 | wx.setNavigationBarTitle({ 39 | title: '登录', 40 | }) 41 | }, 42 | 43 | //处理input内容变化时的时间 44 | handleInput(e) { 45 | let inputtype = e.target.dataset.inputtype 46 | let value = e.detail.value 47 | this.user[inputtype] = value 48 | // if (inputtype == "username") { 49 | // if (this.data.isregister) { 50 | // this.isUsernameChecked = false 51 | // } 52 | // } 53 | // console.log(inputtype, this.user[inputtype]) 54 | }, 55 | 56 | async checkUsername() { 57 | let username = this.user.username 58 | if (username == '') { return false } 59 | if(!(this.checkUsernameVaild())) return false 60 | // console.log('check whether', username, 'have been registered') 61 | let res = await userApi.checkUsernameInDB({ username }) 62 | // console.log('checkUsername', res) 63 | if (!res.errorcode) { return false } 64 | if (res.data.isFind) { 65 | this.setErrType(3) 66 | return false 67 | } 68 | // this.isUsernameChecked = true 69 | return true 70 | }, 71 | 72 | checkUsernameVaild(register = true) { 73 | // 用户名合法性判断,只能包含字母、数字、中文、下划线且不能以下划线开头或结尾 74 | // let exp1 = /^(?!_)(?!.*?_$)[a-zA-Z0-9_\u4e00-\u9fa5]+$/ 75 | let username = this.user.username 76 | let exp1 = /^[a-zA-Z0-9_\u4e00-\u9fa5]+$/ 77 | let exp2 = /^(?!_)(?!.*?_$).+$/ 78 | if (!exp1.test(username)) { 79 | this.setErrType(register ? 5 : 2) 80 | return false 81 | } 82 | if (!exp2.test(username)) { 83 | this.setErrType(register ? 6 : 2) 84 | return false 85 | } 86 | return true 87 | }, 88 | 89 | checkPwd(register = true) { 90 | // 密码合法性判断,只能包含字母、数字、下划线且不能以下划线开头或结尾 91 | let pwd = this.user.pwd 92 | let exp1 = /^[a-zA-Z0-9_]+$/ 93 | let exp2 = /^(?!_)(?!.*?_$).+$/ 94 | if (!exp1.test(pwd)) { 95 | this.setErrType(register ? 7 : 2) 96 | return false 97 | } 98 | if (!exp2.test(pwd)) { 99 | this.setErrType(register ? 8 : 2) 100 | return false 101 | } 102 | return true 103 | }, 104 | 105 | checkTwoPwd() { 106 | let pwd = this.user.pwd 107 | let confirm_pwd = this.user.confirm_pwd 108 | if (pwd != confirm_pwd) { 109 | this.setErrType(4) 110 | return false 111 | } 112 | return true 113 | }, 114 | 115 | checkEmptyField() { 116 | if (this.user.username != '' && this.user.pwd != '') { 117 | if (this.data.isregister) { 118 | if (this.user.confirm_pwd != '') { 119 | return true 120 | } 121 | } else { 122 | return true 123 | } 124 | } 125 | this.setErrType(1) 126 | return false 127 | }, 128 | 129 | changeType(e) { 130 | this.setData({ 131 | isregister: !(this.data.isregister), 132 | errtype: 0, 133 | }) 134 | this.user = { 135 | username: '', 136 | pwd: '', 137 | confirm_pwd: '', 138 | } 139 | // this.isUsernameChecked = false 140 | }, 141 | 142 | setErrType(errtype) { 143 | let _this = this 144 | this.setData({ errtype }) 145 | clearTimeout(this.timer) 146 | this.timer = setTimeout(() => { 147 | _this.setData({ errtype: 0 }) 148 | }, 1500) 149 | }, 150 | 151 | async login() { 152 | if (!(this.checkEmptyField()) || !(this.checkUsernameVaild(false)) || !(this.checkPwd(false))) { 153 | return 154 | } 155 | console.log('try to login') 156 | let username = this.user.username 157 | let pwd = this.user.pwd 158 | let res = await userApi.login({ username, pwd }) 159 | console.log(res) 160 | this.afterLogin(res) 161 | }, 162 | 163 | async register() { 164 | if (!(this.checkEmptyField()) || !(this.checkPwd()) || !(this.checkTwoPwd())) { 165 | return 166 | } 167 | // if (!(this.isUsernameChecked)) { 168 | let usernameOk = await this.checkUsername() 169 | if (!usernameOk) { return } 170 | // this.isUsernameChecked = true 171 | // } 172 | console.log('try to register') 173 | // return 174 | let username = this.user.username 175 | let pwd = this.user.pwd 176 | let res = await userApi.register({ username, pwd }) 177 | // console.log(res) 178 | this.afterLogin(res) 179 | }, 180 | 181 | async wxLogin() { 182 | console.log('login/register using wechat userinfo') 183 | let res = await userApi.getWxUserInfo() 184 | if (!res.userInfo) { return } 185 | let username = res.userInfo.nickName 186 | let avatar_pic = res.userInfo.avatarUrl 187 | let res1 = await userApi.wxLogin({ username, avatar_pic }) 188 | console.log(res1) 189 | this.afterLogin(res1) 190 | }, 191 | 192 | afterLogin(res) { 193 | let duration = 1000 194 | if (res.errorcode == rescontent.LOGINERR.errorcode) { 195 | this.setErrType(2) 196 | return 197 | } else if (res.errorcode == rescontent.REGISTEROK.errorcode) { 198 | wx.showToast({ 199 | title: `注册成功`, 200 | icon: 'none', 201 | duration: duration, 202 | }) 203 | } else if (res.errorcode == rescontent.LOGINOK.errorcode) { 204 | let lastlogin = formatTime(res.data.last_login) 205 | wx.showToast({ 206 | title: `登录成功,上次登录时间 ${lastlogin}`, 207 | icon: 'none', 208 | duration: duration, 209 | }) 210 | } else { 211 | wx.showToast({ 212 | title: '服务出错,请重试', 213 | icon: 'none', 214 | duration: duration 215 | }) 216 | return 217 | } 218 | 219 | setTimeout(function () { 220 | app.globalData.isLogin = true 221 | app.globalData.userInfo = res.data 222 | app.globalData.updatedForIndex = true 223 | app.globalData.updatedForOverview = true 224 | let storageContent = { 225 | time: new Date().getTime(), 226 | info: res.data, 227 | } 228 | wx.setStorageSync('userInfo', storageContent) 229 | wx.navigateBack({ 230 | delta: 1, 231 | complete: (res) => { console.log('navigate back complete', res) }, 232 | }) 233 | }, duration) 234 | }, 235 | 236 | 237 | }) -------------------------------------------------------------------------------- /miniprogram/pages/login/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/login/login.less: -------------------------------------------------------------------------------- 1 | .bgWrapper { 2 | width: 100%; 3 | height: 100%; 4 | position: absolute; 5 | z-index: -100; 6 | background-image: linear-gradient(to bottom, #d0e6a5, #FFFFFF); 7 | // -webkit-filter: blur(10px); 8 | // filter: blur(10px); 9 | 10 | .bg { 11 | margin-left: -15%; 12 | margin-top: -15%; 13 | width: 130%; 14 | height: 130%; 15 | } 16 | } 17 | 18 | .wrapper { 19 | width: 80%; 20 | height: 500rpx; 21 | margin-left: auto; 22 | margin-right: auto; 23 | margin-top: 300rpx; 24 | margin-bottom: auto; 25 | 26 | .title { 27 | margin-top: 30rpx; 28 | width: 100%; 29 | font-size: 56rpx; 30 | text-align: center; 31 | font-weight: 800; 32 | color: #819c4b; 33 | } 34 | 35 | .inputField { 36 | height: 70rpx; 37 | font-size: 34rpx; 38 | background-color: rgba(255, 255, 255, 0.7); 39 | padding-left: 20rpx; 40 | border: solid 4rpx #e2e2e2; 41 | } 42 | 43 | .username { 44 | margin-top: 40rpx; 45 | border-top-left-radius: 10rpx; 46 | border-top-right-radius: 10rpx; 47 | border-bottom: 2rpx solid #e2e2e2; 48 | } 49 | 50 | .middlePwd { 51 | border-top: 2rpx solid #e2e2e2; 52 | border-bottom: 2rpx solid #e2e2e2; 53 | margin-top: -2rpx; 54 | } 55 | 56 | .pwd { 57 | border-bottom-left-radius: 10rpx; 58 | border-bottom-right-radius: 10rpx; 59 | border-top: 2rpx solid #e2e2e2; 60 | margin-top: -2rpx; 61 | } 62 | 63 | .btnWrapper { 64 | margin-top: 10rpx; 65 | width: 100%; 66 | height: 40rpx; 67 | 68 | .changeBtn { 69 | width: 60rpx; 70 | height: 40rpx; 71 | font-size: 28rpx; 72 | line-height: 40rpx; 73 | color: #819c4b; 74 | font-weight: 500; 75 | } 76 | 77 | .loginBtn { 78 | margin-left: 10rpx; 79 | } 80 | 81 | .registerBtn { 82 | margin-right: 10rpx; 83 | } 84 | } 85 | 86 | .registerBtnWrapper { 87 | display: flex; 88 | justify-content: flex-end; 89 | } 90 | 91 | .errmsg { 92 | height: 50rpx; 93 | width: 100%; 94 | text-align: center; 95 | line-height: 50rpx; 96 | font-size: 30rpx; 97 | color: rgb(247, 98, 96); 98 | } 99 | 100 | .submit { 101 | margin-top: 0rpx; 102 | width: 100%; 103 | color: white; 104 | // background-color: #007bff; 105 | background-color: #b9ce8e; 106 | } 107 | } 108 | 109 | .wxLoginWrapper { 110 | position: absolute; 111 | bottom: 150rpx; 112 | width: 100%; 113 | height: 150rpx; 114 | margin-left: 0; 115 | margin-right: 0; 116 | 117 | .loginBtn { 118 | width: 100rpx; 119 | height: 100rpx; 120 | border-radius: 50%; 121 | margin-left: auto; 122 | margin-right: auto; 123 | background-color: rgb(42, 174, 103); 124 | display: flex; 125 | align-items: center; 126 | justify-content: center; 127 | 128 | .logo { 129 | width: 85%; 130 | height: 85%; 131 | border-radius: 50%; 132 | } 133 | } 134 | 135 | .wxLoginTip { 136 | margin-top: 20rpx; 137 | width: 100%; 138 | height: 24rpx; 139 | font-size: 22rpx; 140 | color: rgba(0, 0, 0, 0.3); 141 | text-align: center; 142 | } 143 | } 144 | 145 | .wasTaped { 146 | filter: grayscale(30%); 147 | } 148 | 149 | .avatarPicTest { 150 | width: 100rpx; 151 | height: 100rpx; 152 | margin-top: 50rpx; 153 | margin-right: auto; 154 | margin-left: auto; 155 | border-radius: 50rpx; 156 | 157 | .pic { 158 | width: 100rpx; 159 | height: 100rpx; 160 | border-radius: 50rpx; 161 | } 162 | } -------------------------------------------------------------------------------- /miniprogram/pages/login/login.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 | 9 | 注册 10 | 11 | {{errmsg[errtype]}} 12 | 13 | 14 | 15 | 16 | 注册 17 | 18 | 19 | 20 | 21 | 登录 22 | 23 | {{errmsg[errtype]}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 微信登录无需注册哦~ 32 | -------------------------------------------------------------------------------- /miniprogram/pages/login/login.wxss: -------------------------------------------------------------------------------- 1 | .bgWrapper { 2 | width: 100%; 3 | height: 100%; 4 | position: absolute; 5 | z-index: -100; 6 | background-image: linear-gradient(to bottom, #d0e6a5, #FFFFFF); 7 | } 8 | .bgWrapper .bg { 9 | margin-left: -15%; 10 | margin-top: -15%; 11 | width: 130%; 12 | height: 130%; 13 | } 14 | .wrapper { 15 | width: 80%; 16 | height: 500rpx; 17 | margin-left: auto; 18 | margin-right: auto; 19 | margin-top: 300rpx; 20 | margin-bottom: auto; 21 | } 22 | .wrapper .title { 23 | margin-top: 30rpx; 24 | width: 100%; 25 | font-size: 56rpx; 26 | text-align: center; 27 | font-weight: 800; 28 | color: #819c4b; 29 | } 30 | .wrapper .inputField { 31 | height: 70rpx; 32 | font-size: 34rpx; 33 | background-color: rgba(255, 255, 255, 0.7); 34 | padding-left: 20rpx; 35 | border: solid 4rpx #e2e2e2; 36 | } 37 | .wrapper .username { 38 | margin-top: 40rpx; 39 | border-top-left-radius: 10rpx; 40 | border-top-right-radius: 10rpx; 41 | border-bottom: 2rpx solid #e2e2e2; 42 | } 43 | .wrapper .middlePwd { 44 | border-top: 2rpx solid #e2e2e2; 45 | border-bottom: 2rpx solid #e2e2e2; 46 | margin-top: -2rpx; 47 | } 48 | .wrapper .pwd { 49 | border-bottom-left-radius: 10rpx; 50 | border-bottom-right-radius: 10rpx; 51 | border-top: 2rpx solid #e2e2e2; 52 | margin-top: -2rpx; 53 | } 54 | .wrapper .btnWrapper { 55 | margin-top: 10rpx; 56 | width: 100%; 57 | height: 40rpx; 58 | } 59 | .wrapper .btnWrapper .changeBtn { 60 | width: 60rpx; 61 | height: 40rpx; 62 | font-size: 28rpx; 63 | line-height: 40rpx; 64 | color: #819c4b; 65 | font-weight: 500; 66 | } 67 | .wrapper .btnWrapper .loginBtn { 68 | margin-left: 10rpx; 69 | } 70 | .wrapper .btnWrapper .registerBtn { 71 | margin-right: 10rpx; 72 | } 73 | .wrapper .registerBtnWrapper { 74 | display: flex; 75 | justify-content: flex-end; 76 | } 77 | .wrapper .errmsg { 78 | height: 50rpx; 79 | width: 100%; 80 | text-align: center; 81 | line-height: 50rpx; 82 | font-size: 30rpx; 83 | color: #f76260; 84 | } 85 | .wrapper .submit { 86 | margin-top: 0rpx; 87 | width: 100%; 88 | color: white; 89 | background-color: #b9ce8e; 90 | } 91 | .wxLoginWrapper { 92 | position: absolute; 93 | bottom: 150rpx; 94 | width: 100%; 95 | height: 150rpx; 96 | margin-left: 0; 97 | margin-right: 0; 98 | } 99 | .wxLoginWrapper .loginBtn { 100 | width: 100rpx; 101 | height: 100rpx; 102 | border-radius: 50%; 103 | margin-left: auto; 104 | margin-right: auto; 105 | background-color: #2aae67; 106 | display: flex; 107 | align-items: center; 108 | justify-content: center; 109 | } 110 | .wxLoginWrapper .loginBtn .logo { 111 | width: 85%; 112 | height: 85%; 113 | border-radius: 50%; 114 | } 115 | .wxLoginWrapper .wxLoginTip { 116 | margin-top: 20rpx; 117 | width: 100%; 118 | height: 24rpx; 119 | font-size: 22rpx; 120 | color: rgba(0, 0, 0, 0.3); 121 | text-align: center; 122 | } 123 | .wasTaped { 124 | filter: grayscale(30%); 125 | } 126 | .avatarPicTest { 127 | width: 100rpx; 128 | height: 100rpx; 129 | margin-top: 50rpx; 130 | margin-right: auto; 131 | margin-left: auto; 132 | border-radius: 50rpx; 133 | } 134 | .avatarPicTest .pic { 135 | width: 100rpx; 136 | height: 100rpx; 137 | border-radius: 50rpx; 138 | } 139 | -------------------------------------------------------------------------------- /miniprogram/pages/overview/overview.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "mpProgress": "../../components/mp-progress/mp-progress", 4 | "ec-canvas": "../../components/ec-canvas/ec-canvas" 5 | } 6 | } -------------------------------------------------------------------------------- /miniprogram/pages/review/review.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "mpProgress": "../../components/mp-progress/mp-progress" 4 | } 5 | } -------------------------------------------------------------------------------- /miniprogram/pages/review/review.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{reviewedNum}} / {{reviewNum}} 7 | 8 | 9 | 10 | {{wordDetail.word}} 11 | 12 | 14 | 16 | 18 | 19 | / {{wordDetail.phonetic}} / 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | {{wrongTransWordList[item].translation.pos}} 37 | {{wrongTransWordList[item].translation.meaning}} 38 | 39 | 40 | 41 | 42 | {{item.pos}} 43 | {{item.meaning}} 45 | 46 | 47 | 48 | 49 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 64 | 答案 65 | 66 | 67 | 68 | 70 | 认识 71 | 72 | 73 | 75 | 模糊 76 | 77 | 78 | 80 | 不认识 81 | 82 | 83 | 84 | 85 | 86 | 下一个 87 | 88 | 89 | 91 | 记错了 92 | 93 | 94 | 95 | 97 | 下一个 98 | 99 | 100 | 101 | 102 | 104 | 106 | 108 | 109 | 110 | 111 | 112 | 本组单词复习已完成 113 | 114 | 115 | 单词 116 | 下次学习时间 117 | 118 | 119 | 120 | {{item.word}} 121 | {{item.NOI}}天后 122 | 已掌握 123 | 上传失败 124 | 125 | 126 | 127 | 128 | 完成复习 129 | 继续复习 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /miniprogram/pages/search/search.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/search/search.less: -------------------------------------------------------------------------------- 1 | .searchWrapper { 2 | width: 100%; 3 | height: 100rpx; 4 | position: fixed; 5 | z-index: 8; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | background-color: #f6f6f6; 10 | 11 | .searchIcon { 12 | position: absolute; 13 | top: 30rpx; 14 | left: 40rpx; 15 | font-size: 40rpx; 16 | font-weight: 500; 17 | color: #808080; 18 | z-index: 10; 19 | } 20 | 21 | .search { 22 | width: 85%; 23 | height: 70rpx; 24 | font-size: 32rpx; 25 | line-height: 60rpx; 26 | padding-left: 70rpx; 27 | // border: 4rpx solid #e2e2e2; 28 | border: 2rpx solid #e2e2e2; 29 | border-radius: 15rpx; 30 | background-color: white; 31 | color: #333333; 32 | font-family: Arial; 33 | } 34 | 35 | .placeHolder { 36 | font-size: 32rpx; 37 | line-height: 60rpx; 38 | // padding-left: 70rpx; 39 | // color: #333333; 40 | // font-family: 'Microsoft YaHei', Arial; 41 | } 42 | } 43 | 44 | .cancelWrapper { 45 | position: fixed; 46 | top: 0rpx; 47 | right: 8rpx; 48 | width: 100rpx; 49 | height: 100rpx; 50 | z-index: 9999; 51 | display: flex; 52 | align-items: center; 53 | justify-content: center; 54 | 55 | .cancel { 56 | width: 36rpx; 57 | height: 36rpx; 58 | // text-align: center; 59 | // font-size: 36rpx; 60 | // line-height: 36rpx; 61 | // color: #757575; 62 | background-color: #d6d6d6; 63 | border-radius: 18rpx; 64 | display: flex; 65 | align-items: center; 66 | justify-content: center; 67 | 68 | .cancelIcon { 69 | font-size: 24rpx; 70 | color: white; 71 | } 72 | } 73 | } 74 | 75 | .resultWrapper { 76 | margin-top: 100rpx; 77 | width: 100%; 78 | display: flex; 79 | flex-direction: column; 80 | align-items: center; 81 | // margin-bottom: 10rpx; 82 | 83 | .result { 84 | width: 670rpx; // 750-40*2 85 | height: 80rpx; 86 | line-height: 80rpx; 87 | font-size: 32rpx; 88 | padding-left: 40rpx; 89 | padding-right: 40rpx; 90 | display: -webkit-box; 91 | overflow: hidden; 92 | -webkit-box-orient: vertical; 93 | -webkit-line-clamp: 1; 94 | 95 | .word { 96 | color: #333333; 97 | } 98 | 99 | .desc { 100 | color: #333333; 101 | } 102 | 103 | .trans { 104 | color: #757575; 105 | font-size: 28rpx; 106 | } 107 | } 108 | 109 | .wasTaped { 110 | background-color: #e6e6e6; 111 | } 112 | } 113 | 114 | .resultTips { 115 | width: 100%; 116 | height: 60rpx; 117 | margin-bottom: 130rpx; 118 | display: flex; 119 | align-items: center; 120 | justify-content: center; 121 | 122 | .text { 123 | font-size: 28rpx; 124 | color: #757575; 125 | } 126 | } 127 | 128 | .historyWrapper { 129 | margin-top: 100rpx; 130 | width: 100%; 131 | display: flex; 132 | flex-direction: column; 133 | align-items: center; 134 | margin-bottom: 140rpx; 135 | 136 | .history { 137 | width: 670rpx; // 750-40*2 138 | height: 80rpx; 139 | line-height: 80rpx; 140 | font-size: 32rpx; 141 | padding-left: 40rpx; 142 | padding-right: 40rpx; 143 | position: relative; 144 | 145 | .wordInfo { 146 | width: 630rpx; 147 | display: -webkit-box; 148 | overflow: hidden; 149 | -webkit-box-orient: vertical; 150 | -webkit-line-clamp: 1; 151 | 152 | .word { 153 | color: #333333; 154 | } 155 | 156 | .trans { 157 | color: #757575; 158 | font-size: 28rpx; 159 | } 160 | 161 | } 162 | 163 | .delete { 164 | position: absolute; 165 | top: 0rpx; 166 | right: 40rpx; 167 | width: 30rpx; 168 | height: 80rpx; 169 | z-index: 5; 170 | // border-radius: 18rpx; 171 | display: flex; 172 | align-items: center; 173 | justify-content: center; 174 | 175 | .deleteIcon { 176 | font-size: 30rpx; 177 | color: #515151; 178 | } 179 | } 180 | } 181 | 182 | .clearAll { 183 | margin-top: 20rpx; 184 | margin-left: auto; 185 | margin-right: auto; 186 | width: 200rpx; 187 | font-size: 28rpx; 188 | color: #808080; 189 | } 190 | 191 | .wasTaped { 192 | background-color: #e6e6e6; 193 | } 194 | } 195 | 196 | .changeBigDB { 197 | position: fixed; 198 | width: 100%; 199 | height: 80rpx; 200 | padding-bottom: 40rpx; 201 | bottom: 0rpx; 202 | display: flex; 203 | flex-direction: column; 204 | align-items: center; 205 | justify-content: center; 206 | background-color: #f6f6f6; 207 | z-index: 10; 208 | 209 | .text { 210 | font-size: 24rpx; 211 | line-height: 30rpx; 212 | color: #a0a0a0; 213 | 214 | .changeBtn { 215 | color: #515151; 216 | } 217 | } 218 | } -------------------------------------------------------------------------------- /miniprogram/pages/search/search.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | {{item.word}} 18 | 的{{item.exchange}} 19 |     {{item.translation}} 20 | 21 | 23 | {{item.word}} 24 |     {{item.translation}} 25 | 26 | 27 | 28 | 没有更多结果了哦 29 | 30 | 31 | 32 | 34 | 35 | {{item.word}} 36 |     {{item.translation}} 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 清除全部历史 45 | 46 | 47 | 48 | 49 | 当前在使用小词库,速度较快,能满足大部分需求 50 | 可切换大词库获得更多的搜索结果 51 | 当前在使用大词库,包含本应用所有词汇,速度较慢 52 | 可切换小词库获得更快的搜索速度 53 | -------------------------------------------------------------------------------- /miniprogram/pages/search/search.wxss: -------------------------------------------------------------------------------- 1 | .searchWrapper { 2 | width: 100%; 3 | height: 100rpx; 4 | position: fixed; 5 | z-index: 8; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | background-color: #f6f6f6; 10 | } 11 | .searchWrapper .searchIcon { 12 | position: absolute; 13 | top: 30rpx; 14 | left: 40rpx; 15 | font-size: 40rpx; 16 | font-weight: 500; 17 | color: #808080; 18 | z-index: 10; 19 | } 20 | .searchWrapper .search { 21 | width: 85%; 22 | height: 70rpx; 23 | font-size: 32rpx; 24 | line-height: 60rpx; 25 | padding-left: 70rpx; 26 | border: 2rpx solid #e2e2e2; 27 | border-radius: 15rpx; 28 | background-color: white; 29 | color: #333333; 30 | font-family: Arial; 31 | } 32 | .searchWrapper .placeHolder { 33 | font-size: 32rpx; 34 | line-height: 60rpx; 35 | } 36 | .cancelWrapper { 37 | position: fixed; 38 | top: 0rpx; 39 | right: 8rpx; 40 | width: 100rpx; 41 | height: 100rpx; 42 | z-index: 9999; 43 | display: flex; 44 | align-items: center; 45 | justify-content: center; 46 | } 47 | .cancelWrapper .cancel { 48 | width: 36rpx; 49 | height: 36rpx; 50 | background-color: #d6d6d6; 51 | border-radius: 18rpx; 52 | display: flex; 53 | align-items: center; 54 | justify-content: center; 55 | } 56 | .cancelWrapper .cancel .cancelIcon { 57 | font-size: 24rpx; 58 | color: white; 59 | } 60 | .resultWrapper { 61 | margin-top: 100rpx; 62 | width: 100%; 63 | display: flex; 64 | flex-direction: column; 65 | align-items: center; 66 | } 67 | .resultWrapper .result { 68 | width: 670rpx; 69 | height: 80rpx; 70 | line-height: 80rpx; 71 | font-size: 32rpx; 72 | padding-left: 40rpx; 73 | padding-right: 40rpx; 74 | display: -webkit-box; 75 | overflow: hidden; 76 | -webkit-box-orient: vertical; 77 | -webkit-line-clamp: 1; 78 | } 79 | .resultWrapper .result .word { 80 | color: #333333; 81 | } 82 | .resultWrapper .result .desc { 83 | color: #333333; 84 | } 85 | .resultWrapper .result .trans { 86 | color: #757575; 87 | font-size: 28rpx; 88 | } 89 | .resultWrapper .wasTaped { 90 | background-color: #e6e6e6; 91 | } 92 | .resultTips { 93 | width: 100%; 94 | height: 60rpx; 95 | margin-bottom: 130rpx; 96 | display: flex; 97 | align-items: center; 98 | justify-content: center; 99 | } 100 | .resultTips .text { 101 | font-size: 28rpx; 102 | color: #757575; 103 | } 104 | .historyWrapper { 105 | margin-top: 100rpx; 106 | width: 100%; 107 | display: flex; 108 | flex-direction: column; 109 | align-items: center; 110 | margin-bottom: 140rpx; 111 | } 112 | .historyWrapper .history { 113 | width: 670rpx; 114 | height: 80rpx; 115 | line-height: 80rpx; 116 | font-size: 32rpx; 117 | padding-left: 40rpx; 118 | padding-right: 40rpx; 119 | position: relative; 120 | } 121 | .historyWrapper .history .wordInfo { 122 | width: 630rpx; 123 | display: -webkit-box; 124 | overflow: hidden; 125 | -webkit-box-orient: vertical; 126 | -webkit-line-clamp: 1; 127 | } 128 | .historyWrapper .history .wordInfo .word { 129 | color: #333333; 130 | } 131 | .historyWrapper .history .wordInfo .trans { 132 | color: #757575; 133 | font-size: 28rpx; 134 | } 135 | .historyWrapper .history .delete { 136 | position: absolute; 137 | top: 0rpx; 138 | right: 40rpx; 139 | width: 30rpx; 140 | height: 80rpx; 141 | z-index: 5; 142 | display: flex; 143 | align-items: center; 144 | justify-content: center; 145 | } 146 | .historyWrapper .history .delete .deleteIcon { 147 | font-size: 30rpx; 148 | color: #515151; 149 | } 150 | .historyWrapper .clearAll { 151 | margin-top: 20rpx; 152 | margin-left: auto; 153 | margin-right: auto; 154 | width: 200rpx; 155 | font-size: 28rpx; 156 | color: #808080; 157 | } 158 | .historyWrapper .wasTaped { 159 | background-color: #e6e6e6; 160 | } 161 | .changeBigDB { 162 | position: fixed; 163 | width: 100%; 164 | height: 80rpx; 165 | padding-bottom: 40rpx; 166 | bottom: 0rpx; 167 | display: flex; 168 | flex-direction: column; 169 | align-items: center; 170 | justify-content: center; 171 | background-color: #f6f6f6; 172 | z-index: 10; 173 | } 174 | .changeBigDB .text { 175 | font-size: 24rpx; 176 | line-height: 30rpx; 177 | color: #a0a0a0; 178 | } 179 | .changeBigDB .text .changeBtn { 180 | color: #515151; 181 | } 182 | -------------------------------------------------------------------------------- /miniprogram/pages/user/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/user/user.less: -------------------------------------------------------------------------------- 1 | .header { 2 | width: 100%; 3 | height: 500rpx; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | position: relative; 9 | 10 | .background { 11 | position: absolute; 12 | top: 0; 13 | left: 0; 14 | width: 100%; 15 | height: 100%; 16 | filter: blur(60rpx); 17 | z-index: -1; 18 | opacity: 0.6; 19 | 20 | .bgImg { 21 | width: 100%; 22 | height: 100%; 23 | } 24 | } 25 | 26 | .loginBtn { 27 | width: 300rpx; 28 | height: 80rpx; 29 | background-color: #90ced3; 30 | color: #FFFFFF; 31 | font-size: 36rpx; 32 | line-height: 80rpx; 33 | text-align: center; 34 | border-radius: 10rpx; 35 | font-weight: 800; 36 | // box-shadow: 4rpx 4rpx 4rpx #e6e6e6; 37 | } 38 | 39 | .wasTaped { 40 | opacity: 0.7; 41 | } 42 | 43 | .avatar { 44 | width: 160rpx; 45 | height: 160rpx; 46 | border-radius: 50%; 47 | border: solid 4rpx #e6e6e6; 48 | margin-top: 20rpx; 49 | } 50 | 51 | .username { 52 | margin-top: 30rpx; 53 | color: #ffffff; 54 | font-size: 44rpx; 55 | text-shadow: 2rpx 2rpx 2rpx #bfbfbf; 56 | } 57 | } 58 | 59 | .optionList { 60 | width: 100%; 61 | // height: 600rpx; 62 | background-color: #ffffff; 63 | 64 | .option { 65 | width: 670rpx; // 750-40*2 66 | height: 100rpx; 67 | padding: 0 40rpx; 68 | background-color: #ffffff; 69 | // background-color: #f6f6f6; 70 | // border-bottom: solid 4rpx #e6e6e6; 71 | display: flex; 72 | align-items: center; 73 | justify-content: center; 74 | position: relative; 75 | font-weight: 600; 76 | 77 | .optionIcon { 78 | width: 40rpx; 79 | height: 40rpx; 80 | color: #bfbfbf; 81 | color: #8a8a8a; 82 | // color: #fd6802; 83 | // font-weight: 600; 84 | font-size: 40rpx; 85 | } 86 | 87 | .optionName { 88 | width: 600rpx; // 670-40-30 89 | margin-left: 30rpx; 90 | color: #515151; 91 | font-weight: 600; 92 | font-size: 32rpx; 93 | } 94 | 95 | .more { 96 | position: absolute; 97 | right: 40rpx; 98 | top: 30rpx; 99 | width: 40rpx; 100 | height: 40rpx; 101 | color: #bfbfbf; 102 | color: #8a8a8a; 103 | // font-weight: 600; 104 | font-size: 40rpx; 105 | 106 | } 107 | } 108 | 109 | .split { 110 | width: 100%; 111 | height: 20rpx; 112 | background-color: #f6f6f6; 113 | } 114 | 115 | .wasTaped { 116 | background-color: #e6e6e6; 117 | } 118 | } 119 | 120 | .logoutBtn { 121 | width: 250rpx; 122 | height: 80rpx; 123 | line-height: 80rpx; 124 | text-align: center; 125 | margin-top: 50rpx; 126 | margin-left: auto; 127 | margin-right: auto; 128 | color: #8a8a8a; 129 | color: #bfbfbf; 130 | font-weight: 600; 131 | background-color: #f6f6f6; 132 | border-radius: 10rpx; 133 | } 134 | 135 | .wasTaped { 136 | background-color: rgba(150, 150, 150, 0.1); 137 | } 138 | 139 | .customizeWrapper { 140 | width: 750rpx; 141 | height: 500rpx; 142 | margin-top: 50rpx; 143 | position: relative; 144 | z-index: 101; 145 | 146 | .customValue { 147 | width: 600rpx; 148 | height: 80rpx; 149 | margin-top: 20rpx; 150 | margin-left: auto; 151 | margin-right: auto; 152 | padding: 0 30rpx; 153 | border: 2rpx solid #e2e2e2; 154 | border-radius: 10rpx; 155 | font-size: 36rpx; 156 | color: #515151; 157 | } 158 | 159 | .placeHolder { 160 | font-size: 30rpx; 161 | } 162 | 163 | .errMsg{ 164 | margin-top: 20rpx; 165 | width: 600rpx; 166 | height: 60rpx; 167 | margin-left: auto; 168 | margin-right: auto; 169 | text-align: center; 170 | line-height: 60rpx; 171 | font-size: 30rpx; 172 | color: rgb(247, 98, 96); 173 | } 174 | 175 | .btn { 176 | width: 300rpx; 177 | height: 80rpx; 178 | margin-top: 20rpx; 179 | margin-left: auto; 180 | margin-right: auto; 181 | font-size: 36rpx; 182 | font-weight: 600; 183 | line-height: 80rpx; 184 | text-align: center; 185 | color: #ffffff; 186 | background-color: #90ced3; 187 | // background-color: #fd6802; 188 | border-radius: 10rpx; 189 | } 190 | 191 | .wasTaped { 192 | opacity: 0.7; 193 | } 194 | } -------------------------------------------------------------------------------- /miniprogram/pages/user/user.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 登录 8 | 9 | 10 | {{userInfo.username}} 11 | 12 | 13 | 14 | 15 | 16 | 更改头像 17 | 18 | 19 | 20 | 更改昵称 21 | 22 | 23 | 24 | 修改密码 25 | 26 | 27 | 28 | 29 | 更多设置 30 | 31 | 32 | 33 | 退出登录 34 | 35 | 37 | 38 | 41 | 44 | 47 | {{errMsg}} 48 | 确认 49 | 50 | -------------------------------------------------------------------------------- /miniprogram/pages/user/user.wxss: -------------------------------------------------------------------------------- 1 | .header { 2 | width: 100%; 3 | height: 500rpx; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | position: relative; 9 | } 10 | .header .background { 11 | position: absolute; 12 | top: 0; 13 | left: 0; 14 | width: 100%; 15 | height: 100%; 16 | filter: blur(60rpx); 17 | z-index: -1; 18 | opacity: 0.6; 19 | } 20 | .header .background .bgImg { 21 | width: 100%; 22 | height: 100%; 23 | } 24 | .header .loginBtn { 25 | width: 300rpx; 26 | height: 80rpx; 27 | background-color: #90ced3; 28 | color: #FFFFFF; 29 | font-size: 36rpx; 30 | line-height: 80rpx; 31 | text-align: center; 32 | border-radius: 10rpx; 33 | font-weight: 800; 34 | } 35 | .header .wasTaped { 36 | opacity: 0.7; 37 | } 38 | .header .avatar { 39 | width: 160rpx; 40 | height: 160rpx; 41 | border-radius: 50%; 42 | border: solid 4rpx #e6e6e6; 43 | margin-top: 20rpx; 44 | } 45 | .header .username { 46 | margin-top: 30rpx; 47 | color: #ffffff; 48 | font-size: 44rpx; 49 | text-shadow: 2rpx 2rpx 2rpx #bfbfbf; 50 | } 51 | .optionList { 52 | width: 100%; 53 | background-color: #ffffff; 54 | } 55 | .optionList .option { 56 | width: 670rpx; 57 | height: 100rpx; 58 | padding: 0 40rpx; 59 | background-color: #ffffff; 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | position: relative; 64 | font-weight: 600; 65 | } 66 | .optionList .option .optionIcon { 67 | width: 40rpx; 68 | height: 40rpx; 69 | color: #bfbfbf; 70 | color: #8a8a8a; 71 | font-size: 40rpx; 72 | } 73 | .optionList .option .optionName { 74 | width: 600rpx; 75 | margin-left: 30rpx; 76 | color: #515151; 77 | font-weight: 600; 78 | font-size: 32rpx; 79 | } 80 | .optionList .option .more { 81 | position: absolute; 82 | right: 40rpx; 83 | top: 30rpx; 84 | width: 40rpx; 85 | height: 40rpx; 86 | color: #bfbfbf; 87 | color: #8a8a8a; 88 | font-size: 40rpx; 89 | } 90 | .optionList .split { 91 | width: 100%; 92 | height: 20rpx; 93 | background-color: #f6f6f6; 94 | } 95 | .optionList .wasTaped { 96 | background-color: #e6e6e6; 97 | } 98 | .logoutBtn { 99 | width: 250rpx; 100 | height: 80rpx; 101 | line-height: 80rpx; 102 | text-align: center; 103 | margin-top: 50rpx; 104 | margin-left: auto; 105 | margin-right: auto; 106 | color: #8a8a8a; 107 | color: #bfbfbf; 108 | font-weight: 600; 109 | background-color: #f6f6f6; 110 | border-radius: 10rpx; 111 | } 112 | .wasTaped { 113 | background-color: rgba(150, 150, 150, 0.1); 114 | } 115 | .customizeWrapper { 116 | width: 750rpx; 117 | height: 500rpx; 118 | margin-top: 50rpx; 119 | position: relative; 120 | z-index: 101; 121 | } 122 | .customizeWrapper .customValue { 123 | width: 600rpx; 124 | height: 80rpx; 125 | margin-top: 20rpx; 126 | margin-left: auto; 127 | margin-right: auto; 128 | padding: 0 30rpx; 129 | border: 2rpx solid #e2e2e2; 130 | border-radius: 10rpx; 131 | font-size: 36rpx; 132 | color: #515151; 133 | } 134 | .customizeWrapper .placeHolder { 135 | font-size: 30rpx; 136 | } 137 | .customizeWrapper .errMsg { 138 | margin-top: 20rpx; 139 | width: 600rpx; 140 | height: 60rpx; 141 | margin-left: auto; 142 | margin-right: auto; 143 | text-align: center; 144 | line-height: 60rpx; 145 | font-size: 30rpx; 146 | color: #f76260; 147 | } 148 | .customizeWrapper .btn { 149 | width: 300rpx; 150 | height: 80rpx; 151 | margin-top: 20rpx; 152 | margin-left: auto; 153 | margin-right: auto; 154 | font-size: 36rpx; 155 | font-weight: 600; 156 | line-height: 80rpx; 157 | text-align: center; 158 | color: #ffffff; 159 | background-color: #90ced3; 160 | border-radius: 10rpx; 161 | } 162 | .customizeWrapper .wasTaped { 163 | opacity: 0.7; 164 | } 165 | -------------------------------------------------------------------------------- /miniprogram/pages/user_settings/user_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/user_settings/user_settings.less: -------------------------------------------------------------------------------- 1 | .optionList { 2 | width: 100%; 3 | margin-bottom: 100rpx; 4 | 5 | .option { 6 | width: 100%; 7 | height: 100rpx; 8 | display: flex; 9 | align-items: center; 10 | justify-content: space-between; 11 | background-color: #ffffff; 12 | 13 | .optionName { 14 | font-size: 32rpx; 15 | font-weight: 600; 16 | color: #515151; 17 | margin-left: 40rpx; 18 | } 19 | 20 | .optionValue { 21 | font-size: 28rpx; 22 | font-weight: 600; 23 | color: #757575; 24 | margin-right: 40rpx; 25 | 26 | .switch { 27 | zoom: 0.9; 28 | margin-right: -10rpx; 29 | } 30 | } 31 | } 32 | 33 | .wasTaped { 34 | background-color: rgba(150, 150, 150, 0.1); 35 | } 36 | 37 | .split { 38 | width: 100%; 39 | height: 20rpx; 40 | background-color: #f6f6f6; 41 | } 42 | } 43 | 44 | .mask { 45 | position: absolute; 46 | width: 100%; 47 | height: 100%; 48 | z-index: 100; 49 | background-color: rgba(0, 0, 0, 0.7); 50 | } 51 | 52 | .customizeWrapper { 53 | width: 750rpx; 54 | height: 500rpx; 55 | margin-top: 50rpx; 56 | position: relative; 57 | z-index: 101; 58 | 59 | .customValue { 60 | width: 600rpx; 61 | height: 80rpx; 62 | margin-top: 20rpx; 63 | margin-left: auto; 64 | margin-right: auto; 65 | padding: 0 30rpx; 66 | border: 2rpx solid #e2e2e2; 67 | border-radius: 10rpx; 68 | font-size: 36rpx; 69 | color: #515151; 70 | } 71 | 72 | .placeHolder{ 73 | font-size: 30rpx; 74 | } 75 | 76 | .btn { 77 | width: 300rpx; 78 | height: 80rpx; 79 | margin-top: 100rpx; 80 | margin-left: auto; 81 | margin-right: auto; 82 | font-size: 36rpx; 83 | font-weight: 600; 84 | line-height: 80rpx; 85 | text-align: center; 86 | color: #ffffff; 87 | background-color: #90ced3; 88 | // background-color: #fd6802; 89 | border-radius: 10rpx; 90 | } 91 | 92 | .wasTaped { 93 | opacity: 0.7; 94 | } 95 | } 96 | 97 | .resetbtn { 98 | margin-top: 100rpx; 99 | margin-left: auto; 100 | margin-right: auto; 101 | } -------------------------------------------------------------------------------- /miniprogram/pages/user_settings/user_settings.wxss: -------------------------------------------------------------------------------- 1 | .optionList { 2 | width: 100%; 3 | margin-bottom: 100rpx; 4 | } 5 | .optionList .option { 6 | width: 100%; 7 | height: 100rpx; 8 | display: flex; 9 | align-items: center; 10 | justify-content: space-between; 11 | background-color: #ffffff; 12 | } 13 | .optionList .option .optionName { 14 | font-size: 32rpx; 15 | font-weight: 600; 16 | color: #515151; 17 | margin-left: 40rpx; 18 | } 19 | .optionList .option .optionValue { 20 | font-size: 28rpx; 21 | font-weight: 600; 22 | color: #757575; 23 | margin-right: 40rpx; 24 | } 25 | .optionList .option .optionValue .switch { 26 | zoom: 0.9; 27 | margin-right: -10rpx; 28 | } 29 | .optionList .wasTaped { 30 | background-color: rgba(150, 150, 150, 0.1); 31 | } 32 | .optionList .split { 33 | width: 100%; 34 | height: 20rpx; 35 | background-color: #f6f6f6; 36 | } 37 | .mask { 38 | position: absolute; 39 | width: 100%; 40 | height: 100%; 41 | z-index: 100; 42 | background-color: rgba(0, 0, 0, 0.7); 43 | } 44 | .customizeWrapper { 45 | width: 750rpx; 46 | height: 500rpx; 47 | margin-top: 50rpx; 48 | position: relative; 49 | z-index: 101; 50 | } 51 | .customizeWrapper .customValue { 52 | width: 600rpx; 53 | height: 80rpx; 54 | margin-top: 20rpx; 55 | margin-left: auto; 56 | margin-right: auto; 57 | padding: 0 30rpx; 58 | border: 2rpx solid #e2e2e2; 59 | border-radius: 10rpx; 60 | font-size: 36rpx; 61 | color: #515151; 62 | } 63 | .customizeWrapper .placeHolder { 64 | font-size: 30rpx; 65 | } 66 | .customizeWrapper .btn { 67 | width: 300rpx; 68 | height: 80rpx; 69 | margin-top: 100rpx; 70 | margin-left: auto; 71 | margin-right: auto; 72 | font-size: 36rpx; 73 | font-weight: 600; 74 | line-height: 80rpx; 75 | text-align: center; 76 | color: #ffffff; 77 | background-color: #90ced3; 78 | border-radius: 10rpx; 79 | } 80 | .customizeWrapper .wasTaped { 81 | opacity: 0.7; 82 | } 83 | .resetbtn { 84 | margin-top: 100rpx; 85 | margin-left: auto; 86 | margin-right: auto; 87 | } 88 | -------------------------------------------------------------------------------- /miniprogram/pages/word_detail/word_detail.js: -------------------------------------------------------------------------------- 1 | // pages/word_detail/word_detail.js 2 | import regeneratorRuntime, { async } from '../../lib/runtime/runtime'; 3 | const wordApi = require("../../utils/wordApi.js") 4 | const word_utils = require("../../utils/word_utils.js") 5 | 6 | const app = getApp() 7 | const colorList = ['#ffb284', '#99c4d3', '#d0e6a5', '#86e3ce', '#ffdd95', '#fa897b', 8 | '#ccabd8', '#80beaf', '#b3ddd1', '#d1dce2', '#ef9d6d', '#c6c09c', '#f5cec7', 9 | '#ffc98b', '#b598c6', '#73c8dd', '#c56a4b'] 10 | const innerAudioContext = wx.createInnerAudioContext({ useWebAudioImplement: true }) 11 | 12 | Page({ 13 | 14 | /** 15 | * 页面的初始数据 16 | */ 17 | data: { 18 | // bgStyle: '#ffb284', 19 | // bgStyle: '#d1dce0', 20 | colorType: 16, 21 | word_id: 0, 22 | wordDetail: {}, 23 | voiceUrl: '', 24 | isInNotebook: false, 25 | }, 26 | 27 | /** 28 | * 生命周期函数--监听页面加载 29 | */ 30 | onLoad: function (options) { 31 | wx.setNavigationBarTitle({ 32 | title: '单词详情', 33 | }) 34 | 35 | let colorType = Math.floor(Math.random() * 17) 36 | if (options.colorType) { 37 | colorType = options.colorType 38 | } 39 | wx.setNavigationBarColor({ 40 | backgroundColor: colorList[colorType], 41 | frontColor: '#ffffff', 42 | }) 43 | this.setData({ colorType }) 44 | 45 | console.log(options) 46 | // let pages = getCurrentPages() 47 | // let thisPage = pages[pages.length-1] 48 | // let pagesOptions = thisPage.options 49 | // console.log(pagesOptions) 50 | let word_id = parseInt(options.word_id) 51 | this.getDetail(word_id) 52 | }, 53 | 54 | async getDetail(word_id) { 55 | let user_id = -1 56 | let isLogin = app.globalData.isLogin 57 | if (isLogin) user_id = app.globalData.userInfo.user_id 58 | let res = await wordApi.getWordDetail({ 59 | word_id, 60 | user_id, 61 | }) 62 | let wordDetail = JSON.parse(JSON.stringify(res.data)) 63 | console.log(wordDetail) 64 | wordDetail = word_utils.handleWordDetail(wordDetail) 65 | console.log(wordDetail) 66 | this.setData({ 67 | wordDetail, 68 | isLogin, 69 | isInNotebook: wordDetail.in_notebook, 70 | }) 71 | let voiceUrl = word_utils.getWordVoiceUrl(wordDetail.word) 72 | innerAudioContext.src = voiceUrl 73 | }, 74 | 75 | playVoice() { 76 | innerAudioContext.stop() 77 | innerAudioContext.play() 78 | }, 79 | 80 | // 调整是否添加到生词本 81 | toggleAddToNB: async function () { 82 | let add = this.data.isInNotebook 83 | let res = await wordApi.toggleAddToNB({ 84 | user_id: app.globalData.userInfo.user_id, 85 | word_id: this.data.wordDetail.word_id, 86 | add: !add, 87 | }) 88 | console.log(res) 89 | if (res.data) { 90 | this.setData({ 91 | isInNotebook: !add, 92 | }) 93 | } else { 94 | wx.showToast({ 95 | title: '操作出错,请重试', 96 | icon: 'none', 97 | duration: 1000, 98 | }) 99 | } 100 | }, 101 | 102 | /** 103 | * 生命周期函数--监听页面初次渲染完成 104 | */ 105 | onReady: function () { 106 | 107 | }, 108 | 109 | /** 110 | * 生命周期函数--监听页面显示 111 | */ 112 | onShow: function () { 113 | 114 | }, 115 | 116 | /** 117 | * 生命周期函数--监听页面隐藏 118 | */ 119 | onHide: function () { 120 | 121 | }, 122 | 123 | /** 124 | * 生命周期函数--监听页面卸载 125 | */ 126 | onUnload: function () { 127 | if (this.data.wordDetail.in_notebook != this.data.isInNotebook) app.globalData.updatedForOverview = true 128 | }, 129 | 130 | /** 131 | * 页面相关事件处理函数--监听用户下拉动作 132 | */ 133 | onPullDownRefresh: function () { 134 | 135 | }, 136 | 137 | /** 138 | * 页面上拉触底事件的处理函数 139 | */ 140 | onReachBottom: function () { 141 | 142 | }, 143 | 144 | /** 145 | * 用户点击右上角分享 146 | */ 147 | onShareAppMessage: function () { 148 | 149 | } 150 | }) -------------------------------------------------------------------------------- /miniprogram/pages/word_detail/word_detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/word_detail/word_detail.less: -------------------------------------------------------------------------------- 1 | .bgWrapper { 2 | width: 100%; 3 | height: 100%; 4 | position: fixed; 5 | z-index: -100; 6 | // background-image: linear-gradient(to bottom, #99c4d3, #FFFFFF); 7 | } 8 | 9 | .word { 10 | margin-top: 70rpx; 11 | margin-left: 40rpx; 12 | height: 80rpx; 13 | font-size: 64rpx; 14 | // font-family: 'Microsoft YaHei', 'Times New Roman', Times, serif; 15 | font-weight: 700; 16 | line-height: 70rpx; 17 | position: relative; 18 | 19 | .notebookBtn { 20 | position: absolute; 21 | top: 0; 22 | right: 10rpx; 23 | width: 80rpx; 24 | height: 80rpx; 25 | font-size: 46rpx; 26 | color: #f0f0f0; 27 | line-height: 80rpx; 28 | text-align: center; 29 | } 30 | 31 | .icon-addToNB-yes { 32 | color: #fb6a00; 33 | } 34 | 35 | .wasTaped-bottom { 36 | color: #ffffff; 37 | } 38 | 39 | .wasTaped-bottom1 { 40 | filter: grayscale(20%); 41 | } 42 | } 43 | 44 | .pron { 45 | margin-top: 20rpx; 46 | margin-left: 45rpx; 47 | height: 40rpx; 48 | line-height: 40rpx; 49 | font-size: 30rpx; 50 | // font-weight: 600; 51 | font-family: Arial, Helvetica, sans-serif; 52 | // color: #f6f6f6; 53 | color: #ffffff; 54 | } 55 | 56 | .tagContainer { 57 | width: 670rpx; 58 | margin-top: 10rpx; 59 | margin-bottom: 40rpx; 60 | margin-left: auto; 61 | margin-right: auto; 62 | display: flex; 63 | flex-wrap: wrap; 64 | 65 | .tag { 66 | margin-right: 10rpx; 67 | height: 40rpx; 68 | line-height: 40rpx; 69 | font-size: 26rpx; 70 | border-radius: 20rpx; 71 | background-color: rgba(0, 0, 0, 0.2); 72 | color: #e6e6e6; 73 | padding: 0rpx 20rpx; 74 | margin-top: 10rpx; 75 | } 76 | } 77 | 78 | .contentCard { 79 | width: 95%; 80 | margin-top: 20rpx; 81 | margin-left: auto; 82 | margin-right: auto; 83 | background-color: rgba(255, 255, 255, 0.6); 84 | border-radius: 20rpx; 85 | box-shadow: 2rpx 2rpx 10rpx rgba(0, 0, 0, 0.1); 86 | 87 | .title { 88 | width: 655rpx; 89 | margin-top: 26rpx; 90 | margin-left: auto; 91 | margin-right: auto; 92 | font-size: 36rpx; 93 | line-height: 60rpx; 94 | font-weight: 700; 95 | } 96 | 97 | .contentWrapper { 98 | width: 650rpx; 99 | margin-top: 14rpx; 100 | margin-left: auto; 101 | margin-right: auto; 102 | padding-bottom: 50rpx; 103 | 104 | .content { 105 | font-size: 32rpx; 106 | // font-family: Arial, Helvetica, sans-serif; 107 | color: #757575; 108 | // color: #515151; 109 | margin-bottom: 10rpx; 110 | // font-weight: 600; 111 | line-height: 40rpx; 112 | 113 | .exchangeName { 114 | display: inline-block; 115 | width: 220rpx; 116 | } 117 | 118 | .exchangeWord { 119 | display: inline-block; 120 | } 121 | 122 | .pos { 123 | font-size: 28rpx; 124 | color: #a0a0a0; 125 | margin-right: 10rpx; 126 | } 127 | } 128 | } 129 | } 130 | 131 | .last { 132 | margin-bottom: 50rpx; 133 | } -------------------------------------------------------------------------------- /miniprogram/pages/word_detail/word_detail.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{wordDetail.word}} 8 | 10 | 11 | 12 | / {{wordDetail.phonetic}} / 14 | 15 | {{item}} 16 | 17 | 18 | 19 | 20 | 英文释义 21 | 22 | 23 | 24 | {{item.pos}} 25 | {{item.meaning}} 26 | 27 | 28 | 29 | 30 | 31 | 中文释义 32 | 33 | 34 | {{item.pos}} 35 | {{item.meaning}} 36 | 37 | 38 | 39 | 40 | 41 | 词形变换 42 | 43 | 44 | 46 | {{item.name}} 47 | {{item.word}} 48 | {{item.word}} 的{{item.name}} 49 | 50 | 51 | -------------------------------------------------------------------------------- /miniprogram/pages/word_detail/word_detail.wxss: -------------------------------------------------------------------------------- 1 | .bgWrapper { 2 | width: 100%; 3 | height: 100%; 4 | position: fixed; 5 | z-index: -100; 6 | } 7 | .word { 8 | margin-top: 70rpx; 9 | margin-left: 40rpx; 10 | height: 80rpx; 11 | font-size: 64rpx; 12 | font-weight: 700; 13 | line-height: 70rpx; 14 | position: relative; 15 | } 16 | .word .notebookBtn { 17 | position: absolute; 18 | top: 0; 19 | right: 10rpx; 20 | width: 80rpx; 21 | height: 80rpx; 22 | font-size: 46rpx; 23 | color: #f0f0f0; 24 | line-height: 80rpx; 25 | text-align: center; 26 | } 27 | .word .icon-addToNB-yes { 28 | color: #fb6a00; 29 | } 30 | .word .wasTaped-bottom { 31 | color: #ffffff; 32 | } 33 | .word .wasTaped-bottom1 { 34 | filter: grayscale(20%); 35 | } 36 | .pron { 37 | margin-top: 20rpx; 38 | margin-left: 45rpx; 39 | height: 40rpx; 40 | line-height: 40rpx; 41 | font-size: 30rpx; 42 | font-family: Arial, Helvetica, sans-serif; 43 | color: #ffffff; 44 | } 45 | .tagContainer { 46 | width: 670rpx; 47 | margin-top: 10rpx; 48 | margin-bottom: 40rpx; 49 | margin-left: auto; 50 | margin-right: auto; 51 | display: flex; 52 | flex-wrap: wrap; 53 | } 54 | .tagContainer .tag { 55 | margin-right: 10rpx; 56 | height: 40rpx; 57 | line-height: 40rpx; 58 | font-size: 26rpx; 59 | border-radius: 20rpx; 60 | background-color: rgba(0, 0, 0, 0.2); 61 | color: #e6e6e6; 62 | padding: 0rpx 20rpx; 63 | margin-top: 10rpx; 64 | } 65 | .contentCard { 66 | width: 95%; 67 | margin-top: 20rpx; 68 | margin-left: auto; 69 | margin-right: auto; 70 | background-color: rgba(255, 255, 255, 0.6); 71 | border-radius: 20rpx; 72 | box-shadow: 2rpx 2rpx 10rpx rgba(0, 0, 0, 0.1); 73 | } 74 | .contentCard .title { 75 | width: 655rpx; 76 | margin-top: 26rpx; 77 | margin-left: auto; 78 | margin-right: auto; 79 | font-size: 36rpx; 80 | line-height: 60rpx; 81 | font-weight: 700; 82 | } 83 | .contentCard .contentWrapper { 84 | width: 650rpx; 85 | margin-top: 14rpx; 86 | margin-left: auto; 87 | margin-right: auto; 88 | padding-bottom: 50rpx; 89 | } 90 | .contentCard .contentWrapper .content { 91 | font-size: 32rpx; 92 | color: #757575; 93 | margin-bottom: 10rpx; 94 | line-height: 40rpx; 95 | } 96 | .contentCard .contentWrapper .content .exchangeName { 97 | display: inline-block; 98 | width: 220rpx; 99 | } 100 | .contentCard .contentWrapper .content .exchangeWord { 101 | display: inline-block; 102 | } 103 | .contentCard .contentWrapper .content .pos { 104 | font-size: 28rpx; 105 | color: #a0a0a0; 106 | margin-right: 10rpx; 107 | } 108 | .last { 109 | margin-bottom: 50rpx; 110 | } 111 | -------------------------------------------------------------------------------- /miniprogram/pages/word_list/word_list.js: -------------------------------------------------------------------------------- 1 | // pages/word_list/word_list.js 2 | import regeneratorRuntime, { async } from '../../lib/runtime/runtime'; 3 | const wordApi = require("../../utils/wordApi.js") 4 | const word_utils = require("../../utils/word_utils.js") 5 | const color = require("../../utils/color.js") 6 | 7 | const app = getApp() 8 | 9 | let typeParameter = { 10 | getBkLearnedWord: { navTitle: '本书已学', user_id: true, wd_bk_id: true }, 11 | getBkMasteredWord: { navTitle: '本书已掌握', user_id: true, wd_bk_id: true }, 12 | getBkUnlearnedWord: { navTitle: '本书未学', user_id: true, wd_bk_id: true }, 13 | getBkWord: { navTitle: '本书全部单词', user_id: false, wd_bk_id: true }, 14 | getLearnedWord: { navTitle: '已学单词', user_id: true, wd_bk_id: false }, 15 | getMasteredWord: { navTitle: '已掌握单词', user_id: true, wd_bk_id: false }, 16 | getReviewWord: { navTitle: '复习中单词', user_id: true, wd_bk_id: false }, 17 | getNoteBookWord: { navTitle: '收藏夹', user_id: true, wd_bk_id: false }, 18 | today: { navTitle: '今日学习&复习', user_id: true, wd_bk_id: false }, 19 | } 20 | 21 | Page({ 22 | 23 | /** 24 | * 页面的初始数据 25 | */ 26 | data: { 27 | wordList: [], 28 | hasMore: true, 29 | learnHasMore: true, 30 | reviewHasMore: true, 31 | isToday: false, 32 | todayLearn: undefined, 33 | todayReview: undefined, 34 | todayType: -1, 35 | }, 36 | skip: 0, 37 | learnSkip: undefined, 38 | reviewSkip: undefined, 39 | type: '', 40 | 41 | /** 42 | * 生命周期函数--监听页面加载 43 | */ 44 | onLoad: function (options) { 45 | let type = this.options.type 46 | console.log('type', type) 47 | 48 | wx.setNavigationBarTitle({ 49 | title: typeParameter[type].navTitle, 50 | }) 51 | this.type = type 52 | 53 | if (type != 'today') { 54 | this.getData() 55 | } else { 56 | this.setData({ 57 | todayType: 0 58 | }) 59 | this.getTodayWord(0) 60 | this.getTodayWord(1) 61 | } 62 | }, 63 | 64 | async getData() { 65 | let type = this.type 66 | if (!this.data.hasMore) return 67 | wx.showLoading({ 68 | title: '加载中...', 69 | }) 70 | let parameters = {} 71 | if (typeParameter[type].user_id) parameters.user_id = app.globalData.userInfo.user_id 72 | if (typeParameter[type].wd_bk_id) parameters.wd_bk_id = app.globalData.userInfo.l_book_id 73 | parameters.skip = this.skip 74 | let wordList = this.data.wordList 75 | console.log('parameters', parameters) 76 | let res = await wordApi[type](parameters) 77 | 78 | console.log('res', res) 79 | 80 | for (let i = 0; i < res.data.length; i++) { 81 | if (res.data[i].translation.indexOf('\n') != -1) { 82 | res.data[i].translation = res.data[i].translation.substring(0, res.data[i].translation.indexOf('\n')) 83 | } 84 | // console.log('rect length of:', directres[i], word_utils.getResObjRectLength(directres[i])) 85 | } 86 | 87 | wordList = wordList.concat(res.data) 88 | this.skip = wordList.length 89 | let hasMore = true 90 | if (res.data.length < 20) hasMore = false 91 | 92 | this.setData({ 93 | wordList, 94 | hasMore 95 | }) 96 | wx.hideLoading() 97 | }, 98 | 99 | async getTodayWord(todayType) { 100 | if (todayType === undefined) todayType = this.data.todayType 101 | let hasMoreType = ['learnHasMore', 'reviewHasMore'] 102 | if (!this.data[hasMoreType[todayType]]) return 103 | wx.showLoading({ 104 | title: '加载中...', 105 | }) 106 | let apiNameType = ['getTodayLearnWord', 'getTodayReviewWord'] 107 | let skipType = ['getTodayLearnWord', 'getTodayReviewWord'] 108 | let wordListType = ['todayLearn', 'todayReview'] 109 | let type = apiNameType[todayType] 110 | let parameters = {} 111 | parameters.user_id = app.globalData.userInfo.user_id 112 | if (this[skipType[todayType]] === undefined) this[skipType[todayType]] = 0 113 | parameters.skip = this[skipType[todayType]] 114 | if (this.data[wordListType[todayType]] === undefined) this.data[wordListType[todayType]] = [] 115 | let wordList = this.data[wordListType[todayType]] 116 | 117 | console.log('parameters', parameters) 118 | let res = await wordApi[type](parameters) 119 | 120 | console.log('res', res) 121 | 122 | for (let i = 0; i < res.data.length; i++) { 123 | if (res.data[i].translation.indexOf('\n') != -1) { 124 | res.data[i].translation = res.data[i].translation.substring(0, res.data[i].translation.indexOf('\n')) 125 | } 126 | // console.log('rect length of:', directres[i], word_utils.getResObjRectLength(directres[i])) 127 | } 128 | 129 | wordList = wordList.concat(res.data) 130 | this[skipType[todayType]] = wordList.length 131 | let hasMore = true 132 | if (res.data.length < 20) hasMore = false 133 | 134 | let updateData = {} 135 | updateData[wordListType[todayType]] = wordList 136 | updateData[hasMoreType[todayType]] = hasMore 137 | 138 | this.setData(updateData) 139 | wx.hideLoading() 140 | }, 141 | 142 | getWordDetail(e) { 143 | let wordListName = 'wordList' 144 | if (this.data.todayType != -1) { 145 | let wordListType = ['todayLearn', 'todayReview'] 146 | wordListName = wordListType[this.data.todayType] 147 | } 148 | let index = e.currentTarget.dataset.index 149 | let word_id = this.data[wordListName][index].word_id 150 | wx.navigateTo({ 151 | url: `../word_detail/word_detail?word_id=${word_id}`, 152 | }) 153 | }, 154 | 155 | changeType() { 156 | this.setData({ 157 | todayType: (this.data.todayType + 1) % 2 158 | }) 159 | }, 160 | 161 | /** 162 | * 页面上拉触底事件的处理函数 163 | */ 164 | onReachBottom: function () { 165 | console.log('onReachBottom') 166 | if (this.data.todayType == -1) { 167 | this.getData() 168 | } else { 169 | this.getTodayWord() 170 | } 171 | }, 172 | 173 | /** 174 | * 生命周期函数--监听页面初次渲染完成 175 | */ 176 | onReady: function () { 177 | 178 | }, 179 | 180 | /** 181 | * 生命周期函数--监听页面显示 182 | */ 183 | onShow: function () { 184 | 185 | }, 186 | 187 | /** 188 | * 生命周期函数--监听页面隐藏 189 | */ 190 | onHide: function () { 191 | 192 | }, 193 | 194 | /** 195 | * 生命周期函数--监听页面卸载 196 | */ 197 | onUnload: function () { 198 | 199 | }, 200 | 201 | /** 202 | * 页面相关事件处理函数--监听用户下拉动作 203 | */ 204 | onPullDownRefresh: function () { 205 | 206 | }, 207 | 208 | /** 209 | * 用户点击右上角分享 210 | */ 211 | onShareAppMessage: function () { 212 | 213 | } 214 | }) -------------------------------------------------------------------------------- /miniprogram/pages/word_list/word_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/word_list/word_list.less: -------------------------------------------------------------------------------- 1 | @wordItemHeight: 80rpx; 2 | 3 | .wordWrapper { 4 | // margin-top: 10rpx; 5 | width: 100%; 6 | // margin-bottom: 10rpx; 7 | // margin-bottom: 130rpx; 8 | // background-color: #ffffff; 9 | 10 | .wordItem { 11 | // width: 670rpx; 12 | width: 750rpx; 13 | height: wordItemHeight; 14 | // margin-left: auto; 15 | // margin-right: auto; 16 | background-color: #ffffff; 17 | // margin-bottom: 6rpx; 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | font-weight: 600; 22 | 23 | .dot { 24 | width: 16rpx; 25 | height: 16rpx; 26 | border-radius: 8rpx; 27 | background-color: #fd6802; 28 | // margin-left: 40rpx; 29 | margin-right: 24rpx; 30 | } 31 | 32 | .wordInfo { 33 | width: 630rpx; // 750-40*2-20-20 34 | height: @wordItemHeight; 35 | line-height: @wordItemHeight; 36 | font-size: 32rpx; 37 | // padding-left: 40rpx; 38 | // padding-right: 40rpx; 39 | display: -webkit-box; 40 | overflow: hidden; 41 | -webkit-box-orient: vertical; 42 | -webkit-line-clamp: 1; 43 | // border-bottom: solid 4rpx #f6f6f6; 44 | 45 | .word { 46 | // color: #333333; 47 | color: #757575; 48 | } 49 | 50 | .trans { 51 | // color: #757575; 52 | color: #8a8a8a; 53 | font-size: 28rpx; 54 | } 55 | } 56 | } 57 | 58 | .wasTaped { 59 | background-color: #e6e6e6; 60 | } 61 | 62 | .tips { 63 | width: 100%; 64 | height: 100rpx; 65 | color: #8a8a8a; 66 | font-size: 28rpx; 67 | display: flex; 68 | align-items: center; 69 | justify-content: center; 70 | } 71 | 72 | .changeType { 73 | width: 100%; 74 | height: 100rpx; 75 | font-size: 32rpx; 76 | display: flex; 77 | background-color: #ffffff; 78 | position: fixed; 79 | 80 | .type { 81 | width: 50%; 82 | height: 100rpx; 83 | display: flex; 84 | flex-direction: column; 85 | align-items: center; 86 | justify-content: center; 87 | 88 | .text { 89 | font-size: 26rpx; 90 | color: #8a8a8a; 91 | font-weight: 600; 92 | } 93 | 94 | .decorate { 95 | margin-top: 10rpx; 96 | width: 30rpx; 97 | height: 10rpx; 98 | border-radius: 5rpx; 99 | background-color: #fd6802; 100 | } 101 | 102 | .active { 103 | font-size: 30rpx; 104 | color: #515151; 105 | margin-top: 10rpx; 106 | } 107 | } 108 | } 109 | 110 | .forToday { 111 | width: 100%; 112 | margin-top: 100rpx; 113 | } 114 | 115 | .bottom { 116 | width: 100%; 117 | height: 100rpx; 118 | } 119 | } -------------------------------------------------------------------------------- /miniprogram/pages/word_list/word_list.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | {{item.word}} 9 |     {{item.translation}} 10 | 11 | 12 | 没有更多了哦~ 13 | ~ 14 | 15 | 16 | 17 | 今日学习 18 | 19 | 20 | 21 | 今日复习 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | {{item.word}} 31 |     {{item.translation}} 32 | 33 | 34 | 35 | 没有更多了哦~ 36 | 37 | -------------------------------------------------------------------------------- /miniprogram/pages/word_list/word_list.wxss: -------------------------------------------------------------------------------- 1 | .wordWrapper { 2 | width: 100%; 3 | } 4 | .wordWrapper .wordItem { 5 | width: 750rpx; 6 | height: wordItemHeight; 7 | background-color: #ffffff; 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | font-weight: 600; 12 | } 13 | .wordWrapper .wordItem .dot { 14 | width: 16rpx; 15 | height: 16rpx; 16 | border-radius: 8rpx; 17 | background-color: #fd6802; 18 | margin-right: 24rpx; 19 | } 20 | .wordWrapper .wordItem .wordInfo { 21 | width: 630rpx; 22 | height: 80rpx; 23 | line-height: 80rpx; 24 | font-size: 32rpx; 25 | display: -webkit-box; 26 | overflow: hidden; 27 | -webkit-box-orient: vertical; 28 | -webkit-line-clamp: 1; 29 | } 30 | .wordWrapper .wordItem .wordInfo .word { 31 | color: #757575; 32 | } 33 | .wordWrapper .wordItem .wordInfo .trans { 34 | color: #8a8a8a; 35 | font-size: 28rpx; 36 | } 37 | .wordWrapper .wasTaped { 38 | background-color: #e6e6e6; 39 | } 40 | .wordWrapper .tips { 41 | width: 100%; 42 | height: 100rpx; 43 | color: #8a8a8a; 44 | font-size: 28rpx; 45 | display: flex; 46 | align-items: center; 47 | justify-content: center; 48 | } 49 | .wordWrapper .changeType { 50 | width: 100%; 51 | height: 100rpx; 52 | font-size: 32rpx; 53 | display: flex; 54 | background-color: #ffffff; 55 | position: fixed; 56 | } 57 | .wordWrapper .changeType .type { 58 | width: 50%; 59 | height: 100rpx; 60 | display: flex; 61 | flex-direction: column; 62 | align-items: center; 63 | justify-content: center; 64 | } 65 | .wordWrapper .changeType .type .text { 66 | font-size: 26rpx; 67 | color: #8a8a8a; 68 | font-weight: 600; 69 | } 70 | .wordWrapper .changeType .type .decorate { 71 | margin-top: 10rpx; 72 | width: 30rpx; 73 | height: 10rpx; 74 | border-radius: 5rpx; 75 | background-color: #fd6802; 76 | } 77 | .wordWrapper .changeType .type .active { 78 | font-size: 30rpx; 79 | color: #515151; 80 | margin-top: 10rpx; 81 | } 82 | .wordWrapper .forToday { 83 | width: 100%; 84 | margin-top: 100rpx; 85 | } 86 | .wordWrapper .bottom { 87 | width: 100%; 88 | height: 100rpx; 89 | } 90 | -------------------------------------------------------------------------------- /miniprogram/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules":[{ 4 | "action": "allow", 5 | "page": "pages/index/index" 6 | }, { 7 | "action": "disallow", 8 | "page": "*" 9 | }] 10 | } -------------------------------------------------------------------------------- /miniprogram/static/color.wxss: -------------------------------------------------------------------------------- 1 | /* for #ffb284 */ 2 | .bg-linear-0 { 3 | background-image: linear-gradient(to bottom, #ffb284, #FFFFFF); 4 | } 5 | 6 | .word-color-0 { 7 | color: #ee5b20; 8 | } 9 | 10 | .content-title-color-0 { 11 | color: #fa804f; 12 | } 13 | 14 | .bg-color-light-0 { 15 | background-color: #fa804f; 16 | } 17 | 18 | /* for #99c4d3 */ 19 | .bg-linear-1 { 20 | background-image: linear-gradient(to bottom, #99c4d3, #FFFFFF); 21 | } 22 | 23 | .word-color-1 { 24 | color: #166381; 25 | } 26 | 27 | .content-title-color-1 { 28 | color: #66a8eb; 29 | } 30 | 31 | .bg-color-light-1 { 32 | background-color: #66a8eb; 33 | } 34 | 35 | /* for #d0e6a5 */ 36 | .bg-linear-2 { 37 | background-image: linear-gradient(to bottom, #d0e6a5, #FFFFFF); 38 | } 39 | 40 | .word-color-2 { 41 | color: #7a9e32; 42 | } 43 | 44 | .content-title-color-2 { 45 | color: #adc57c; 46 | } 47 | 48 | .bg-color-light-2 { 49 | background-color: #adc57c; 50 | } 51 | 52 | /* for #86e3ce */ 53 | .bg-linear-3 { 54 | background-image: linear-gradient(to bottom, #86e3ce, #FFFFFF); 55 | } 56 | 57 | .word-color-3 { 58 | color: #30a58a; 59 | } 60 | 61 | .content-title-color-3 { 62 | color: #60d4b9; 63 | } 64 | 65 | .bg-color-light-3 { 66 | background-color: #60d4b9; 67 | } 68 | 69 | /* for #ffdd95 */ 70 | .bg-linear-4 { 71 | background-image: linear-gradient(to bottom, #ffdd95, #FFFFFF); 72 | } 73 | 74 | .word-color-4 { 75 | color: #c79b3d; 76 | } 77 | 78 | .content-title-color-4 { 79 | color: #f0c76f; 80 | } 81 | 82 | .bg-color-light-4 { 83 | background-color: #f0c76f; 84 | } 85 | 86 | /* for #fa897b */ 87 | .bg-linear-5 { 88 | background-image: linear-gradient(to bottom, #fa897b, #FFFFFF); 89 | } 90 | 91 | .word-color-5 { 92 | color: #d84c39; 93 | } 94 | 95 | .content-title-color-5 { 96 | color: #fd7361; 97 | } 98 | 99 | .bg-color-light-5 { 100 | background-color: #fd7361; 101 | } 102 | 103 | /* for #ccabd8 */ 104 | .bg-linear-6 { 105 | background-image: linear-gradient(to bottom, #ccabd8, #FFFFFF); 106 | } 107 | 108 | .word-color-6 { 109 | color: #b163ce; 110 | } 111 | 112 | .content-title-color-6 { 113 | color: #be78d8; 114 | } 115 | 116 | .bg-color-light-6 { 117 | background-color: #be78d8; 118 | } 119 | 120 | /* for #80beaf */ 121 | .bg-linear-7 { 122 | background-image: linear-gradient(to bottom, #80beaf, #FFFFFF); 123 | } 124 | 125 | .word-color-7 { 126 | color: #46927f; 127 | } 128 | 129 | .content-title-color-7 { 130 | color: #61cfb4; 131 | } 132 | 133 | .bg-color-light-7 { 134 | background-color: #61cfb4; 135 | } 136 | 137 | /* for #b3ddd1 */ 138 | .bg-linear-8 { 139 | background-image: linear-gradient(to bottom, #b3ddd1, #FFFFFF); 140 | } 141 | 142 | .word-color-8 { 143 | color: #459780; 144 | } 145 | 146 | .content-title-color-8 { 147 | color: #7adabe; 148 | } 149 | 150 | .bg-color-light-8 { 151 | background-color: #7adabe; 152 | } 153 | 154 | /* for #d1dce2 */ 155 | .bg-linear-9 { 156 | background-image: linear-gradient(to bottom, #d1dce2, #FFFFFF); 157 | } 158 | 159 | .word-color-9 { 160 | color: #6fa7c5; 161 | } 162 | 163 | .content-title-color-9 { 164 | color: #95bfd6; 165 | } 166 | 167 | .bg-color-light-9 { 168 | background-color: #95bfd6; 169 | } 170 | 171 | /* for #ef9d6d */ 172 | .bg-linear-10 { 173 | background-image: linear-gradient(to bottom, #ef9d6d, #FFFFFF); 174 | } 175 | 176 | .word-color-10 { 177 | color: #c4632c; 178 | } 179 | 180 | .content-title-color-10 { 181 | color: #f38b4e; 182 | } 183 | 184 | .bg-color-light-10 { 185 | background-color: #f38b4e; 186 | } 187 | 188 | /* for #c6c09c */ 189 | .bg-linear-11 { 190 | background-image: linear-gradient(to bottom, #c6c09c, #FFFFFF); 191 | } 192 | 193 | .word-color-11 { 194 | color: #ac9f5a; 195 | } 196 | 197 | .content-title-color-11 { 198 | color: #c7b861; 199 | } 200 | 201 | .bg-color-light-11 { 202 | background-color: #c7b861; 203 | } 204 | 205 | /* for #f5cec7 */ 206 | .bg-linear-12 { 207 | background-image: linear-gradient(to bottom, #f5cec7, #FFFFFF); 208 | } 209 | 210 | .word-color-12 { 211 | color: #c7938a; 212 | } 213 | 214 | .content-title-color-12 { 215 | color: #f0b2a7; 216 | } 217 | 218 | .bg-color-light-12 { 219 | background-color: #f0b2a7; 220 | } 221 | 222 | /* for #ffc98b */ 223 | .bg-linear-13 { 224 | background-image: linear-gradient(to bottom, #ffc98b, #FFFFFF); 225 | } 226 | 227 | .word-color-13 { 228 | color: #ce9553; 229 | } 230 | 231 | .content-title-color-13 { 232 | color: #f1b167; 233 | } 234 | 235 | .bg-color-light-13 { 236 | background-color: #f1b167; 237 | } 238 | 239 | /* for #b598c6 */ 240 | .bg-linear-14 { 241 | background-image: linear-gradient(to bottom, #b598c6, #FFFFFF); 242 | } 243 | 244 | .word-color-14 { 245 | color: #866699; 246 | } 247 | 248 | .content-title-color-14 { 249 | color: #ae78ce; 250 | } 251 | 252 | .bg-color-light-14 { 253 | background-color: #ae78ce; 254 | } 255 | 256 | /* for #73c8dd */ 257 | .bg-linear-15 { 258 | background-image: linear-gradient(to bottom, #73c8dd, #FFFFFF); 259 | } 260 | 261 | .word-color-15 { 262 | color: #4899ad; 263 | } 264 | 265 | .content-title-color-15 { 266 | color: #50c1dd; 267 | } 268 | 269 | .bg-color-light-15 { 270 | background-color: #50c1dd; 271 | } 272 | 273 | /* for #c56a4b */ 274 | .bg-linear-16 { 275 | background-image: linear-gradient(to bottom, #c56a4b, #FFFFFF); 276 | } 277 | 278 | .word-color-16 { 279 | color: #a14323; 280 | } 281 | 282 | .content-title-color-16 { 283 | color: #e26e47; 284 | } 285 | 286 | .bg-color-light-16 { 287 | background-color: #e26e47; 288 | } 289 | 290 | -------------------------------------------------------------------------------- /miniprogram/static/iconfont.wxss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "iconfont"; /* Project id 2904327 */ 3 | src: url('//at.alicdn.com/t/font_2904327_ob31m8hc8ul.woff2?t=1641287710427') format('woff2'), 4 | url('//at.alicdn.com/t/font_2904327_ob31m8hc8ul.woff?t=1641287710427') format('woff'), 5 | url('//at.alicdn.com/t/font_2904327_ob31m8hc8ul.ttf?t=1641287710427') format('truetype'); 6 | } 7 | 8 | .iconfont { 9 | font-family: "iconfont" !important; 10 | font-size: 16px; 11 | font-style: normal; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | .icon-rotate:before { 17 | content: "\e61d"; 18 | } 19 | 20 | .icon-settings:before { 21 | content: "\e8b7"; 22 | } 23 | 24 | .icon-pwd:before { 25 | content: "\e600"; 26 | } 27 | 28 | .icon-toDetail:before { 29 | content: "\e775"; 30 | } 31 | 32 | .icon-camera:before { 33 | content: "\e77f"; 34 | } 35 | 36 | .icon-settings_old:before { 37 | content: "\e892"; 38 | } 39 | 40 | .icon-nickname:before { 41 | content: "\e608"; 42 | } 43 | 44 | .icon-learned:before { 45 | content: "\e721"; 46 | } 47 | 48 | .icon-addToNB-no:before { 49 | content: "\e8b9"; 50 | } 51 | 52 | .icon-addToNB-yes:before { 53 | content: "\e8c6"; 54 | } 55 | 56 | .icon-skip:before { 57 | content: "\e622"; 58 | } 59 | 60 | .icon-getDetail:before { 61 | content: "\e68e"; 62 | } 63 | 64 | .icon-cancel:before { 65 | content: "\e668"; 66 | } 67 | 68 | .icon-delete:before { 69 | content: "\e621"; 70 | } 71 | 72 | .icon-bin:before { 73 | content: "\e652"; 74 | } 75 | 76 | .icon-search1:before { 77 | content: "\e8d6"; 78 | } 79 | 80 | .icon-search:before { 81 | content: "\e60c"; 82 | } 83 | 84 | .icon-sound:before { 85 | content: "\e7a8"; 86 | } 87 | -------------------------------------------------------------------------------- /miniprogram/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/logo.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-learn-007BFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-learn-007BFF.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-learn-3880B7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-learn-3880B7.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-learn-62BEFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-learn-62BEFF.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-learn-A6D6FA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-learn-A6D6FA.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-learn-CDCDCD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-learn-CDCDCD.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-learn-F6F6F6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-learn-F6F6F6.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-learn-FFFFFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-learn-FFFFFF.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-overview-007BFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-overview-007BFF.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-overview-3880B7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-overview-3880B7.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-overview-62BEFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-overview-62BEFF.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-overview-A6D6FA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-overview-A6D6FA.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-overview-CDCDCD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-overview-CDCDCD.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-user-007BFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-user-007BFF.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-user-3880B7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-user-3880B7.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-user-62BEFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-user-62BEFF.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-user-A6D6FA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-user-A6D6FA.png -------------------------------------------------------------------------------- /miniprogram/static/images/tab-user-CDCDCD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mint-green/UnlearnableWord/b7aabd362e138cc9b3c86d1ede3c958e222c5c10/miniprogram/static/images/tab-user-CDCDCD.png -------------------------------------------------------------------------------- /miniprogram/utils/color.js: -------------------------------------------------------------------------------- 1 | const colorList = ['#ffb284', '#99c4d3', '#d0e6a5', '#86e3ce', '#ffdd95', '#fa897b', 2 | '#ccabd8', '#80beaf', '#b3ddd1', '#d1dce2', '#ef9d6d', '#c6c09c', '#f5cec7', 3 | '#ffc98b', '#b598c6', '#73c8dd', '#c56a4b'] 4 | 5 | const deeperColorList = ['#ee5b20', '#166381', '#7a9e32', '#30a58a', '#c79b3d', '#d84c39', 6 | '#b163ce', '#46927f', '#459780', '#6fa7c5', '#c4632c', '#ac9f5a', '#c7938a', 7 | '#ce9553', '#866699', '#4899ad', '#a14323'] 8 | 9 | module.exports = { 10 | colorList: colorList, 11 | deeperColorList: deeperColorList, 12 | } -------------------------------------------------------------------------------- /miniprogram/utils/format_time.js: -------------------------------------------------------------------------------- 1 | // 传入时间的毫秒数(date.getTime())获取时间详情 2 | 3 | const formatTime = (time) => { 4 | var date = new Date(time) 5 | var y = date.getFullYear() 6 | var m = date.getMonth() + 1 7 | var d = date.getDate() 8 | var h = date.getHours() 9 | var min = date.getMinutes() 10 | var s = date.getSeconds() 11 | var timeStr = y + "-" + enterZero(m) + "-" + enterZero(d) + " " + enterZero(h) + ":" + enterZero(min) + ":" + enterZero(s) 12 | return timeStr 13 | } 14 | 15 | const formatDate = (time) => { 16 | var date = new Date(time) 17 | var y = date.getFullYear() 18 | var m = date.getMonth() + 1 19 | var d = date.getDate() 20 | var dateStr = y + "-" + enterZero(m) + "-" + enterZero(d) 21 | return dateStr 22 | } 23 | 24 | const getDayZeroTime = (time = new Date().getTime()) => { 25 | var date = new Date(time) 26 | date.setMilliseconds(0) 27 | date.setSeconds(0) 28 | date.setMinutes(0) 29 | date.setHours(0) 30 | return date.getTime() 31 | } 32 | 33 | const dateNum = (time) => { 34 | var date = new Date(time) 35 | var y = date.getFullYear() 36 | var m = date.getMonth() + 1 37 | var d = date.getDate() 38 | var num = y * 10000 + m * 100 + d 39 | return num 40 | } 41 | 42 | const enterZero = (num) => { 43 | num = Math.abs(num) 44 | if (num <= 9) { 45 | num = "0" + num 46 | } 47 | return num 48 | } 49 | 50 | module.exports = { 51 | formatTime: formatTime, 52 | formatDate: formatDate, 53 | dateNum: dateNum, 54 | getDayZeroTime: getDayZeroTime, 55 | } -------------------------------------------------------------------------------- /miniprogram/utils/response_content.js: -------------------------------------------------------------------------------- 1 | const SUCCESS = { errorcode: 100, errormsg: "success" } //成功 2 | const LOGINOK = { errorcode: 1, errormsg: "Login successfully" } //登录成功 3 | const REGISTEROK= { errorcode: 2, errormsg: "Register successfully" } //注册成功 4 | const DBERR = { errorcode: -1, errormsg: "Database error!" } //数据库操作失败 5 | const ROUTERERR = { errorcode: -2, errormsg: "Wrong router name" } //路由名字有误 6 | const LOGINERR = { errorcode: -3, errormsg: "Wrong username or pwd" } //登录信息有误 7 | const DATAERR = { errorcode: -4, errormsg: "Wrong data!" } //数据有误 8 | const UNKOWNERR = { errorcode: -100, errormsg: "Unkown error!" } //出现未知错误 9 | 10 | 11 | module.exports={ 12 | SUCCESS: SUCCESS, 13 | LOGINOK: LOGINOK, 14 | REGISTEROK: REGISTEROK, 15 | DBERR: DBERR, 16 | ROUTERERR: ROUTERERR, 17 | LOGINERR: LOGINERR, 18 | DATAERR: DATAERR, 19 | UNKOWNERR: UNKOWNERR, 20 | } 21 | -------------------------------------------------------------------------------- /miniprogram/utils/userApi.js: -------------------------------------------------------------------------------- 1 | const checkUsernameInDB = (data) => { 2 | data.$url = 'checkUsername' 3 | return new Promise((resolve, reject) => { 4 | wx.cloud.callFunction({ 5 | name: "userRouter", 6 | data, 7 | success: (res) => { 8 | resolve(res.result) 9 | }, 10 | fail: (err) => { 11 | reject(err) 12 | } 13 | }) 14 | }) 15 | } 16 | 17 | const register = (data) => { 18 | data.$url = 'register' 19 | return new Promise((resolve, reject) => { 20 | wx.cloud.callFunction({ 21 | name: "userRouter", 22 | data, 23 | success: (res) => { 24 | resolve(res.result) 25 | }, 26 | fail: (err) => { 27 | reject(err) 28 | } 29 | }) 30 | }) 31 | } 32 | 33 | const login = (data) => { 34 | data.$url = 'login' 35 | return new Promise((resolve, reject) => { 36 | wx.cloud.callFunction({ 37 | name: "userRouter", 38 | data, 39 | success: (res) => { 40 | resolve(res.result) 41 | }, 42 | fail: (err) => { 43 | reject(err) 44 | } 45 | }) 46 | }) 47 | } 48 | 49 | const getWxUserInfo = () => { 50 | return new Promise((resolve, reject) => { 51 | wx.getUserProfile({ 52 | desc: '信息用于快捷登录小程序', 53 | success: (res) => { 54 | resolve(res) 55 | }, 56 | fail: (err) => { 57 | console.log('获取微信用户信息失败') 58 | reject(err) 59 | } 60 | }) 61 | }) 62 | } 63 | 64 | const wxLogin = (data) => { 65 | data.$url = 'wxLogin' 66 | return new Promise((resolve, reject) => { 67 | wx.cloud.callFunction({ 68 | name: "userRouter", 69 | data, 70 | success: (res) => { 71 | resolve(res.result) 72 | }, 73 | fail: (err) => { 74 | reject(err) 75 | } 76 | }) 77 | }) 78 | } 79 | 80 | const changeWordBook = (data) => { 81 | // let data = {} 82 | data.$url = 'changeWordBook' 83 | return new Promise((resolve, reject) => { 84 | wx.cloud.callFunction({ 85 | name: "userRouter", 86 | data, 87 | success: (res) => { 88 | resolve(res.result) 89 | }, 90 | fail: (err) => { 91 | reject(err) 92 | } 93 | }) 94 | }) 95 | } 96 | 97 | const changeSettings = (data) => { 98 | // let data = {} 99 | data.$url = 'changeSettings' 100 | return new Promise((resolve, reject) => { 101 | wx.cloud.callFunction({ 102 | name: "userRouter", 103 | data, 104 | success: (res) => { 105 | resolve(res.result) 106 | }, 107 | fail: (err) => { 108 | reject(err) 109 | } 110 | }) 111 | }) 112 | } 113 | 114 | const getUserInfoViaId = (data) => { 115 | // let data = {} 116 | data.$url = 'getUserInfoViaId' 117 | return new Promise((resolve, reject) => { 118 | wx.cloud.callFunction({ 119 | name: "userRouter", 120 | data, 121 | success: (res) => { 122 | resolve(res.result) 123 | }, 124 | fail: (err) => { 125 | reject(err) 126 | } 127 | }) 128 | }) 129 | } 130 | 131 | const changeUserInfo = (data) => { 132 | // let data = {} 133 | data.$url = 'changeUserInfo' 134 | return new Promise((resolve, reject) => { 135 | wx.cloud.callFunction({ 136 | name: "userRouter", 137 | data, 138 | success: (res) => { 139 | resolve(res.result) 140 | }, 141 | fail: (err) => { 142 | reject(err) 143 | } 144 | }) 145 | }) 146 | } 147 | 148 | const changePwd = (data) => { 149 | // let data = {} 150 | data.$url = 'changePwd' 151 | return new Promise((resolve, reject) => { 152 | wx.cloud.callFunction({ 153 | name: "userRouter", 154 | data, 155 | success: (res) => { 156 | resolve(res.result) 157 | }, 158 | fail: (err) => { 159 | reject(err) 160 | } 161 | }) 162 | }) 163 | } 164 | 165 | const uploadFile = (imgSrc) => { 166 | return new Promise((resolve, reject) => { 167 | let fileExtName = /\.\w+$/.exec(imgSrc)[0] //获取文件格式(后缀名) 168 | wx.cloud.uploadFile({ 169 | cloudPath: 'avatar_pic/' + Date.now() + '-' + Math.floor(Math.random() * 10000) + fileExtName, //生成添加时间戳后的随机序列作为文件名 170 | filePath: imgSrc, 171 | success: (res) => { 172 | resolve(res) 173 | }, 174 | fail: (err) => { 175 | console.log(err) 176 | reject(err) 177 | } 178 | }) 179 | }) 180 | } 181 | 182 | const downloadFile = (imgSrc) => { 183 | return new Promise((resolve, reject) => { 184 | wx.downloadFile({ 185 | url: imgSrc, 186 | success(res) { 187 | // 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容 188 | if (res.statusCode === 200) { 189 | // console.log(res) 190 | resolve(res) 191 | } 192 | }, 193 | fail: (err) => { 194 | console.log(err) 195 | reject(err) 196 | } 197 | }) 198 | }) 199 | } 200 | 201 | module.exports = { 202 | checkUsernameInDB: checkUsernameInDB, 203 | register: register, 204 | login: login, 205 | getWxUserInfo: getWxUserInfo, 206 | wxLogin: wxLogin, 207 | changeWordBook: changeWordBook, 208 | changeSettings: changeSettings, 209 | getUserInfoViaId: getUserInfoViaId, 210 | changeUserInfo: changeUserInfo, 211 | changePwd: changePwd, 212 | downloadFile: downloadFile, 213 | uploadFile: uploadFile, 214 | } 215 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "miniprogram/", 3 | "cloudfunctionRoot": "cloudfunctions/", 4 | "setting": { 5 | "urlCheck": true, 6 | "es6": true, 7 | "enhance": true, 8 | "postcss": true, 9 | "preloadBackgroundData": false, 10 | "minified": true, 11 | "newFeature": true, 12 | "coverView": true, 13 | "nodeModules": false, 14 | "autoAudits": false, 15 | "showShadowRootInWxmlPanel": true, 16 | "scopeDataCheck": false, 17 | "uglifyFileName": false, 18 | "checkInvalidKey": true, 19 | "checkSiteMap": true, 20 | "uploadWithSourceMap": true, 21 | "compileHotReLoad": false, 22 | "lazyloadPlaceholderEnable": false, 23 | "useMultiFrameRuntime": true, 24 | "useApiHook": true, 25 | "useApiHostProcess": true, 26 | "babelSetting": { 27 | "ignore": [], 28 | "disablePlugins": [], 29 | "outputPath": "" 30 | }, 31 | "enableEngineNative": false, 32 | "useIsolateContext": false, 33 | "userConfirmedBundleSwitch": false, 34 | "packNpmManually": false, 35 | "packNpmRelationList": [], 36 | "minifyWXSS": true, 37 | "disableUseStrict": false, 38 | "minifyWXML": true, 39 | "showES6CompileOption": false, 40 | "useCompilerPlugins": false 41 | }, 42 | "appid": "wx9d444179caa0a6b5", 43 | "projectname": "%E5%AD%A6%E4%B8%8D%E4%BC%9A%E5%8D%95%E8%AF%8D", 44 | "libVersion": "2.20.1", 45 | "cloudfunctionTemplateRoot": "cloudfunctionTemplate", 46 | "condition": { 47 | "search": { 48 | "list": [] 49 | }, 50 | "conversation": { 51 | "list": [] 52 | }, 53 | "plugin": { 54 | "list": [] 55 | }, 56 | "game": { 57 | "list": [] 58 | }, 59 | "miniprogram": { 60 | "list": [] 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /project.private.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "setting": {}, 3 | "condition": { 4 | "plugin": { 5 | "list": [] 6 | }, 7 | "game": { 8 | "list": [] 9 | }, 10 | "gamePlugin": { 11 | "list": [] 12 | }, 13 | "miniprogram": { 14 | "list": [ 15 | { 16 | "name": "pages/search/search", 17 | "pathName": "pages/search/search", 18 | "query": "", 19 | "scene": null 20 | }, 21 | { 22 | "name": "pages/word_detail/word_detail", 23 | "pathName": "pages/word_detail/word_detail", 24 | "query": "word_id=1630", 25 | "scene": null 26 | }, 27 | { 28 | "name": "pages/login/login", 29 | "pathName": "pages/login/login", 30 | "query": "", 31 | "scene": null 32 | }, 33 | { 34 | "name": "pages/learning/learning", 35 | "pathName": "pages/learning/learning", 36 | "query": "", 37 | "scene": null 38 | }, 39 | { 40 | "name": "pages/overview/overview", 41 | "pathName": "pages/overview/overview", 42 | "query": "", 43 | "scene": null 44 | }, 45 | { 46 | "name": "pages/review/review", 47 | "pathName": "pages/review/review", 48 | "query": "", 49 | "scene": null 50 | }, 51 | { 52 | "name": "pages/word_list/word_list", 53 | "pathName": "pages/word_list/word_list", 54 | "query": "type=getBkLearnedWord", 55 | "scene": null 56 | }, 57 | { 58 | "name": "pages/user/user", 59 | "pathName": "pages/user/user", 60 | "query": "", 61 | "scene": null 62 | }, 63 | { 64 | "name": "pages/user_settings/user_settings", 65 | "pathName": "pages/user_settings/user_settings", 66 | "query": "", 67 | "scene": null 68 | }, 69 | { 70 | "name": "", 71 | "pathName": "pages/image_cropper/image_cropper", 72 | "query": "", 73 | "scene": null 74 | } 75 | ] 76 | } 77 | } 78 | } --------------------------------------------------------------------------------