Compare commits

..

100 Commits

Author SHA1 Message Date
helloworld 48a22e5d2e update api.js 2022-12-24 19:23:46 +08:00
lcq e8bc9625af 上传书签解码错误继续进行 2022-10-30 17:17:14 +08:00
lcq 9f462bfed1 更新间距 2022-02-09 17:46:16 +08:00
lcq 1677d60b51 Update Dockerfile 2022-01-27 23:18:08 +08:00
lcq 60bbfbb391 Update 2022-01-23 13:21:53 +08:00
lcq ea9097b124 update divider margin 2022-01-02 09:09:45 +08:00
lcq c8f183fbff 首页只返回最近添加与最近点击 2021-12-25 00:25:24 +08:00
lcq ac1758b8da update 2021-12-25 00:17:24 +08:00
lcq 27f1ccdb1a 首页加横线区分分类 2021-12-25 00:05:39 +08:00
lcq 7b0ed143d4 增加数据量 2021-12-24 17:49:08 +08:00
lovej 64c1750c20 remove cors 2021-12-08 13:52:07 +08:00
lcq 3d06055292 增加json 2021-12-08 11:44:47 +08:00
LuChenQun 0a74c4fc0a
Update README.md 2021-11-17 10:25:06 +08:00
LuChenQun 93fa0ce016
Update README.md
更新arm架构的docker
2021-11-17 10:24:34 +08:00
lcq d02c27a8f7 过期日期改为30天 2021-11-08 14:08:52 +08:00
LuChenQun 692ab124aa
Update api.js 2021-08-04 09:26:00 +08:00
lovej f2797f4bbf 书签存在如果重复添加则更新添加日期 2021-08-04 09:24:22 +08:00
lcq f6fd7bda23 修复提升问题 2021-06-12 18:24:06 +08:00
lcq dcdfb5d202 fix bug 2021-06-12 18:06:54 +08:00
lcq b52c622f93 5 minutes 2021-06-08 19:24:37 +08:00
B05BEE13.卢郴群 c9ae4bd803 Update loading style 2021-06-03 10:29:33 +08:00
B05BEE13.卢郴群 cbe4ad4e91 修复小问题 2021-06-01 09:32:55 +08:00
B05BEE13.卢郴群 2448ce767b 修复导入导出的问题 2021-05-31 17:29:56 +08:00
B05BEE13.卢郴群 ba087ae32c update css 2021-05-28 11:02:50 +08:00
B05BEE13.卢郴群 0d5adcd304 update css 2021-05-28 11:01:27 +08:00
B05BEE13.卢郴群 f2f041b7f7 页面小调整 2021-05-26 17:08:33 +08:00
B05BEE13.卢郴群 30878006eb 将域名mybookmark.cn切换成b.lucq.fun 2021-05-26 16:03:33 +08:00
B05BEE13.卢郴群 dd67d8ee64 修复aa.bb@outlook.com无法注册的问题 2021-05-12 14:53:32 +08:00
LuChenQun 075ff1fd0b
更新登录时间 2021-04-06 14:01:51 +08:00
B05BEE13.卢郴群 d4d65275d6 Update readme.md 2021-01-06 13:58:44 +08:00
B05BEE13.卢郴群 8dfe0db390 Update dockerfile 2021-01-05 17:18:45 +08:00
B05BEE13.卢郴群 72993b46a5 mv www --> view 2021-01-05 17:14:20 +08:00
LuChenQun 2e019d26ac
Update README.md 2020-12-18 11:27:52 +08:00
B05BEE13.卢郴群 47773c2560 Merge branch 'master' of github.com:luchenqun/my-bookmark 2020-12-18 11:12:05 +08:00
B05BEE13.卢郴群 82b45db461 更新Docker文件 2020-12-18 11:08:45 +08:00
lovej 7b1401c4f5 解决岛链无法显示的问题 2020-12-10 10:12:43 +08:00
B05BEE13.卢郴群 f4f5149e27 替换岛链图片 2020-12-10 09:56:32 +08:00
B05BEE13.卢郴群 38d6ff2084 Update Dockerfile 2020-11-24 13:46:29 +08:00
B05BEE13.卢郴群 5c756434ee Update Dockerfile 2020-11-24 13:04:15 +08:00
B05BEE13.卢郴群 a09dbf23dd Update Dockerfile 2020-11-24 12:34:47 +08:00
B05BEE13.卢郴群 14d976a12d update 2020-11-24 11:51:27 +08:00
B05BEE13.卢郴群 c77bf01a08 Update README.md 2020-11-23 14:08:33 +08:00
B05BEE13.卢郴群 c0e3af63c1 项目自启动服务 2020-11-23 13:53:49 +08:00
B05BEE13.卢郴群 597052515c Update README.md 2020-11-19 14:25:50 +08:00
B05BEE13.卢郴群 d637f070a9 Update README.md 2020-11-19 09:40:25 +08:00
B05BEE13.卢郴群 7044dff709 Update Dockerfile 2020-11-19 08:51:44 +08:00
B05BEE13.卢郴群 53d1bc0ea1 Update Dockerfile 2020-11-18 12:20:52 +08:00
B05BEE13.卢郴群 5c2420ab1d add Dockerfile 2020-11-18 11:21:46 +08:00
B05BEE13.卢郴群 ef42a5ada7 add test user 2020-11-16 13:11:49 +08:00
B05BEE13.卢郴群 7f89c32404 Update 2020-11-16 13:05:11 +08:00
B05BEE13.卢郴群 101022258d 修复热门显示问题 2020-11-12 08:50:42 +08:00
B05BEE13.卢郴群 bdb9a931fc 取消双击复制备忘内容,显示书签备忘 2020-10-26 10:10:59 +08:00
B05BEE13.卢郴群 f667b0f290 完成排序bug 2020-09-02 10:31:35 +08:00
B05BEE13.卢郴群 040bc47219 完成排序bug 2020-09-02 10:28:00 +08:00
B05BEE13.卢郴群 7c8b533a6e 搜索文字变小 2020-09-02 09:43:45 +08:00
B05BEE13.卢郴群 664083f0e3 更新第0页显示逻辑 2020-09-02 08:31:37 +08:00
B05BEE13.卢郴群 13dfecc3af fix bugs 2020-09-01 17:54:43 +08:00
B05BEE13.卢郴群 60b7ff714f 完成书签的分类显示 2020-09-01 17:41:22 +08:00
B05BEE13.卢郴群 c4dba3cb18 添加书签的分类按照最新显示 2020-09-01 17:00:58 +08:00
HelloWorld 6967bcee7a 首页不显示 2020-07-21 17:22:49 +08:00
HelloWorld c9fa1add85 更新收取图片抓取逻辑 2020-07-17 10:00:27 +08:00
HelloWorld 74de1c1ffa 放开注册 2020-06-29 14:00:32 +08:00
HelloWorld c9f6c71120 放开注册 2020-06-29 13:56:50 +08:00
HelloWorld d18abde892 配合工信部备案 2020-06-23 08:55:26 +08:00
HelloWorld 34d2696cc3 配合工信部备案 2020-06-23 08:52:46 +08:00
HelloWorld 411cd8b970 更换域名 2020-06-22 17:31:11 +08:00
HelloWorld a0242aa1a8 图片如果不是地址使用默认的 2020-05-06 10:08:17 +08:00
LuChenQun 8d9f3b20ba
Update README.md 2020-05-04 09:42:34 +08:00
LuChenQun 7de7687dcf
Update README.md 2020-04-30 08:42:48 +08:00
LuChenQun bd67663191
Update README.md 2020-04-24 08:26:51 +08:00
LuChenQun 666ced7d6d
Update README.md 2020-04-23 16:00:15 +08:00
HelloWorld 18bab95327 分类没有用未知替代 2020-04-23 08:48:34 +08:00
HelloWorld a3b179a4e7 更新微信到热门 2020-04-23 08:25:46 +08:00
HelloWorld b812d99ee6 https不行http上 2020-04-22 20:50:53 +08:00
HelloWorld 73105a7acb http --> https 2020-04-22 20:30:33 +08:00
HelloWorld 3b17d30a34 收趣图片替换成https 2020-04-22 17:54:48 +08:00
HelloWorld a25bd7ce63 图片加载上https 2020-04-22 17:18:05 +08:00
HelloWorld 823e1625b0 搜索书签bug修复 2020-04-22 15:14:05 +08:00
HelloWorld dd13f85163 默认隐藏全局书签 2020-04-22 08:36:26 +08:00
HelloWorld 819109c900 默认隐藏全局书签 2020-04-22 08:30:52 +08:00
HelloWorld 746460eded 默认隐藏全局书签 2020-04-22 08:29:11 +08:00
HelloWorld 2f5f7792d9 增加一个全局书签导航 2020-04-22 08:25:01 +08:00
HelloWorld 10dc1f46a8 test不允许修改密码 2020-04-21 17:39:12 +08:00
HelloWorld 28895b7019 修复搜索bug 2020-04-21 17:34:27 +08:00
LuChenQun c58a367e88
Update README.md 2020-04-17 08:11:30 +08:00
LuChenQun fec9ecf03f
Update schema.sql 2020-04-17 08:09:41 +08:00
HelloWorld 70f166de70 自己不想看热门 2020-04-16 10:15:41 +08:00
HelloWorld 97a35af919 热门可以不用登录 2020-04-14 11:45:37 +08:00
HelloWorld 7425be2690 完成随机查看收趣收藏 2020-04-14 11:35:47 +08:00
HelloWorld b8a482f94c 完成热门抓取 2020-04-14 11:00:39 +08:00
HelloWorld c6d8a76102 增加定时10分钟抓取文章 2020-04-14 09:49:47 +08:00
HelloWorld 18fd732daa 提供一个下载链接 2020-04-13 22:07:06 +08:00
HelloWorld e6af3742ac 完成书签的备份下载 2020-04-13 21:49:21 +08:00
HelloWorld 6636b698ff 修复搜索获取用户问题 2020-04-13 11:23:44 +08:00
HelloWorld a4c95df12b 默认d登录 2020-04-13 10:28:04 +08:00
HelloWorld df29e3e1d5 更新一下菜单逻辑 2020-04-13 10:22:48 +08:00
HelloWorld 56620d2565 Update README.md 2020-04-13 08:08:49 +08:00
HelloWorld 6492d3ad51 Update README.md 2020-04-12 16:40:33 +08:00
HelloWorld 6cfdc601ab Update README.md 2020-04-12 16:31:09 +08:00
HelloWorld c4f61f6bbe 整理一下热门 2020-04-10 21:04:36 +08:00
143 changed files with 2115 additions and 1897 deletions

27
Dockerfile Normal file
View File

@ -0,0 +1,27 @@
FROM luchenqun/ubuntu-mysql-node
LABEL maintainer="luchenqun@qq.com"
RUN mkdir -p /app
COPY src /app/src
COPY view /app/view
COPY package.json /app/package.json
COPY production.js /app/production.js
COPY schema.sql /app/schema.sql
WORKDIR /app
RUN USER=`sed -n '4,4p' /etc/mysql/debian.cnf | awk 'BEGIN { FS = "= " } ; { print $2 }'` \
&& sed -i "s/test/${USER}/" /app/src/config/adapter.js \
&& PASSWORD=`sed -n '5,5p' /etc/mysql/debian.cnf | awk 'BEGIN { FS = "= " } ; { print $2 }'` \
&& sed -i "s/123456/${PASSWORD}/g" /app/src/config/adapter.js \
&& npm install --production \
&& touch /usr/local/bin/start.sh \
&& chmod 777 /usr/local/bin/start.sh \
&& echo "#!/bin/bash" >> /usr/local/bin/start.sh \
&& echo "service mysql restart" >> /usr/local/bin/start.sh \
&& echo "mysql -u root < /app/schema.sql" >> /usr/local/bin/start.sh \
&& echo "node /app/production.js" >> /usr/local/bin/start.sh
EXPOSE 3306
EXPOSE 2000
ENTRYPOINT ["start.sh"]

147
README.md
View File

@ -1,22 +1,149 @@
# 在线书签管理工具
![image](https://b.lucq.fun/images/screenshot.gif)
Application created by [ThinkJS](http://www.thinkjs.org)
1 在线体验(demo)
-------------
[在线书签管理系统](http://b.lucq.fun/ "在线书签管理系统")体验账号test。密码123456。
## Install dependencies
2 为什么要做个网络书签
------------------
每个浏览器上面都会有个书签可以供你收藏你以后可能还要用到的网址。但是你可能还是会遇到下列问题:
1、如果你重装系统或者换浏览器怎么办
2、如果你有多个浏览器书签该如何整合
3、如何快速搜索保存的书签比如我只想搜索某个时间段保存的书签
4、如果一个分类下面书签过多如何方便快速查看
5、我能不能查看别人收藏的书签
6、在其他地方上网的时候能不能查看我自己的书签
7、如果公用一台电脑如何区分我收藏的跟别人收藏的书签
在线书签管理工具,帮助你快速记录你喜欢的网站,并可以随时随地查看这些站点,而不必拘泥于使用的浏览器。无论在什么地方,只要能接入网络,就能打开属于你自己的网络书签,看到自己收藏的页面网址。
3 主要功能(开发计划)
-------
- [x] 需要注册账号用户。
- [x] 在书签分类里面,可以更新分类,删除分类,新增分类,对分类显示进行排序。分类的标签默认按照添加日期展示,但是可以点击表格的标题,按照点击次数,添加日期,最后点击从大到小进行排序。
- [x] 可以按照指定添加时间段,指定分类目录,指定网址关键字等进行查询。
- [x] 添加书签的时候会自动获取title供用户编辑。其中Insert键打开添加页面再次按Insert键保存书签Esc取消添加。
- [x] 可以导入Chrome的书签导出文件暂时做在设置里面。
- [x] 书签可以作为公有或者私有,公有可供所有人搜索。
- [x] 可以将搜索到其他用户的书签转存为自己的书签。
- [x] 可以将书签导出来,然后导入到浏览器。
- [x] 在热门标签里面,有在网上找的热门书签。
- [x] 新增备忘录功能有时候随手要做点纪录就方便了。任意界面按快捷键A增加备忘录。双击备忘录可查看详情亦可分享备忘。
- [x] 在设置的全局链接,可设置快捷键,用来在任何页面,快速打开设置的链接。
- [x] 增加[Chrome插件](https://chrome.google.com/webstore/detail/%E4%B9%A6%E7%AD%BE%E5%BF%AB%E9%80%9F%E6%B7%BB%E5%8A%A0/lmmobgephofdffmaednjooplcpbgbjle),可在任意界面快速添加书签至系统。如果你无法访问该插件,可以按照[Chrome如何安装插件开发版本/自制)](https://jingyan.baidu.com/article/f3ad7d0f58d6b609c3345b80.html)方法安装插件,插件请到[bookmark-plugin](https://github.com/luchenqun/bookmark-plugin)下载。
- [x] 适配手机平板,手机端请访问[mb.lucq.fun](http://mb.lucq.fun/)。
4 主要用到的软件与模块说明
------------------
**Node.js**`v12.13.0` 用来做后台服务。
**MySQL**: `v5.7.23`用来做数据存储。
**AngularJS**`v1.5.8`前端JavaScript框架。
**jQuery**: `v3.1.1`本来用了AngularJS是不需要再使用jQuery了的。但是有些功能AngularJS要大费周章才能完成jQuery一句代码就能解决。所以还是忍不住将它导入了进来。
**Semantic**`v2.4.0`由于没有美工人员自己开发的不想界面太丑用了这套UI。
5 目录结构
---------
```
my-bookmark/
├── development.js # 开发环境下的入口文件
├── logs/ # 日志目录
├── Dockerfile # Dockerfile 构建文件
├── nginx.conf # nginx 配置文件
├── package.json # 项目依赖包
├── pm2.json # pm2 配置文件
├── production.js # 生产环境下的入口文件
├── runtime/ # 后台运行文件夹
├── schema.sql # mysql数据库建表文件
├── src/ # 后台实现文件夹
│   ├── bootstrap/ # 启动自动执行目录
│   │   ├── master.js # Master 进程下自动执行
│   │   └── worker.js # Worker 进程下自动执行
│   ├── config/ # 后台配置文件夹
│   │   ├── adapter.js # 后台适配器文件
│   │   ├── config.js # 后台配置文件
│   │   ├── config.production.js # 后台生产环境配置文件
│   │   ├── extend.js # 后台extend配置文件
│   │   ├── middleware.js # 后台middleware配置文件
│   │   └── router.js # 自定义路由配置文件
│   ├── controller/ # 后台控制器文件夹
│   │   ├── api.js # 后台api控制器实现
│   │   ├── base.js # 后台base控制器实现
│   │   └── index.js # 后台index控制器实现
│   ├── logic/ # 后台逻辑文件夹
│   │   ├── api.js # 后台逻辑api文件
│   │   └── index.js # 后台逻辑index文件
│   └── model/ # 后台模型文件夹
│   └── index.js # 后台模型文件
├── test/ # 后台测试文件夹
│   └── index.js # 后台测试文件
├── update.sql # MySQL更新文件
├── view/ # 网站主页显示文件夹
│   ├── 404.html # 默认404页面
│   ├── css/ # 样式表文件夹
│   │   ├── externe/ # 外部引入引来的css文件
│   │   └── style.css # 自己写的css文件
│   ├── favicon.ico # 网站favicon
│   ├── images/ # 图片文件夹
│   ├── scripts/ # 前端逻辑实现的JS文件以及引入的JS文件
│   │   ├── app-angular.js # AngularJS路由配置文件
│   │   ├── controllers/ # 所有的AngularJS控制器
│   │   ├── directives/ # 所有的AngularJS指令
│   │   ├── externe/ # 外部引入的JS文件
│   │   └── services/ # 所有的AngularJS服务文件
│   ├── views/ # 页面实现文件
│   └── index.html # 网站主页
└── README.md # 项目工程说明文件
```
6 Docker安装部署
-------------
此部署方式适合新手。
如果你的Linux环境中没有安装Docker环境。那么请先执行如下命令安装Docker环境。
```
npm install
curl -fsSL get.docker.com -o get-docker.sh
sudo sh get-docker.sh --mirror Aliyun
```
## Start server
安装好docker环境之后执行命令 `docker run -d -p 2000:2000 -p 3306:3306 luchenqun/mybookmark` 安装并启动应用即可。然后在浏览器输入: `http://你的IP:2000/` 即可访问书签应用。安装好的环境默认了一个账号`test`,密码为`123456`。
如果MySQL需要远程访问那么你需要进入容器之后更新 `/etc/mysql/mysql.conf.d/mysqld.cnf`,将绑定地址 `127.0.0.1` 改为 `0.0.0.0`。然后执行命令`service mysql restart`重启数据库服务。安装后的 MySQL默认有两个账户一个是root账户无密码。一个是在文件`/etc/mysql/debian.cnf`有个账号密码。当然这些账号都是只能在本地访问的,你需要手动创建一个可供远程访问的账号。
另外有人做了arm架构的docker如果有需要的请按如下命令执行安装
```
npm start
docker run -itd --name mybookmark -p 2000:2000 -p 3306:3306 740162752/bookmark
```
## Deploy with pm2
Use pm2 to deploy app on production enviroment.
7 安装部署指南
-------------
这种适合动手能力比较强的人员。
1、安装MySQL数据库。如果不会请戳教程[MySQL 数据库安装教程](http://baidu.lucq.fun/?q=TXlTUUwg5pWw5o2u5bqT5a6J6KOF5pWZ56iL "mysql 数据库安装教程")。有点需要注意的是MySQL的版本至少要是5.6。否则执行schema.sql文件会出错。
2、新建一个数据库名使用MySQL将根目录下面的schema.sql文件执行一遍创建数据库表格。有个问题尤其要注意**数据库一定要使用UTF-8的编码**否则执行一些汉字的sql语句会出错如果是Ubuntu大概过程如下。
```
pm2 startOrReload pm2.json
```
mysql -u root -p // 使用root账号进入mysql数据库。按回车之后输入安装时候root的密码。
CREATE DATABASE mybookmarks DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; // 创建mybookmarks数据库。
CREATE USER 'test'@'%' IDENTIFIED BY '123456';// 创建一个以用户名为test密码为123456的用户
GRANT ALL ON *.* TO 'test'@'%'; // 给刚创建的test用户数据库所有的权限
use mybookmarks; //选择刚创建的数据库。
source /home/lcq/schema.sql; // 执行schema.sql文件创建数据库表格。注意将路径换为你schema.sql所在路径。
```
3、如果你是全新部署你可忽略此步骤。如果之前部署过此应用那么需要执行update.sql文件需要升级。注意升级之前请务必备份数据库确认是否需要运行此升级sql文件也很简单看一下你之前的数据库mybookmarks下面有没有`tags_bookmarks`这个数据表。如果有,那么需要执行。执行方法还是如上类似`source /home/lcq/update.sql;`。
4、安装Node.js。Node.js版本至少要求12.0以上。不会的话请按照上面步骤1提供的方法自行解决。
5、克隆代码`git clone git@github.com:luchenqun/my-bookmark.git`,切换到项目根目录下面,执行`npm install`安装package。
6、在根目录更新`pm2.json`文件,只需要更新`cwd`项即可。该项为你项目所在的路径。更新`src/config/adapter.js`下面`exports.model`关于你的MySQL的账号密码信息。注意该账号必须要有写数据库的权限
7、如果上面的都做好了执行命令`npm install pm2 -g`安装pm2模块。再执行命令`pm2 startOrReload pm2.json`。以后如果项目代码有升级,更新代码之后,执行此命令即可重启该应用。
8、在浏览器里面输入`http://你的IP:2000/`。
9、如果需要域名部署的话推荐使用nginx作为HTTP和反向代理服务器根目录有一份`nginx.conf`文件,你只需要更新`root`项即可使用。相关知识,请自行百度。
8 其他说明
---------
1、我没有做浏览器兼容测试只在Google Chrome下面进行了测试开发。
9 开源许可证
-----------
[MIT License](http://www.opensource.org/licenses/MIT)
你可以随意使用此项目,无需通知我,因为我可能很忙没时间。注意,手机版当前没开源

View File

@ -1,8 +1,8 @@
server {
listen 80;
server_name book.mybookmark.cn;
server_name b.lucq.fun;
root /var/www/my-bookmark;
set $node_port 8360;
set $node_port 2000;
index index.js index.html index.htm;
if ( -f $request_filename/index.html ){

View File

@ -13,6 +13,7 @@
"cheerio": "^1.0.0-rc.3",
"fs-extra": "^9.0.0",
"node-readability": "^3.0.0",
"request": "^2.88.2",
"think-cache": "^1.0.0",
"think-cache-file": "^1.0.8",
"think-logger3": "^1.0.0",

View File

@ -1,6 +1,8 @@
CREATE DATABASE IF NOT EXISTS mybookmarks DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 创建mybookmarks数据库
USE mybookmarks;
-- 用户信息表
drop table if exists users;
CREATE TABLE `users` (
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`username` varchar(255) NOT NULL, -- 用户名
`password` varchar(255) NOT NULL, -- 密码
@ -9,15 +11,14 @@ CREATE TABLE `users` (
`lastLogin` datetime DEFAULT now(), -- 最后一次登录时间
`searchHistory` varchar(512) DEFAULT NULL, -- 历史搜索记录
`avatar` varchar(512) DEFAULT NULL, -- 头像地址
`quickUrl` varchar(2048) DEFAULT '{\"B\":\"https://www.baidu.com/\",\"G\":\"https://www.google.com.hk/\",\"V\":\"https://www.v2ex.com/\",\"L\":\"http://luchenqun.com/\",\"H\":\"https://github.com/\",\"Q\":\"http://www.iqiyi.com/\",\"J\":\"https://www.jd.com/\"}', -- 全局快捷地址
`quickUrl` varchar(2048) DEFAULT '{\"B\":\"https://www.baidu.com/\",\"G\":\"https://www.google.com.hk/\",\"H\":\"https://github.com/\"}', -- 全局快捷地址
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
);
-- 书签表
drop table if exists bookmarks;
CREATE TABLE `bookmarks` (
CREATE TABLE IF NOT EXISTS `bookmarks` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`userId` int(11) NOT NULL, -- 用户id
`tagId` int(11) NOT NULL, -- 分类id (只允许一个书签对应一个分类)
@ -33,8 +34,7 @@ CREATE TABLE `bookmarks` (
);
-- 书签分类表
drop table if exists tags;
CREATE TABLE `tags` (
CREATE TABLE IF NOT EXISTS `tags` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`userId` int(11) NOT NULL, -- 用户id
`name` varchar(32) NOT NULL, -- 标签
@ -47,8 +47,7 @@ CREATE TABLE `tags` (
);
-- 建议留言
drop table if exists advices;
CREATE TABLE `advices` (
CREATE TABLE IF NOT EXISTS `advices` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`userId` int(11) NOT NULL, -- 用户id
`comment` text NOT NULL, -- 评论
@ -59,8 +58,7 @@ CREATE TABLE `advices` (
);
-- 热门表
drop table if exists hot_bookmarks;
CREATE TABLE `hot_bookmarks` (
CREATE TABLE IF NOT EXISTS `hot_bookmarks` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id(articleId)
`title` varchar(255) DEFAULT NULL, -- 标题(title)
`url` varchar(1024) DEFAULT NULL, -- 链接(url)
@ -69,18 +67,17 @@ CREATE TABLE `hot_bookmarks` (
`createdAt` datetime DEFAULT now(), -- 创建时间(updatetime)
`lastClick` datetime DEFAULT now(), -- 最后一次点击时间(createtime)
`snap` varchar(1024) DEFAULT NULL, -- 截图链接(imageList[0])
`icon` varchar(1024) DEFAULT NULL -- icon链接(sourceLogo)
`icon` varchar(1024) DEFAULT NULL, -- icon链接(sourceLogo)
PRIMARY KEY (`id`)
);
-- 备忘录
drop table if exists notes;
CREATE TABLE `notes` (
CREATE TABLE IF NOT EXISTS `notes` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`userId` int(11) NOT NULL, -- 用户id
`userId` int(11) NOT NULL, -- 用户id
`content` text NOT NULL, -- 备忘内容
`tagId` int(11) NOT NULL, -- 分类id
`createdAt` datetime DEFAULT now(), -- 创建时间
`tagId` int(11) NOT NULL, -- 分类id
`createdAt` datetime DEFAULT now(), -- 创建时间
`public` tinyint(4) DEFAULT '0', -- 是否公开 1 公开0 不公开
PRIMARY KEY (`id`)
);

View File

@ -1 +1,18 @@
// invoked in worker
const { execSync } = require('child_process');
const os = require("os");
think.beforeStartServer(async () => {
if (os.platform().startsWith("linux")) {
try {
execSync('service mysql start', { stdio: ['inherit', 'inherit', 'inherit'] });
} catch (error) {
console.log("beforeStartServer", error);
}
}
const username = 'test';
let user = await think.model("users").where({ username }).find();
if (think.isEmpty(user)) {
await think.model("users").add({ username, password: 'e10adc3949ba59abbe56e057f20f883e', email: 'ilovejiajia@qq.com' });
}
})

View File

@ -66,7 +66,7 @@ exports.session = {
tokenName: 'authorization', // if tokenType not 'cookie', this will be token name, 'jwt' is default 后端字母要小写
sign: {
// sign options is not required
expiresIn: '604800s' // 7天过期
expiresIn: '2592000s' // 30天过期
},
verify: {
// verify options is not required

46
src/config/crontab.js Normal file
View File

@ -0,0 +1,46 @@
const request = require("request");
module.exports = [{
interval: 1000 * 60 * 10, // 10分钟抓取一次
immediate: true,
handle: async () => {
let form = {
userId: null,
lastupdataTime: new Date().getTime(),
pageNo: 1,
pageSize: 1000,
sort: "desc",
renderType: 0,
date: think.datetime(new Date(), "YYYY年MM月DD日"),
idfa: "d4995f8a0c9b2ad9182369016e376278",
os: "ios",
osv: "9.3.5"
};
const url = "https://api.shouqu.me/api_service/api/v1/daily/dailyMark";
request.post({ url, form }, async function (error, response, body) {
if (!error && response && response.statusCode == 200) {
let data = JSON.parse(body).data;
let list = data.list;
let dataList = [];
for (let item of list) {
if (item.images_upd.indexOf(',http') >= 0) {
item.images_upd = item.images_upd.split(',http')[0];
}
dataList.push({
id: item.articleId,
title: item.title,
url: item.url,
clickCount: item.favCount,
tagName: item.sourceName,
createdAt: think.datetime(item.updatetime > item.createtime ? item.createtime : item.updatetime),
lastClick: think.datetime(item.updatetime <= item.createtime ? item.createtime : item.updatetime),
snap: item.images_upd.replace("http://", "https://"),
icon: item.sourceLogo.replace("http://", "https://")
})
}
await think.model('hot_bookmarks').addMany(dataList, { replace: true });
}
})
}
}]

View File

@ -13,8 +13,8 @@ module.exports = [
handle: 'resource',
enable: true,
options: {
root: path.join(think.ROOT_PATH, 'www'),
publicPath: /^\/(scripts|css|views|images|favicon\.ico)/
root: path.join(think.ROOT_PATH, 'view'),
publicPath: /^\/(scripts|css|views|images|admin\.jpg|favicon\.ico)/
}
},
{

View File

@ -11,7 +11,7 @@ function md5(str) {
module.exports = class extends Base {
async __before() {
if (['userRegister', 'userLogin', 'noteShare'].indexOf(this.ctx.action) >= 0) {
if (['userRegister', 'userLogin', 'noteShare', 'bookmarkDownload', 'hotBookmarks', 'hotBookmarksRandom'].indexOf(this.ctx.action) >= 0) {
return;
}
try {
@ -59,6 +59,7 @@ module.exports = class extends Base {
username: user.username
});
user.token = token;
await this.model('users').where({ id: user.id }).update({ lastLogin: ['exp', 'NOW()'] });
this.json({ code: 0, data: user, msg: "登陆成功" });
}
} catch (error) {
@ -210,6 +211,17 @@ module.exports = class extends Base {
let bookmark = this.post();
bookmark.userId = this.ctx.state.user.id;
try {
let bookmarkFind = await this.model('bookmarks').where({ userId: this.ctx.state.user.id, url: bookmark.url }).find();
if (!think.isEmpty(bookmarkFind)) {
await this.model('bookmarks').where({
userId: this.ctx.state.user.id,
id: bookmarkFind.id
}).update({
createdAt: ['exp', 'NOW()']
});
this.json({ code: 0, data: bookmarkFind, msg: `书签 ${bookmark.title} 已存在,更新创建日期!` });
return
}
// 没有分类的直接放未分类里面
if (!bookmark.tagId) {
const name = "未分类";
@ -225,6 +237,10 @@ module.exports = class extends Base {
}
}
let data = await this.model("bookmarks").add(bookmark);
await this.model('tags').where({
userId: this.ctx.state.user.id,
id: bookmark.tagId
}).update({ lastUse: think.datetime(new Date()) });
this.json({ code: 0, data, msg: `书签 ${bookmark.title} 添加成功` });
} catch (error) {
this.json({ code: 1, data: '', msg: error.toString() });
@ -265,13 +281,13 @@ module.exports = class extends Base {
if (page == 0 && tagId == -1) {
let count = await this.model('bookmarks').where(condition).count('id');
let totalPages = Math.ceil(count / pageSize);
// 按照 2:2:1取数据
let length = Math.ceil(pageSize * 2 / 5);
// 按照 1:1取数据
let length = Math.ceil(pageSize / 2);
let bookmarks = await this.model('bookmarks').where(condition).order('createdAt DESC').limit(0, length).select(); // 这个取一半
// 取最近点击部分数据
let cnt = 0;
let bookmarks2 = await this.model('bookmarks').where(condition).order('lastClick DESC').limit(0, pageSize * 2).select(); // 这个多取一点,有可能跟上面的重复了
let bookmarks2 = await this.model('bookmarks').where(condition).order('lastClick DESC').limit(0, pageSize * 4).select(); // 这个多取一点,有可能跟上面的重复了
for (const bookmark of bookmarks2) {
let find = bookmarks.find(item => item.id == bookmark.id);
if (!find) {
@ -281,16 +297,6 @@ module.exports = class extends Base {
}
}
// 取点击次数最多部分
let bookmarks3 = await this.model('bookmarks').where(condition).order('clickCount DESC').limit(0, pageSize * 2).select(); // 这个多取一点,有可能跟上面的重复了
for (const bookmark of bookmarks3) {
let find = bookmarks.find(item => item.id == bookmark.id);
if (!find) {
bookmarks.push(bookmark);
if (bookmarks.length >= pageSize) break;
}
}
data = {
count,
totalPages,
@ -340,13 +346,13 @@ module.exports = class extends Base {
}
try {
let data = await this.model(tableName).where(condition).page(this.get('page') || 1, this.get('pageSize') || 20).countSelect();
let data = await this.model(tableName).where(condition).order('createdAt DESC').page(this.get('page') || 1, this.get('pageSize') || 20).countSelect();
if (tableName == "bookmarks") {
let ids = [];
for (let bookmark of data.data) {
ids.push(bookmark.tagId);
}
let tags = await this.model('tags').where({ id: ['IN', ids] }).select();
let tags = ids.length > 0 ? await this.model('tags').where({ id: ['IN', ids] }).select() : [];
for (let bookmark of data.data) {
bookmark.tagName = (tags.find((tag) => tag.id == bookmark.tagId) || { name: "未知分类" }).name;
}
@ -420,97 +426,248 @@ module.exports = class extends Base {
// path: 'C:\\Users\\lucq\\AppData\\Local\\Temp\\upload_4ae3b14dacaa107076d3bddd471ebe39.html',
// name: 'exportbookmark-lcq-20200402084709.html',
// type: 'text/html',
const file = this.file("file");
let bookmarks = [];
const getRootFolder = function (body) {
let h3 = body.find("h3").first();
// let isChrome = typeof h3.attr("personal_toolbar_folder") === "string";
// let isIE = typeof h3.attr("item_id") === "string";
// let isFireFox = h3.text() === "Mozilla Firefox";
let isSafari = typeof h3.attr("folded") === "string";
return isSafari ? body : body.children("dl").first();
};
let now = new Date().getTime();
let fileName = 'uploadbookmark-' + this.ctx.state.user.username + '-' + now + '.html';
if (file) {
const filePath = path.join(think.ROOT_PATH, `runtime/upload/${fileName}`);
await fs.ensureDir(path.dirname(filePath));
await fs.move(file.path, filePath);
let data = await fs.readFile(filePath);
let $ = cheerio.load(data.toString());
const parseByString = function (content) {
let $ = cheerio.load(content, { decodeEntities: false });
let body = $("body");
let root = [];
let rdt = getRootFolder(body).children("dt");
let parseNode = function (node) {
let eq0 = node.children().eq(0);
let title = eq0.html() || "无标题";
let type = "site";
let href = "";
let attrCreatedAt = "";
let attrLastClick = "";
let attrClickCount = "";
let anchors = $("dl").find("a");
anchors.each(async (i, e) => {
let url = $(e).attr("href");
let title = $(e).text() || "无标题";
let children = [];
switch (eq0[0].name) {
case "h3":
// folder
type = "folder";
let dl = node.children("dl").first();
let dts = dl.children();
let ls = dts.toArray().map(function (ele) { return ele.name !== "dt" ? null : parseNode($(ele)); });
children = ls.filter(function (item) { return item !== null; });
case "a":
// site
href = eq0.attr("href") || "";
attrCreatedAt = eq0.attr("add_date");
attrLastClick = eq0.attr("last_click");
attrClickCount = eq0.attr("click_count");
}
// 处理name
if (title.length > 255) {
title = title.substring(255);
}
title = title.replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/gi, "");
let attrCreatedAt = $(e).attr("add_date");
let attrLastClick = $(e).attr("last_click");
let attrClickCount = $(e).attr("click_count");
let createdAt = think.datetime(attrCreatedAt ? parseInt(attrCreatedAt) * 1000 : new Date());
let lastClick = think.datetime(attrLastClick ? parseInt(attrLastClick) * 1000 : new Date());
let clickCount = attrClickCount ? parseInt(attrClickCount) : 1;
// 只允许用一个标签
let tagName = "未分类";
$(e).parents("dl").each(function (ii, ee) {
let folder = $(ee).prev();
let temp = folder.text().replace(/(^\s*)|(\s*$)/g, '').replace(/\s+/g, ' ');
if (temp != "Bookmarks" && temp != "书签栏" && temp != "" && temp != undefined) { tagName = temp; }
});
bookmarks.push({ title, url, createdAt, lastClick, tagName, clickCount, userId: this.ctx.state.user.id })
return {
title,
type,
url: href,
createdAt: think.datetime(attrCreatedAt ? parseInt(attrCreatedAt) * 1000 : new Date()),
lastClick: think.datetime(attrLastClick ? parseInt(attrLastClick) * 1000 : new Date()),
clickCount: attrClickCount ? parseInt(attrClickCount) : 1,
children: children
};
};
rdt.each(function (_, item) {
let node = $(item);
let child = parseNode(node);
root.push(child);
});
return root;
};
const parseByPath = function (path) {
var content = fs.readFileSync(path, 'utf-8');
return parseByString(content);
};
const userId = this.ctx.state.user.id;
const flatBookmarks = (originBookmarks, tagName, bookmarks) => {
for (let bookmark of originBookmarks) {
if (bookmark.type == "site") {
bookmarks.push({
title: bookmark.title,
url: bookmark.url,
createdAt: bookmark.createdAt,
lastClick: bookmark.lastClick,
tagName,
clickCount: bookmark.clickCount,
userId
});
} else if (bookmark.type == "folder") {
flatBookmarks(bookmark.children, tagName == '未分类' ? bookmark.title : tagName, bookmarks);
}
}
}
let bookmarks = [];
const file = this.file("file");
let fileName = 'uploadbookmark-' + this.ctx.state.user.username + '-' + think.datetime(new Date(), "YYYYMMDDHHmmss") + '.html';
if (file) {
const filePath = path.join(think.ROOT_PATH, `runtime/upload/${fileName}`);
await fs.ensureDir(path.dirname(filePath));
await fs.move(file.path, filePath);
let originBookmarks = parseByPath(filePath);
Array.isArray(originBookmarks) && originBookmarks.length >= 0 && (originBookmarks[0].title = "未分类");
flatBookmarks(originBookmarks, originBookmarks[0].title, bookmarks); // 传上来的树级目录改为只有一级目录
}
let count = 0;
let repeat = 0;
let tags = await this.model('tags').where({ userId: this.ctx.state.user.id }).select();
let fail = 0;
let failStr = "";
let tags = await this.model("tags").where({ userId: this.ctx.state.user.id }).select();
for (let bookmark of bookmarks) {
let find = await this.model('bookmarks').where({ userId: this.ctx.state.user.id, url: bookmark.url }).find();
let find = await this.model("bookmarks").where({ userId: this.ctx.state.user.id, url: bookmark.url }).find();
if (think.isEmpty(find)) {
let tag = tags.find(item => item.name == bookmark.tagName);
let tag = tags.find((item) => item.name == bookmark.tagName);
if (tag) {
bookmark.tagId = tag.id;
} else {
bookmark.tagId = await this.model("tags").add({ userId: this.ctx.state.user.id, name: bookmark.tagName });
tags.push({
id: bookmark.tagId,
name: bookmark.tagName
})
name: bookmark.tagName,
});
}
delete bookmark.tagName;
await this.model("bookmarks").add(bookmark);
try {
await this.model("bookmarks").add(bookmark);
} catch (error) {
fail++;
failStr += bookmark.title + ",";
}
count++;
} else {
repeat++;
}
}
this.json({ code: 0, data: count, msg: `书签传入${bookmarks.length}个,重复书签${repeat}个,成功导入${count}个。` });
this.json({ code: 0, data: count, msg: `书签传入${bookmarks.length}个,重复书签${repeat}个,${fail}个导入失败:${failStr}成功导入${count}个。` });
}
// 书签备份
async bookmarkBackupAction() {
let init = '<TITLE>Bookmarks</TITLE><H1>Bookmarks</H1><DL id="0"></DL>';
let $ = cheerio.load(init, {
decodeEntities: false,
xmlMode: true,
});
const sample =
`<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
<DT><H3 ADD_DATE="1606958496" LAST_MODIFIED="1622450430" PERSONAL_TOOLBAR_FOLDER="true">书签栏</H3>
<DL><p>
<DT><H3 ADD_DATE="1622427860" LAST_MODIFIED="1622450436">JavaScript</H3>
<DL><p>
<DT><A HREF="https://github.com/luchenqun/my-bookmark/issues" ADD_DATE="1622427872">Issues · luchenqun/my-bookmark</A>
<DT><A HREF="https://mail.google.com/mail/u/0/#inbox" ADD_DATE="1622450430">收件箱 - lcq530485521@gmail.com - Gmail</A>
</DL><p>
</DL><p>
</DL><p>`
let time = (date) => parseInt(new Date(date).getTime() / 1000); // 日期转时间
let now = new Date();
let left = `<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
<DT><H3 ADD_DATE="${time(now)}" LAST_MODIFIED="${time(now)}" PERSONAL_TOOLBAR_FOLDER="true">书签栏</H3>
<DL><p>\n`;
let middle = '';
let right = ` </DL><p>
</DL><p>`;
let tags = await this.model('tags').where({ userId: this.ctx.state.user.id }).order('sort ASC, lastUse DESC').select();
for (const tag of tags) {
$('#0').append(`<DT><H3>${tag.name}</H3></DT><DL id="${tag.id}"></DL>`);
let tagStr = ` <DT><H3 ADD_DATE="${time(tag.lastUse)}" LAST_MODIFIED="${time(tag.lastUse)}">${tag.name}</H3>\n <DL><p>\n`;
let bookmarks = await this.model('bookmarks').where({ tagId: tag.id }).select();
for (const bookmark of bookmarks) {
$('#' + tag.id).append(`<DT><A HREF="${bookmark.url}" ADD_DATE="${time(bookmark.createdAt)}" LAST_CLICK="${time(bookmark.lastClick)}" CLICK_COUNT="${bookmark.clickCount}" >${bookmark.title}</A></DT>`)
tagStr += ` <DT><A HREF="${bookmark.url}" ADD_DATE="${time(bookmark.createdAt)}" LAST_CLICK="${time(bookmark.lastClick)}" CLICK_COUNT="${bookmark.clickCount}">${bookmark.title}</A>\n`
}
tagStr += ` </DL><p>\n`;
middle += bookmarks.length > 0 ? tagStr : '';
}
let now = new Date().getTime()
let fileName = 'exportbookmark-' + this.ctx.state.user.username + '-' + now + '.html';
let fileName = 'exportbookmark-' + this.ctx.state.user.username + '-' + think.datetime(new Date(), "YYYYMMDDHHmmss") + '.html';
let filePath = path.join(think.ROOT_PATH, 'runtime', 'backup', fileName);
await fs.ensureFile(filePath);
await fs.writeFile(filePath, $.xml());
this.json({ code: 0, data: `runtime/backup/${fileName}` });
setTimeout(() => fs.remove(filePath), 10000);
// await this.download(filePath, fileName)
await fs.writeFile(filePath, left + middle + right);
this.json({ code: 0, data: fileName });
setTimeout(async () => {
let exists = await fs.pathExists(filePath);
if (exists) {
await fs.remove(filePath);
}
}, 1000 * 60 * 10); // 十分钟内没下载就给删掉
}
async bookmarkDownloadAction() {
let fileName = this.get('fileName');
let filePath = path.join(think.ROOT_PATH, 'runtime', 'backup', fileName);
let exists = await fs.pathExists(filePath);
if (exists) {
await this.download(filePath);
await fs.remove(filePath);
} else {
this.body = "文件不存在!";
}
}
// 获取收趣的热门书签
async hotBookmarksAction() {
let page = this.get('page');
let pageSize = parseInt(this.get('pageSize') || 50);
try {
// 如果是第0页而且是个人定制的把 最近点击 与 最近新增 的返回去。
let data = {};
data = await this.model('hot_bookmarks').order('id DESC').page(page || 1, pageSize).countSelect();
for (let bookmark of data.data) {
if (!bookmark.tagName) {
bookmark.tagName = "未知";
}
}
this.json({ code: 0, data });
} catch (error) {
this.json({ code: 1, msg: error.toString() });
}
}
// 随机获取收趣的热门书签
async hotBookmarksRandomAction() {
try {
let sql = `SELECT * FROM hot_bookmarks ORDER BY RAND() LIMIT 50;`;
let data = await this.model('hot_bookmarks').query(sql);
for (let bookmark of data) {
if (!bookmark.tagName) {
bookmark.tagName = "未知";
}
}
this.json({ code: 0, data });
} catch (error) {
this.json({ code: 1, msg: error.toString() });
}
}
// 获取文章
@ -550,6 +707,9 @@ module.exports = class extends Base {
async adviceAddAction() {
let advice = this.post();
advice.userId = this.ctx.state.user.id;
if (this.ctx.state.user.username == 'test') {
return this.json({ code: 400, data: '', msg: `Test user forbid advice!` });
}
try {
let res = await this.model("advices").add(advice);
this.json({ code: 0, data: res, msg: `留言 添加成功` });
@ -573,6 +733,21 @@ module.exports = class extends Base {
let note = this.post();
note.userId = this.ctx.state.user.id;
try {
// 没有分类的直接放未分类里面
if (!note.tagId) {
const name = "未分类";
let tag = await this.model("tags").where({ name }).find();
if (!think.isEmpty(tag)) {
note.tagId = tag.id;
} else {
let tagId = await this.model("tags").add({
userId: this.ctx.state.user.id,
name
});
note.tagId = tagId;
}
}
let data = await this.model("notes").add(note);
this.json({ code: 0, data, msg: `备忘添加成功` });
} catch (error) {
@ -626,24 +801,29 @@ module.exports = class extends Base {
async noteShareAction() {
let id = this.get("id");
let json = this.get("json");
let note = await this.model('notes').where({ id, public: 1 }).find();
let body = think.isEmpty(note) ? "备忘为非公开或者已删除!" : note.content;
this.body = `<body style="margin:0px;height:100%;">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<script>
if(screen && screen.availWidth <= 1024) {
setTimeout(() => {
document.getElementById("note-div").style.width = "100%";
document.getElementById("note-div").style["background-color"] = "#F3F4F5";
document.getElementById("note").style.width = "95%";
}, 100);
}
</script>
</head>
<div id="note-div" style="text-align:center;">
<pre id="note" style="background-color:RGB(243,244,245); padding:0px 10px 0px 10px; margin:0px; width:60%; min-height:100%;display: inline-block;text-align: left; font-size: 15px; font-family:italic arial,sans-serif;word-wrap: break-word;white-space: pre-wrap;">\n\n${body}\n\n</pre>
</div>
</body>`;
if (json) {
this.json(JSON.parse(note.content))
} else {
let body = think.isEmpty(note) ? "备忘为非公开或者已删除!" : note.content;
this.body = `<body style="margin:0px;height:100%;">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<script>
if(screen && screen.availWidth <= 1024) {
setTimeout(() => {
document.getElementById("note-div").style.width = "100%";
document.getElementById("note-div").style["background-color"] = "#F3F4F5";
document.getElementById("note").style.width = "95%";
}, 100);
}
</script>
</head>
<div id="note-div" style="text-align:center;">
<pre id="note" style="background-color:RGB(243,244,245); padding:0px 10px 0px 10px; margin:0px; width:60%; min-height:100%;display: inline-block;text-align: left; font-size: 15px; font-family:italic arial,sans-serif;word-wrap: break-word;white-space: pre-wrap;">\n\n${body}\n\n</pre>
</div>
</body>`;
}
}
};

View File

@ -1,7 +1,7 @@
const Base = require('./base.js');
module.exports = class extends Base {
indexAction() {
return this.display();
}
};
const Base = require('./base.js');
module.exports = class extends Base {
async indexAction() {
await this.display("index.html");
}
};

View File

@ -44,10 +44,10 @@ CHANGE COLUMN `fav_count` `clickCount` smallint(6) NULL DEFAULT 1 AFTER `url`,
CHANGE COLUMN `created_by` `tagName` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL AFTER `clickCount`,
CHANGE COLUMN `snap_url` `snap` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL AFTER `last_click`,
CHANGE COLUMN `favicon_url` `icon` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL AFTER `snap`,
ADD COLUMN `createAt` datetime NULL AFTER `tagName`,
ADD COLUMN `lastClick` datetime NULL AFTER `createAt`,
ADD COLUMN `createdAt` datetime NULL AFTER `tagName`,
ADD COLUMN `lastClick` datetime NULL AFTER `createdAt`,
MODIFY COLUMN `id` int(11) NOT NULL AUTO_INCREMENT FIRST;
UPDATE hot_bookmarks SET createAt = FROM_UNIXTIME(created_at/1000), lastClick = FROM_UNIXTIME(last_click/1000);
UPDATE hot_bookmarks SET createdAt = FROM_UNIXTIME(created_at/1000), lastClick = FROM_UNIXTIME(last_click/1000);
ALTER TABLE `hot_bookmarks`
DROP COLUMN `created_at`,
DROP COLUMN `last_click`;

View File

@ -1,84 +1,84 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>提示-404</title>
<style type="text/css">
html {
background: none;
background-color: #f0eeee;
}
body {
padding: 0;
}
#header {
width: 952px;
margin: 0 auto;
height: 120px;
background: none;
box-shadow: none;
position: static;
}
#content {
font-family: "微软雅黑";
background: url("images/404.png") no-repeat 122px 0px;
width: 952px;
margin: 0 auto;
font-size: 18px;
color: #999;
}
#content p {
padding: 180px 0 0 426px;
line-height: 38px;
}
#content p a.backhome {
font-size: 18px;
}
#content p a.backhome span {
width: 80px;
}
#totalSecond {
color: #f00;
}
</style>
</head>
<body>
<div>
<div id="content" style="height: 530px;">
<p>
对不起,您的风筝已掉线,请时光倒流回前一秒。<br />
<span id="totalSecond">5</span>秒后自动
<a href="https://mybookmark.cn" class="backhome"><span>返回首页</span></a>
</p>
</div>
</div>
</body>
<script language="javascript" type="text/javascript">
var second = document.getElementById("totalSecond").textContent;
// 判断是IE浏览器还是Firefox浏览器采用相应措施取得秒数
if (navigator.appName.indexOf("Explorer") > -1) {
second = document.getElementById("totalSecond").innerText;
} else {
second = document.getElementById("totalSecond").textContent;
}
setInterval(function() {
if (second < 0) {
location.href = document.location.origin;
} else {
if (navigator.appName.indexOf("Explorer") > -1) {
document.getElementById("totalSecond").innerText = second--;
} else {
document.getElementById("totalSecond").textContent = second--;
}
}
}, 1000);
</script>
</html>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>提示-404</title>
<style type="text/css">
html {
background: none;
background-color: #f0eeee;
}
body {
padding: 0;
}
#header {
width: 952px;
margin: 0 auto;
height: 120px;
background: none;
box-shadow: none;
position: static;
}
#content {
font-family: "微软雅黑";
background: url("images/404.png") no-repeat 122px 0px;
width: 952px;
margin: 0 auto;
font-size: 18px;
color: #999;
}
#content p {
padding: 180px 0 0 426px;
line-height: 38px;
}
#content p a.backhome {
font-size: 18px;
}
#content p a.backhome span {
width: 80px;
}
#totalSecond {
color: #f00;
}
</style>
</head>
<body>
<div>
<div id="content" style="height: 530px">
<p>
对不起,您的风筝已掉线,请时光倒流回前一秒。<br />
<span id="totalSecond">5</span>秒后自动
<a href="https://b.lucq.fun" class="backhome"><span>返回首页</span></a>
</p>
</div>
</div>
</body>
<script language="javascript" type="text/javascript">
var second = document.getElementById("totalSecond").textContent;
// 判断是IE浏览器还是Firefox浏览器采用相应措施取得秒数
if (navigator.appName.indexOf("Explorer") > -1) {
second = document.getElementById("totalSecond").innerText;
} else {
second = document.getElementById("totalSecond").textContent;
}
setInterval(function () {
if (second < 0) {
location.href = document.location.origin;
} else {
if (navigator.appName.indexOf("Explorer") > -1) {
document.getElementById("totalSecond").innerText = second--;
} else {
document.getElementById("totalSecond").textContent = second--;
}
}
}, 1000);
</script>
</html>

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 496 KiB

After

Width:  |  Height:  |  Size: 496 KiB

View File

Before

Width:  |  Height:  |  Size: 382 KiB

After

Width:  |  Height:  |  Size: 382 KiB

View File

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,191 +1,195 @@
body {
background-image: url("../images/bg.png");
background-repeat: repeat;
background-position: top left;
background-attachment: scroll;
padding-top: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
code {
background-color: rgba(0, 0, 0, 0.08);
border-radius: 3px;
display: inline-block;
font-family: "Monaco", "Menlo", "Ubuntu Mono", "Consolas", "source-code-pro", monospace;
font-size: 0.875em;
font-weight: bold;
padding: 1px 6px;
vertical-align: baseline;
}
.bookmark {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
color: #212121;
border: 1px solid transparent;
}
.bookmarkTitle {
color: #333;
font-size: 16px;
line-height: 24px;
display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.bookmarkNormalHover {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
color: #212121;
background: #f5f5f5;
cursor: pointer;
border: 1px solid transparent;
}
.bookmarkOperaterHover {
cursor: pointer;
}
.tags {
cursor: default;
}
.bookmarkEditHover {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
color: #212121;
cursor: default;
border: 1px dashed #3388ff;
}
.bookmarkInfo {
cursor: url("../images/detail.ico"), auto;
}
.img-fixed-size {
width: 16px;
height: 16px;
}
td {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
table {
table-layout: fixed;
}
.wrap {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
}
.js-navigate-bookmark a {
color: #212121;
}
.ui.menu .selected.item {
background: rgba(1, 1, 1, 0.05);
}
img.operator {
width: 16px;
height: 16px;
}
.js-bookmark-info .content img {
max-width: 100%;
max-height: 100%;
padding-right: 20px;
}
.hot-image {
max-width: 100%;
max-height: 160px;
min-height: 160px;
background-color: yellow;
overflow: hidden;
}
.sourceName {
display: inline-block;
position: relative;
top: 4px;
max-width: 55px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 12px;
color: #333;
}
.favCount {
position: relative;
top: 1px;
font-size: 12px;
color: #bfc0c6;
}
.urlSpan {
word-wrap: break-word;
word-break: break-all;
cursor: default;
}
.ui.sortable.table thead th.forbid_sorted:hover {
cursor: default;
background: #f9fafb;
}
.ui.selection.list .list > .item,
.ui.selection.list > .item {
border-radius: 0;
}
.js-p-info p {
margin-bottom: 5px;
font-size: 16px;
}
.note-content {
font: 15px "Lucida Grande", Helvetica, Arial, sans-serif;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
.fontgreen {
color: RGB(33, 186, 69);
}
.fontred {
color: #f00;
}
.js-history-word:hover {
background: #f0f0f0;
}
.js-weixin-content img {
width: 100%;
height: 100%;
display: block;
margin: 0 auto;
}
body {
background-image: url("../images/bg.png");
background-repeat: repeat;
background-position: top left;
background-attachment: scroll;
padding-top: 3px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
code {
background-color: rgba(0, 0, 0, 0.08);
border-radius: 3px;
display: inline-block;
font-family: "Monaco", "Menlo", "Ubuntu Mono", "Consolas", "source-code-pro", monospace;
font-size: 0.875em;
font-weight: bold;
padding: 1px 6px;
vertical-align: baseline;
}
.bookmark {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
color: #212121;
border: 1px solid transparent;
}
.bookmarkTitle {
color: #333;
font-size: 16px;
line-height: 24px;
display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.bookmarkNormalHover {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
color: #212121;
background: #f5f5f5;
cursor: pointer;
border: 1px solid transparent;
}
.bookmarkOperaterHover {
cursor: pointer;
}
.tags {
cursor: default;
}
.bookmarkEditHover {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
color: #212121;
cursor: default;
border: 1px dashed #3388ff;
}
.bookmarkInfo {
cursor: url("../images/detail.ico"), auto;
}
.img-fixed-size {
width: 16px;
height: 16px;
}
td {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
table {
table-layout: fixed;
}
.wrap {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
}
.js-navigate-bookmark a {
color: #212121;
}
.ui.menu .selected.item {
background: rgba(1, 1, 1, 0.05);
}
img.operator {
width: 16px;
height: 16px;
}
.js-bookmark-info .content img {
max-width: 100%;
max-height: 100%;
padding-right: 20px;
}
.hot-image {
max-width: 100%;
max-height: 160px;
min-height: 160px;
background-color: yellow;
overflow: hidden;
}
.sourceName {
display: inline-block;
position: relative;
top: 4px;
max-width: 55px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 12px;
color: #333;
}
.favCount {
position: relative;
top: 1px;
font-size: 12px;
color: #bfc0c6;
}
.urlSpan {
word-wrap: break-word;
word-break: break-all;
cursor: default;
}
.ui.sortable.table thead th.forbid_sorted:hover {
cursor: default;
background: #f9fafb;
}
.ui.selection.list .list > .item,
.ui.selection.list > .item {
border-radius: 0;
}
.js-p-info p {
margin-bottom: 5px;
font-size: 16px;
}
.note-content {
font: 15px "Lucida Grande", Helvetica, Arial, sans-serif;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
.fontgreen {
color: RGB(33, 186, 69);
}
.fontred {
color: #f00;
}
.js-history-word:hover {
background: #f0f0f0;
}
.js-weixin-content img {
width: 100%;
height: 100%;
display: block;
margin: 0 auto;
}
.js-search-input {
font-size: 14px;
}

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 705 B

After

Width:  |  Height:  |  Size: 705 B

View File

Before

Width:  |  Height:  |  Size: 687 B

After

Width:  |  Height:  |  Size: 687 B

View File

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 662 B

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 536 B

After

Width:  |  Height:  |  Size: 536 B

View File

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 264 KiB

View File

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 291 B

View File

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 438 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 347 B

After

Width:  |  Height:  |  Size: 347 B

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 425 B

After

Width:  |  Height:  |  Size: 425 B

View File

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 345 B

View File

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 292 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 249 B

After

Width:  |  Height:  |  Size: 249 B

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 371 B

View File

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 389 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 529 B

After

Width:  |  Height:  |  Size: 529 B

View File

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 438 B

View File

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 320 B

View File

Before

Width:  |  Height:  |  Size: 266 KiB

After

Width:  |  Height:  |  Size: 266 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 522 B

View File

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 318 B

View File

Before

Width:  |  Height:  |  Size: 777 B

After

Width:  |  Height:  |  Size: 777 B

View File

Before

Width:  |  Height:  |  Size: 623 B

After

Width:  |  Height:  |  Size: 623 B

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 805 B

After

Width:  |  Height:  |  Size: 805 B

View File

Before

Width:  |  Height:  |  Size: 808 B

After

Width:  |  Height:  |  Size: 808 B

View File

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 507 B

View File

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -37,6 +37,14 @@
var text = e.text.length >= 180 ? e.text.substr(0, 180) + "..." : e.text;
toastr.error(text + "<br/>复制失败", "提示");
});
if (window.location.hostname.indexOf("mybookmark.cn") >= 0) {
toastr.warning("域名【mybookmark.cn】将于2021年11月份到期届时将不再续费无法访问。后续将使用新域名【http://b.lucq.fun】为您提供服务。5分钟后会自动跳转到新的域名。", "提示");
$(".js-domain").removeClass("hidden");
setTimeout(function () {
window.location = "http://b.lucq.fun/#/tags";
}, 5 * 60 * 1000);
}
};
let resizeContainer = () => {
let count = 1;
@ -53,18 +61,25 @@
</head>
<body ng-app="bookmarkApp">
<div class="ui container" id="js-container" style="position: absolute;left: 86;">
<div class="ui container" id="js-container" style="position: absolute; left: 86;width: 70%;">
<div class="ui error hidden message js-domain" style="margin-bottom: 3px">
<div class="header">域名更换提示!</div>
<ul class="list">
<li>域名【mybookmark.cn】将于2021年11月份到期届时将不再续费无法访问。目前已使用新域名【<a href="http://b.lucq.fun/">http://b.lucq.fun</a>】为您提供服务。</li>
<li>5分钟后会自动跳转到新的域名。<a href="http://b.lucq.fun/">立即跳转</a></li>
</ul>
</div>
<!-- directive菜单 -->
<menus></menus>
<div class="ui container" style="width: 100%; height: 2px;"></div>
<div class="ui container" style="width: 100%; height: 2px"></div>
<div class="ui container">
<div ui-view></div>
</div>
<div class="ui container" style="width: 100%; height: 10px;"></div>
<div class="foot" style="margin-bottom: 10px;">
<div class="ui segment container" style="text-align: center;">我爱佳佳与这个世界&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://m.mybookmark.cn/">移动设备访问</a>&nbsp;&nbsp;|&nbsp;&nbsp;联系我(QQ群1026967226)&nbsp;&nbsp;|&nbsp;&nbsp;<a href="https://github.com/luchenqun/my-bookmark" target="_blank">网站源码</a>&nbsp;&nbsp;|&nbsp;&nbsp;V2.0.0&nbsp;&nbsp;|&nbsp;&nbsp;<span>加载失败,请按 Ctrl + Shift + R 强制刷新!(●'◡'●)</span><br /></div>
<div class="ui container" style="width: 100%; height: 2px"></div>
<div class="foot">
<div class="ui segment container" style="text-align: center">我爱佳佳与这个世界&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://mb.lucq.fun/">移动设备访问</a>&nbsp;&nbsp;|&nbsp;&nbsp;联系我(QQ群1026967226)&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://beian.miit.gov.cn/" target="_blank">粤ICP备18032994号</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="https://github.com/luchenqun/my-bookmark" target="_blank">网站源码</a>&nbsp;&nbsp;|&nbsp;&nbsp;V2.0.0&nbsp;&nbsp;|&nbsp;&nbsp;<span>加载失败,请按 Ctrl + Shift + R 强制刷新!(●'◡'●)</span><br /></div>
<!-- 主要用来配合clipboard.min.js复制文本的 -->
<div id="clipboard" data-clipboard-text="i love this world and jiajia!" style="opacity: 0; cursor: default;">
<div id="clipboard" data-clipboard-text="i love this world and jiajia!" style="opacity: 0; cursor: default">
<span>Copy</span>
</div>
</div>
@ -95,7 +110,7 @@
<script src="scripts/controllers/edit-controller.js"></script>
<script src="scripts/controllers/bookmark-info-controller.js"></script>
<script src="scripts/controllers/search-controller.js"></script>
<script src="scripts/controllers/weixin-article-controller.js"></script>
<script src="scripts/controllers/hot-controller.js"></script>
<script src="scripts/controllers/note-controller.js"></script>
<script src="scripts/externe/semantic.min.js"></script>
<script src="scripts/externe/calendar.min.js"></script>
@ -109,5 +124,10 @@
<script src="scripts/externe/md5.js"></script>
<script src="scripts/externe/pnglib.js"></script>
<script src="scripts/externe/identicon.js"></script>
<style>
.ui.container {
width: 100%;
}
</style>
</body>
</html>

View File

@ -53,10 +53,10 @@ app.config(function ($stateProvider, $urlRouterProvider, $httpProvider) {
templateUrl: 'views/bookmarks.html',
controller: 'bookmarksCtr'
})
.state('weixin-article', {
url: '/weixin-article',
templateUrl: 'views/weixin-article.html',
controller: 'weixinArticleCtr'
.state('hot', {
url: '/hot',
templateUrl: 'views/hot.html',
controller: 'hotCtr'
})
.state('note', {
url: '/note',

View File

@ -0,0 +1,46 @@
app.controller('adviceCtr', ['$scope', '$state', '$timeout', '$window', 'pubSubService', 'dataService', function ($scope, $state, $timeout, $window, pubSubService, dataService) {
console.log("Hello adviceCtr");
console.log($window.location.hostname);
if (dataService.smallDevice()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
$scope.comment = '';
$scope.advices = [];
$scope.user = {};
$scope.loading = false;
pubSubService.subscribe('Common.user', $scope, function (event, user) {
$scope.user = user;
});
getAdvices();
$scope.ok = async function () {
if ($scope.comment == '') {
toastr.error('留言失败内容不能为空', "错误");
} else if ($scope.user.username == 'test') {
toastr.error('test用户不允许留言!', "错误");
} else {
await post('adviceAdd', { comment: $scope.comment });
await getAdvices();
}
}
async function getAdvices() {
$scope.loading = true;
$scope.comment = "";
let advices = await get("advices");
advices.forEach(element => {
element.imgData = new Identicon(md5(element.userId)).toString();
});
$timeout(function () {
$scope.advices = advices;
});
$scope.loading = false;
}
}]);

View File

@ -9,34 +9,9 @@ app.controller('bookmarkInfoCtr', ['$scope', '$state', '$timeout', '$sce', '$win
$('.ui.modal.js-bookmark-info').modal({
closable: false,
}).modal('setting', 'transition', dataService.animation()).modal('show');
bookmark.favicon_url = 'http://favicon.luchenqun.com/?url=' + bookmark.url;
bookmark.favicon_url = 'https://favicon.lucq.fun/?url=' + bookmark.url;
$scope.bookmark = bookmark;
$scope.bookmark.description = $sce.trustAsHtml(bookmark.description);
$scope.content = $sce.trustAsHtml(bookmark.content) || '';
if (!$scope.content) {
$timeout(function () {
$('.ui.modal.js-bookmark-info').modal("refresh");
$("p").css("word-wrap", "break-word");
}, 500);
$scope.loading = true;
try {
let data = get("article", { url: bookmark.url });
$scope.content = data.content ? $sce.trustAsHtml(data.content) : $sce.trustAsHtml('<p>数据获取失败可能是服务器不允许获取或者是https网站</p>');
setTimeout(function () {
$('.ui.modal.js-bookmark-info').modal && $('.ui.modal.js-bookmark-info').modal("refresh");
}, 100);
} catch (error) {
}
$scope.loading = false;
} else {
setTimeout(function () {
$('.ui.modal.js-bookmark-info').modal && $('.ui.modal.js-bookmark-info').modal("refresh");
}, 10);
setTimeout(function () {
$('.modals').animate({ scrollTop: 0 }, 100);
}, 500);
}
});
$scope.jumpToUrl = async function (url, id) {

View File

@ -1,7 +1,11 @@
app.controller('bookmarksCtr', ['$scope', '$state', '$stateParams', '$filter', '$window', '$timeout', '$document', 'ngDialog', 'pubSubService', 'dataService', function ($scope, $state, $stateParams, $filter, $window, $timeout, $document, ngDialog, pubSubService, dataService) {
console.log("Hello bookmarksCtr...", $stateParams);
if (dataService.smallDevice()) {
$window.location = "http://m.mybookmark.cn/#/tags";
return;
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
$state.go('tags');
}]);

View File

@ -105,7 +105,7 @@ app.controller('editCtr', ['$scope', '$state', '$timeout', '$document', 'ngDialo
}
$scope.showAddTag = function () {
if ($scope.tags.length < 30) {
if ($scope.tags.length < 50) {
console.log('showAddTag..........')
$scope.newTag = "";
dialog = ngDialog.open({
@ -114,14 +114,14 @@ app.controller('editCtr', ['$scope', '$state', '$timeout', '$document', 'ngDialo
scope: $scope
});
} else {
toastr.error('标签个数总数不能超过30个不允许再添加新分类如有需求请联系管理员。', "提示");
toastr.error('标签个数总数不能超过50个不允许再添加新分类如有需求请联系管理员。', "提示");
}
}
$scope.addTag = async function (tag) {
console.log(tag);
if ($scope.tags.length >= 30) {
toastr.error('标签个数总数不能超过30个不允许再添加新分类如有需求请联系管理员。', "提示");
if ($scope.tags.length >= 50) {
toastr.error('标签个数总数不能超过50个不允许再添加新分类如有需求请联系管理员。', "提示");
return;
}
@ -188,7 +188,9 @@ app.controller('editCtr', ['$scope', '$state', '$timeout', '$document', 'ngDialo
});
$scope.public = (bookmark && bookmark.id) || '1';
$('.ui.checkbox.js-public').checkbox((bookmark && bookmark.public && bookmark.public == '1') ? 'set checked' : 'set unchecked')
$scope.loadTags = false;
$timeout(function () {
$scope.loadTags = false;
}, 10);
});
pubSubService.subscribe('TagCtr.storeBookmark', $scope, function (event, bookmark) {

View File

@ -0,0 +1,14 @@
app.controller('homeCtr', ['$scope', '$stateParams', '$filter', '$state', '$window', 'pubSubService', 'dataService', function ($scope, $stateParams, $filter, $state, $window, pubSubService, dataService) {
console.log('Hello homeCtr......');
if (dataService.smallDevice()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
pubSubService.subscribe('Common.user', $scope, function (event, user) {
user.id && $state.go('tags');
});
}]);

View File

@ -0,0 +1,181 @@
app.controller('hotCtr', ['$scope', '$state', '$sce', '$filter', '$window', '$timeout', '$document', 'pubSubService', 'dataService', function ($scope, $state, $sce, $filter, $window, $timeout, $document, pubSubService, dataService) {
console.log("Hello hotCtr...");
if (dataService.smallDevice()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
const pageSize = 40;
var timeagoInstance = timeago();
$scope.hoverBookmark = null;
$scope.bookmarks = []; // 书签数据
$scope.bookmark = {};
$scope.bookmarkNormalHover = false;
$scope.bookmarkEditHover = false;
$scope.count = 0;
$scope.totalPages = 0;
$scope.currentPage = 1;
$scope.channelId = 0; // 0 默认是后台抓取的收趣
$scope.inputPage = '';
$scope.loading = false;
$scope.toastrId = 0;
$scope.random = 0;
$scope.channels = JSON.parse(`[{"id":0,"name":"收趣"},{"id":-1,"name":"随机一批"},{"id":1,"name":"热门"},{"id":2,"name":"搞笑"},{"id":3,"name":"养生堂"},{"id":4,"name":"私房话"},{"id":5,"name":"八卦精"},{"id":6,"name":"科技咖"},{"id":7,"name":"财经迷"},{"id":8,"name":"汽车控"},{"id":9,"name":"生活家"},{"id":10,"name":"时尚圈"},{"id":11,"name":"育儿"},{"id":12,"name":"旅游"},{"id":13,"name":"职场"},{"id":14,"name":"美食"},{"id":15,"name":"历史"},{"id":16,"name":"教育"},{"id":17,"name":"星座"},{"id":18,"name":"体育"},{"id":19,"name":"军事"},{"id":20,"name":"游戏"},{"id":21,"name":"萌宠"}]`);
$scope.jumpToUrl = async function (url) {
$window.open(url, '_blank');
}
$scope.favoriteBookmark = async function (b) {
var menusScope = $('div[ng-controller="menuCtr"]').scope();
var login = (menusScope && menusScope.login) || false;
if (!login) {
$scope.toastrId = toastr.info('请先登录再收藏书签!', "提示");
} else {
let bookmark = {}
bookmark.title = b.title;
bookmark.url = b.url;
let id = await post("bookmarkAdd", bookmark);
bookmark = await get("bookmark", { id })
pubSubService.publish('EditCtr.inserBookmarsSuccess', bookmark);
}
}
$scope.storeBookmark = async function (bookmark) {
var menusScope = $('div[ng-controller="menuCtr"]').scope();
var login = (menusScope && menusScope.login) || false;
if (!login) {
$scope.toastrId = toastr.info('请先登录再转存书签!', "提示");
} else {
pubSubService.publish('TagCtr.storeBookmark', $.extend(true, {}, bookmark));
}
}
$scope.copy = async function (url) {
dataService.clipboard(url);
}
$scope.detailBookmark = async function (bookmark) {
if (!bookmark.content) {
$scope.jumpToUrl(bookmark.url);
return;
}
$scope.bookmark = bookmark;
$('.js-weixin-content').modal({ blurring: true }).modal('setting', 'transition', dataService.animation()).modal('show')
$timeout(function () {
$('.js-main-content').animate({ scrollTop: 0 }, 100);
$('.js-weixin-content').modal("refresh");
}, 10)
}
$scope.close = async function () {
$('.js-weixin-content').modal('setting', 'transition', dataService.animation()).modal('hide');
}
// 快捷键r随机推荐
$document.bind("keydown", function (event) {
$scope.$apply(async () => {
let key = event.key.toUpperCase();
let menusScope = $('div[ng-controller="menuCtr"]').scope();
let blur = (menusScope && menusScope.blur) || false;
if (key == 'R' && (!blur)) {
$scope.getWeixinArticles(-1, 1);
}
if ($scope.hoverBookmark && dataService.keyShortcuts()) {
if (key == 'I') {
$scope.detailBookmark($scope.hoverBookmark)
} else if (key == 'C') {
$scope.copy($scope.hoverBookmark.url)
}
}
})
});
$scope.setHoverBookmark = async function (bookmark) {
$scope.hoverBookmark = bookmark;
}
$scope.changeCurrentPage = async function (currentPage) {
currentPage = parseInt(currentPage) || 0;
if (currentPage <= $scope.totalPages && currentPage >= 1) {
$scope.getWeixinArticles($scope.channelId, currentPage);
$scope.currentPage = currentPage;
}
}
$scope.getWeixinArticles = async function (channelId, page) {
$scope.bookmarks = [];
$scope.bookmark = {};
$scope.loading = true;
$scope.channelId = channelId;
$scope.currentPage = page;
$scope.totalPages = 0;
$scope.count = 0;
if (channelId == -1) {
let data = await get("hotBookmarksRandom");
$timeout(() => {
$scope.bookmarks = data;
$scope.totalPages = 1;
$scope.count = data.length;
$scope.loading = false;
})
} else if (channelId == 0) {
let reply = await get("hotBookmarks", { page, pageSize });
$timeout(() => {
$scope.bookmarks = reply.data;
$scope.totalPages = reply.totalPages;
$scope.count = reply.count;
$scope.loading = false;
})
} else {
$.ajax({
url: `https://api.jisuapi.com/weixinarticle/get?channelid=${channelId}&start=${(page - 1) * pageSize}&num=${pageSize}&appkey=e95887468ab87d69`,
type: 'get',
dataType: "jsonp",
jsonp: "callback",
complete: function () { $scope.loading = false },
success: function (body) { dealBody(body) },
error: function (json) { toastr.error('获取热门失败!失败原因:' + json.msg, "提示") }
});
}
}
function dealBody(body) {
console.log('success............', body);
$timeout(function () {
var defaultSnap = "./images/default.jpg"
var defaultFavicon = "./images/weixin.ico"
if (body.status == 0) {
var weixinArticles = body.result.list;
var total = body.result.total;
$scope.count = total;
$scope.totalPages = parseInt(total / pageSize) + 1;
weixinArticles.forEach((articl, index) => {
let bookmark = {};
bookmark.index = index;
bookmark.title = articl.title;
bookmark.url = articl.url;
bookmark.icon = defaultFavicon;
bookmark.tagName = articl.weixinname;
bookmark.account = articl.weixinaccount;
bookmark.snap = articl.pic || defaultSnap;
bookmark.clickCount = articl.likenum;
bookmark.createdAt = timeagoInstance.format(articl.addtime * 1000, 'zh_CN');
bookmark.content = articl.content.replace(/https:\/\/mmbiz.qpic.cn/gi, "https://favicon.lucq.fun/qpic?url=https://mmbiz.qpic.cn").replace(/http:\/\/mmbiz.qpic.cn/gi, "https://favicon.lucq.fun/qpic?url=https://mmbiz.qpic.cn");
bookmark.content = $sce.trustAsHtml(bookmark.content);
$scope.bookmarks.push(bookmark);
})
} else {
toastr.error('获取热门失败!失败原因:' + body.msg, "提示");
}
}, 10);
}
$scope.getWeixinArticles($scope.channelId, $scope.currentPage);
}]);

View File

@ -1,13 +1,15 @@
app.controller('loginCtr', ['$scope', '$filter', '$state', '$http', '$cookieStore', '$window', 'pubSubService', 'dataService', function ($scope, $filter, $state, $http, $cookieStore, $window, pubSubService, dataService) {
console.log("Hello loginCtr...", $cookieStore.get("username"));
console.log("Hello loginCtr...");
if (dataService.smallDevice()) {
$window.location = "http://m.mybookmark.cn/#/tags";
return;
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
pubSubService.publish('Common.menuActive', {
login: false,
index: dataService.NotLoginIndexLogin
pubSubService.subscribe('Common.user', $scope, function (event, user) {
user.id && $state.go('tags');
});
$scope.username = $cookieStore.get("username") || "";
@ -39,12 +41,12 @@ app.controller('loginCtr', ['$scope', '$filter', '$state', '$http', '$cookieStor
$cookieStore.put("username", $scope.username);
let data = await post('userLogin', params);
pubSubService.publish('Login', true);
// 更新token信息
localStorage.setItem("authorization", data.token);
pubSubService.publish('loginCtr.login', { login: true });
$state.go('tags')
$state.go('tags');
}
$scope.showRegister = async function () {
@ -70,7 +72,7 @@ app.controller('loginCtr', ['$scope', '$filter', '$state', '$http', '$cookieStor
toastr.error('账号只能是数字字母且长度必须为3到12位', "错误");
return;
}
if (!/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test($scope.emailRegister)) {
if (!/^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test($scope.emailRegister)) {
toastr.error('邮箱格式输入有误', "错误");
return;
}

View File

@ -1,43 +1,51 @@
app.controller('menuCtr', ['$scope', '$stateParams', '$state', '$window', '$timeout', '$document', 'pubSubService', 'dataService', function ($scope, $stateParams, $state, $window, $timeout, $document, pubSubService, dataService) {
console.log("Hello menuCtr")
$scope.login = false; /**< 是否登陆 */
$scope.selectLoginIndex = 0; /**< 默认登陆之后的选择的菜单索引,下表从 0 开始 */
$scope.selectNotLoginIndex = 0; /**< 默认未登陆之后的选择的菜单索引,下表从 0 开始 */
$scope.keyword = ''; /**< 搜索关键字 */
$scope.login = true;
$scope.selectLoginIndex = 0; // 默认登陆之后的选择的菜单索引,下表从 0 开始
$scope.selectNotLoginIndex = 0; // 默认未登陆之后的选择的菜单索引,下表从 0 开始
$scope.keyword = '';
$scope.searchHistory = [];
$scope.historyTypes = dataService.historyTypes;
$scope.quickUrl = {};
$scope.longPress = false;
$scope.user = {};
$scope.loaded = false; // 是否加载完毕
// 防止在登陆的情况下在浏览器里面直接输入url这时候要更新菜单选项
pubSubService.subscribe('Common.menuActive', $scope, function (event, params) {
console.log("subscribe Common.menuActive, login = " + params.login + ", index = " + params.index);
$scope.login = (params && params.login) || false;
var index = $scope.login ? ($scope.selectLoginIndex = (params && params.index) || 0) : ($scope.selectNotLoginIndex = (params && params.index) || 0);
updateMenuActive(index);
pubSubService.subscribe('Login', $scope, function (event, login) {
$scope.login = login;
});
pubSubService.subscribe('Settings.quickUrl', $scope, function (event, params) {
$scope.quickUrl = params.quickUrl;
function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); }
pubSubService.subscribe('Menus.active', $scope, async function () {
while (!$scope.loaded) { await sleep(10); }
updateMenuActive();
});
$scope.loginMenus = dataService.loginMenus; // 登陆之后显示的菜单数据。uiSerf内部跳转链接。
$scope.notLoginMenus = dataService.notLoginMenus; // 未登陆显示的菜单数据
get('user', { full: true }).then(user => {
$timeout(() => {
$scope.user = user;
$scope.searchHistory = JSON.parse(user.searchHistory || '[]');
$scope.quickUrl = JSON.parse(user.quickUrl || '{}');
$scope.searchHistory.forEach((item, index) => {
$scope.searchIcon(item);
})
// if ($scope.user.username === 'lcq') {
// $scope.loginMenus[dataService.LoginIndexHot].show = false;
// }
(async () => {
$timeout(async () => {
try {
let user = await get('user', { full: true });
$scope.login = true;
$scope.user = user;
$scope.searchHistory = JSON.parse(user.searchHistory || '[]');
$scope.quickUrl = JSON.parse(user.quickUrl || '{}');
if (user.username === 'lcq') {
$scope.loginMenus[dataService.LoginIndexHot].show = false;
}
for (const item of $scope.searchHistory) {
$scope.searchIcon(item);
}
} catch (error) {
$scope.login = false;
}
pubSubService.publish('Common.user', $scope.user);
$scope.loaded = true;
})
});
})()
$scope.toggleReady = function (ready) {
if (ready) {
@ -84,7 +92,6 @@ app.controller('menuCtr', ['$scope', '$stateParams', '$state', '$window', '$time
}, {
reload: true,
})
updateMenuActive($scope.selectLoginIndex = 0);
} else if (searchOption == 1) {
$window.open('https://www.google.com.hk/#newwindow=1&safe=strict&q=' + encodeURIComponent(keyword), '_blank');
} else if (searchOption == 2) {
@ -92,15 +99,10 @@ app.controller('menuCtr', ['$scope', '$stateParams', '$state', '$window', '$time
} else if (searchOption == 3) {
$window.open('https://stackoverflow.com/search?q=' + encodeURIComponent(keyword), '_blank');
} else if (searchOption == 4) {
$window.open('http://www.baidu.com/s?tn=mybookmark.cn&ch=3&ie=utf-8&wd=' + encodeURIComponent(keyword), '_blank');
$window.open('http://www.baidu.com/s?tn=b.lucq.fun&ch=3&ie=utf-8&wd=' + encodeURIComponent(keyword), '_blank');
} else if (searchOption == 5) {
console.log('search note, word = ', keyword);
$state.go('note', {
keyword: keyword,
}, {
reload: true,
})
updateMenuActive($scope.selectLoginIndex = dataService.LoginIndexNote);
$state.go('note', { keyword }, { reload: true })
}
if (!keyword) {
@ -186,29 +188,31 @@ app.controller('menuCtr', ['$scope', '$stateParams', '$state', '$window', '$time
$window.open(url, '_blank');
}
$scope.showUpdate = function () {
$state.go('settings', {
formIndex: 5,
});
pubSubService.publish('Common.menuActive', {
login: true,
index: dataService.LoginIndexSettings
});
}
$scope.coffee = function () {
$state.go('settings', {
formIndex: 6,
});
pubSubService.publish('Common.menuActive', {
login: true,
index: dataService.LoginIndexSettings
});
}
function updateMenuActive(index) {
async function updateMenuActive() {
await sleep(10); // 阻塞 10ms 是因为 document.location.hash 还没切换过来等待切换
let mapIndex = {
"#/bookmarks": dataService.LoginIndexBookmarks,
"#/tags": dataService.LoginIndexTags,
"#/note": dataService.LoginIndexNote,
"#/hot": $scope.login ? dataService.LoginIndexHot : dataService.NotLoginIndexHot,
"#/settings": dataService.LoginIndexSettings,
"#/advice": dataService.LoginIndexAdvice,
"#/": $scope.login ? dataService.LoginIndexBookmarks : dataService.NotLoginIndexHome,
"#/login": dataService.NotLoginIndexLogin,
}
console.log('updateMenuActive', $scope.login, document.location.hash, mapIndex[document.location.hash]);
$scope.selectLoginIndex = mapIndex[document.location.hash];
$scope.selectNotLoginIndex = mapIndex[document.location.hash];
$('.ui.menu a.item').removeClass('selected');
$('.ui.menu a.item:eq(' + index + ')').addClass('selected');
$('.ui.menu a.item:eq(' + mapIndex[document.location.hash] + ')').addClass('selected');
}
async function saveHistory() {
@ -249,7 +253,6 @@ app.controller('menuCtr', ['$scope', '$stateParams', '$state', '$window', '$time
// 全局处理添加备忘录
if (key == 'A') {
if ($scope.selectLoginIndex !== dataService.LoginIndexNote) {
updateMenuActive($scope.selectLoginIndex = dataService.LoginIndexNote);
$state.go('note', { key: key }, { reload: true })
}
return;
@ -269,10 +272,6 @@ app.controller('menuCtr', ['$scope', '$stateParams', '$state', '$window', '$time
}
if (key == ',' || key == '.' || key == '/') {
pubSubService.publish('Common.menuActive', {
login: $scope.login,
index: dataService.LoginIndexTags
});
var stateParams = {
tagId: -1,
orderIndex: (key == ',' ? 0 : (key == '.' ? 1 : 2)),
@ -283,11 +282,7 @@ app.controller('menuCtr', ['$scope', '$stateParams', '$state', '$window', '$time
// 数字键用来切换菜单
if (!isNaN(key)) {
var num = parseInt(key);
if (num < 0 || num > 6) return;
pubSubService.publish('Common.menuActive', {
login: $scope.login,
index: num - 1
});
if (num < 0 || num > 6 || (!$scope.login)) return;
$state.go(dataService.loginMenus[num - 1].uiSref, {}, {
reload: true,
})

View File

@ -1,9 +1,12 @@
app.controller('noteCtr', ['$scope', '$state', '$stateParams', '$filter', '$window', '$timeout', '$document', 'ngDialog', 'pubSubService', 'dataService', function ($scope, $state, $stateParams, $filter, $window, $timeout, $document, ngDialog, pubSubService, dataService) {
console.log("Hello noteCtr...", $stateParams);
if (dataService.smallDevice()) {
$window.location = "http://m.mybookmark.cn/#/tags";
return;
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
var dialog = null;
$scope.hoverNote = null;
@ -25,11 +28,6 @@ app.controller('noteCtr', ['$scope', '$state', '$stateParams', '$filter', '$wind
var timeagoInstance = timeago();
pubSubService.publish('Common.menuActive', {
login: true,
index: dataService.LoginIndexNote
});
getTags();
$scope.changeCurrentPage = function (currentPage) {
@ -309,11 +307,15 @@ app.controller('noteCtr', ['$scope', '$state', '$stateParams', '$filter', '$wind
$scope.loading = true;
var params = {
page: $scope.currentPage,
pageSize: 35,
keyword: $scope.keyword,
tagId: tagId || $scope.currentTagId
pageSize: 35
};
if (tagId || $scope.currentTagId) {
params.tagId = tagId || $scope.currentTagId;
} else if ($scope.keyword) {
params.keyword = $scope.keyword;
}
let reply = await get("notes", params);
$timeout(function () {
let notes = reply.data;
@ -363,6 +365,7 @@ app.controller('noteCtr', ['$scope', '$state', '$stateParams', '$filter', '$wind
$timeout(() => {
$scope.loading = false;
tags.sort((a, b) => a.sort - b.sort);
$scope.tags = tags;
getNotes();
})

View File

@ -1,9 +1,12 @@
app.controller('searchCtr', ['$scope', '$state', '$stateParams', '$filter', '$window', '$timeout', '$document', 'ngDialog', 'pubSubService', 'dataService', function ($scope, $state, $stateParams, $filter, $window, $timeout, $document, ngDialog, pubSubService, dataService) {
console.log("Hello searchCtr...", $stateParams);
if (dataService.smallDevice()) {
$window.location = "http://m.mybookmark.cn/#/tags";
return;
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
$scope.hoverBookmark = null;
$scope.bookmarks = []; // 书签数据
@ -36,16 +39,13 @@ app.controller('searchCtr', ['$scope', '$state', '$stateParams', '$filter', '$wi
$scope.search();
}
}
pubSubService.subscribe('Common.user', $scope, function (event, user) {
$scope.user = user;
});
get('tags').then((tags) => $scope.tags = tags)
get('user').then((user) => $scope.user = user)
// 默认登陆
pubSubService.publish('Common.menuActive', {
login: true,
index: dataService.LoginIndexBookmarks
});
$scope.jumpToUrl = async function (url, id) {
if (!$scope.edit) {
$window.open(url);

View File

@ -1,9 +1,12 @@
app.controller('settingsCtr', ['$scope', '$stateParams', '$filter', '$state', '$window', '$timeout', 'pubSubService', 'dataService', function ($scope, $stateParams, $filter, $state, $window, $timeout, pubSubService, dataService) {
console.log('Hello settingsCtr......', $stateParams);
if (dataService.smallDevice()) {
$window.location = "http://m.mybookmark.cn/#/tags";
return;
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
$scope.forbidQuickKey = dataService.forbidQuickKey
$scope.form = [false, false, false, false, false, false, false];
@ -17,9 +20,12 @@ app.controller('settingsCtr', ['$scope', '$stateParams', '$filter', '$state', '$
$scope.key = '';
$scope.url = '';
$scope.quickUrl = {};
$scope.loading = false;
$scope.href = "";
$scope.changeForm = async function (index) {
console.log("changeForm = ", index);
$scope.href = "";
$scope.form = $scope.form.map(() => false);
$scope.form[index] = true;
if (index == 0 || index == 1 || index == 4) {
@ -40,6 +46,11 @@ app.controller('settingsCtr', ['$scope', '$stateParams', '$filter', '$state', '$
$scope.changeForm($scope.form.indexOf(true)); // 马上调用一次
$scope.resetPassword = async function () {
if($scope.user.username == 'test') {
toastr.error('此用户不允许修改密码', "错误");
return;
}
if (!$scope.passwordOrgin || !$scope.passwordNew1 || !$scope.passwordNew2) {
toastr.error('原密码跟新密码不能为空', "错误");
return;
@ -49,12 +60,7 @@ app.controller('settingsCtr', ['$scope', '$stateParams', '$filter', '$state', '$
await post('userResetPwd', { old: $scope.passwordOrgin, password: $scope.passwordNew1 });
await post('userLogout');
axios.defaults.headers.common['Authorization'] = "";
localStorage.setItem("authorization", "");
pubSubService.publish('Common.menuActive', {
login: false,
index: dataService.NotLoginIndexLogin
});
$state.go('login', {})
} else {
toastr.error('新密码两次输入不一致', "错误");
@ -117,8 +123,13 @@ app.controller('settingsCtr', ['$scope', '$stateParams', '$filter', '$state', '$
}
$scope.exportBookmark = async function () {
let data = await get('bookmarkBackup');
console.log(data);
$scope.loading = true;
let fileName = await get('bookmarkBackup');
$timeout(() => {
$scope.href = `${document.location.origin}/api/bookmarkDownload?fileName=${fileName}`;
$scope.loading = false;
$window.open($scope.href, '_blank');
})
}
setTimeout(function () {
@ -136,10 +147,6 @@ app.controller('settingsCtr', ['$scope', '$stateParams', '$filter', '$state', '$
console.log(files, response);
if (response.code == 0) {
setTimeout(function () {
pubSubService.publish('Common.menuActive', {
login: true,
index: dataService.LoginIndexBookmarks
});
$state.go('tags', {})
}, 3000);
toastr.success(response.msg, "提示");
@ -151,11 +158,6 @@ app.controller('settingsCtr', ['$scope', '$stateParams', '$filter', '$state', '$
$(".ui.pointing.menu .item").removeClass("selected");
}, 500);
pubSubService.publish('Common.menuActive', {
login: true,
index: dataService.LoginIndexSettings
});
async function saveQuickUrl() {
await post("userUpdate", { quickUrl: JSON.stringify($scope.quickUrl) });
toastr.success('全局快捷键更新成功', "提示");

View File

@ -1,12 +1,19 @@
app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$stateParams', '$timeout', '$document', 'ngDialog', 'pubSubService', 'dataService', function ($scope, $filter, $state, $window, $stateParams, $timeout, $document, ngDialog, pubSubService, dataService) {
console.log("Hello tagsCtr...", $stateParams);
if (dataService.smallDevice()) {
$window.location = "http://m.mybookmark.cn/#/tags";
return;
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
}
pubSubService.publish('Menus.active');
(async () => {
await getTags();
$scope.user = await get('user');
if ($scope.user.username != 'lcq') {
$(".globalTag").show(); // 自己知道这个功能,不显示
}
})()
let dialog = null;
@ -17,10 +24,12 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
$scope.showType = "createdAt";
$scope.loading = true;
$scope.tags = []; // 书签数据
$scope.user = { username: "test" };
$scope.tagsIndex = []; // 书签索引
$scope.bookmarks = [];
$scope.totalPages = 0;
$scope.currentPage = 0;
$scope.pageSize = 80;
$scope.inputPage = '';
$scope.currentTagId = ($stateParams && $stateParams.tagId) || (-1);
$scope.editMode = false;
@ -30,11 +39,6 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
$scope.waitDelBookmark = {};
$scope.bookmarkNormalHover = false;
pubSubService.subscribe('MenuCtr.tags', $scope, function (event, data) {
console.log('subscribe MenuCtr.tags', data);
getTags();
});
$scope.getBookmarks = async function (tagId, page, showType) {
console.log(tagId, page, showType);
@ -44,7 +48,7 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
showType && ($scope.showType = showType);
$scope.loading = true;
let pageSize = ($scope.showMode == 'item') ? 50 : 20;
let pageSize = ($scope.showMode == 'item') ? $scope.pageSize : 20;
for (let tag of $scope.tags) {
tag.bookmarkClicked = (tag.id == $scope.currentTagId);
@ -84,13 +88,12 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
clearInterval(id);
}
}, 10);
} else if ($scope.showMode == 'item' && bookmarks.length > $scope.pageSize / 2 && $scope.currentTagId == -1) {
$timeout(() => {
$("#" + bookmarks[bookmarks.length / 2 - 1].id).after(`<div class="ui divider" style="width:100%;margin:0px 15px -1px 15px"></div>`);
}, 100);
}
pubSubService.publish('Common.menuActive', {
login: true,
index: dataService.LoginIndexTags
});
$timeout(function () {
dataService.transition('#' + addBookmarkId, {
duration: 1000,
@ -277,7 +280,7 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
$scope.addTag = async function (tag) {
console.log(tag);
if ($scope.tags.length >= 30) {
if ($scope.tags.length >= 50) {
toastr.error('标签个数总数不能超过30个不允许再添加新分类如有需求请联系管理员。', "提示");
return;
}
@ -355,6 +358,10 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
})
});
$scope.globalTag = function () {
$state.go('settings', { formIndex: 4 });
}
async function updateTags(_tags) {
let tags = JSON.parse(JSON.stringify(_tags));
@ -365,8 +372,9 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
id: -1,
bookmarkCount: '...',
bookmarkClicked: false,
name: '个人定制',
show: 1
name: '全部',
show: 1,
sort: -1
})
let find = false;
@ -384,10 +392,7 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
tags[0].bookmarkClicked = true;
}
pubSubService.publish('Common.menuActive', {
login: true,
index: dataService.LoginIndexTags
});
tags.sort((a, b) => a.sort - b.sort);
$timeout(() => {
$scope.loading = false;

View File

@ -150,34 +150,9 @@ app.directive('jsMenuInit', function ($compile) {
link: function ($scope, $element, $attrs) {
if ($scope.$last === true) {
console.log('jsMenuInit......')
$('.js-bookmark-dropdown').dropdown({
action: 'hide',
on: 'hover',
});
$('.js-bookmark-dropdown .ui.checkbox').checkbox();
$('.ui.checkbox.js-radio-navigate').checkbox('check');
$('.ui.menu a.item').on('click', function () {
$(this).addClass('selected').siblings().removeClass('selected');
});
$(".ui.menu a.item:first").hover(
function () {
$('.js-bookmark-dropdown').dropdown('show');
},
function () {
setTimeout(() => {
if ($('.js-menu-option:hover').length === 0) {
$('.js-bookmark-dropdown').dropdown('hide');
}
}, 100)
}
);
$('.ui.menu a.item').on('click', function () {
$(this).addClass('selected').siblings().removeClass('selected');
});
$('.search-item').popup({
on: 'focus',
inline: true

Some files were not shown because too many files have changed in this diff Show More