Compare commits

..

1 Commits

Author SHA1 Message Date
HelloWorld 6daa88b148 sb-gxb 2020-07-04 16:07:51 +08:00
136 changed files with 1371 additions and 1543 deletions

View File

@ -1,27 +0,0 @@
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"]

View File

@ -1,9 +1,9 @@
# 在线书签管理工具
![image](https://b.lucq.fun/images/screenshot.gif)
![image](https://mybookmark.cn/images/screenshot.gif)
1 在线体验(demo)
-------------
[在线书签管理系统](http://b.lucq.fun/ "在线书签管理系统")体验账号test。密码123456。
[在线书签管理系统](http://mybookmark.cn/ "在线书签管理系统")体验账号test。密码123456。
2 为什么要做个网络书签
------------------
@ -31,7 +31,7 @@
- [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/)。
- [x] 适配手机平板,手机端请访问[m.mybookmark.cn](http://m.mybookmark.cn/)。
4 主要用到的软件与模块说明
@ -48,7 +48,6 @@
my-bookmark/
├── development.js # 开发环境下的入口文件
├── logs/ # 日志目录
├── Dockerfile # Dockerfile 构建文件
├── nginx.conf # nginx 配置文件
├── package.json # 项目依赖包
├── pm2.json # pm2 配置文件
@ -79,6 +78,8 @@ my-bookmark/
│   └── index.js # 后台测试文件
├── update.sql # MySQL更新文件
├── view/ # 网站主页显示文件夹
│   └── index_index.html # 网站主页
├── www # 网站实现文件夹
│   ├── 404.html # 默认404页面
│   ├── css/ # 样式表文件夹
│   │   ├── externe/ # 外部引入引来的css文件
@ -91,35 +92,13 @@ my-bookmark/
│   │   ├── directives/ # 所有的AngularJS指令
│   │   ├── externe/ # 外部引入的JS文件
│   │   └── services/ # 所有的AngularJS服务文件
│   ├── views/ # 页面实现文件
│   └── index.html # 网站主页
│   └── views/ # 页面实现文件
└── README.md # 项目工程说明文件
```
6 Docker安装部署
6 安装部署指南
-------------
此部署方式适合新手。
如果你的Linux环境中没有安装Docker环境。那么请先执行如下命令安装Docker环境。
```
curl -fsSL get.docker.com -o get-docker.sh
sudo sh get-docker.sh --mirror Aliyun
```
安装好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如果有需要的请按如下命令执行安装
```
docker run -itd --name mybookmark -p 2000:2000 -p 3306:3306 740162752/bookmark
```
7 安装部署指南
-------------
这种适合动手能力比较强的人员。
1、安装MySQL数据库。如果不会请戳教程[MySQL 数据库安装教程](http://baidu.lucq.fun/?q=TXlTUUwg5pWw5o2u5bqT5a6J6KOF5pWZ56iL "mysql 数据库安装教程")。有点需要注意的是MySQL的版本至少要是5.6。否则执行schema.sql文件会出错。
1、安装MySQL数据库。如果不会请戳教程[MySQL 数据库安装教程](http://baidu.luchenqun.com/?mysql%20%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B "mysql 数据库安装教程")。有点需要注意的是MySQL的版本至少要是5.6。否则执行schema.sql文件会出错。
2、新建一个数据库名使用MySQL将根目录下面的schema.sql文件执行一遍创建数据库表格。有个问题尤其要注意**数据库一定要使用UTF-8的编码**否则执行一些汉字的sql语句会出错如果是Ubuntu大概过程如下。
```
mysql -u root -p // 使用root账号进入mysql数据库。按回车之后输入安装时候root的密码。
@ -130,19 +109,18 @@ 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提供的方法自行解决。
4、安装Node.js。Node.js版本至少要求12.0以上。不会的话请按照上面步骤1、3提供的方法自行解决。
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/`
8、在浏览器里面输入127.0.0.1:2000
9、如果需要域名部署的话推荐使用nginx作为HTTP和反向代理服务器根目录有一份`nginx.conf`文件,你只需要更新`root`项即可使用。相关知识,请自行百度。
8 其他说明
7 其他说明
---------
1、我没有做浏览器兼容测试只在Google Chrome下面进行了测试开发。
9 开源许可证
8 开源许可证
-----------
[MIT License](http://www.opensource.org/licenses/MIT)
你可以随意使用此项目,无需通知我,因为我可能很忙没时间。注意,手机版当前没开源

View File

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

View File

@ -1,8 +1,6 @@
CREATE DATABASE IF NOT EXISTS mybookmarks DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 创建mybookmarks数据库
USE mybookmarks;
-- 用户信息表
CREATE TABLE IF NOT EXISTS `users` (
drop table if exists users;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`username` varchar(255) NOT NULL, -- 用户名
`password` varchar(255) NOT NULL, -- 密码
@ -11,14 +9,15 @@ CREATE TABLE IF NOT EXISTS `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/\",\"H\":\"https://github.com/\"}', -- 全局快捷地址
`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/\"}', -- 全局快捷地址
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
);
-- 书签表
CREATE TABLE IF NOT EXISTS `bookmarks` (
drop table if exists bookmarks;
CREATE TABLE `bookmarks` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`userId` int(11) NOT NULL, -- 用户id
`tagId` int(11) NOT NULL, -- 分类id (只允许一个书签对应一个分类)
@ -34,7 +33,8 @@ CREATE TABLE IF NOT EXISTS `bookmarks` (
);
-- 书签分类表
CREATE TABLE IF NOT EXISTS `tags` (
drop table if exists tags;
CREATE TABLE `tags` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`userId` int(11) NOT NULL, -- 用户id
`name` varchar(32) NOT NULL, -- 标签
@ -47,7 +47,8 @@ CREATE TABLE IF NOT EXISTS `tags` (
);
-- 建议留言
CREATE TABLE IF NOT EXISTS `advices` (
drop table if exists advices;
CREATE TABLE `advices` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`userId` int(11) NOT NULL, -- 用户id
`comment` text NOT NULL, -- 评论
@ -58,7 +59,8 @@ CREATE TABLE IF NOT EXISTS `advices` (
);
-- 热门表
CREATE TABLE IF NOT EXISTS `hot_bookmarks` (
drop table if exists hot_bookmarks;
CREATE TABLE `hot_bookmarks` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id(articleId)
`title` varchar(255) DEFAULT NULL, -- 标题(title)
`url` varchar(1024) DEFAULT NULL, -- 链接(url)
@ -72,7 +74,8 @@ CREATE TABLE IF NOT EXISTS `hot_bookmarks` (
);
-- 备忘录
CREATE TABLE IF NOT EXISTS `notes` (
drop table if exists notes;
CREATE TABLE `notes` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- id
`userId` int(11) NOT NULL, -- 用户id
`content` text NOT NULL, -- 备忘内容

View File

@ -1,18 +1 @@
// 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: '2592000s' // 30天过期
expiresIn: '604800s' // 7天过期
},
verify: {
// verify options is not required

View File

@ -22,11 +22,7 @@ module.exports = [{
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];
}
for (const item of list) {
dataList.push({
id: item.articleId,
title: item.title,

View File

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

View File

@ -59,7 +59,6 @@ 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) {
@ -211,17 +210,6 @@ 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 = "未分类";
@ -237,10 +225,6 @@ 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() });
@ -281,13 +265,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);
// 按照 1:1取数据
let length = Math.ceil(pageSize / 2);
// 按照 2:2:1取数据
let length = Math.ceil(pageSize * 2 / 5);
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 * 4).select(); // 这个多取一点,有可能跟上面的重复了
let bookmarks2 = await this.model('bookmarks').where(condition).order('lastClick DESC').limit(0, pageSize * 2).select(); // 这个多取一点,有可能跟上面的重复了
for (const bookmark of bookmarks2) {
let find = bookmarks.find(item => item.id == bookmark.id);
if (!find) {
@ -297,6 +281,16 @@ 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,
@ -346,7 +340,7 @@ module.exports = class extends Base {
}
try {
let data = await this.model(tableName).where(condition).order('createdAt DESC').page(this.get('page') || 1, this.get('pageSize') || 20).countSelect();
let data = await this.model(tableName).where(condition).page(this.get('page') || 1, this.get('pageSize') || 20).countSelect();
if (tableName == "bookmarks") {
let ids = [];
for (let bookmark of data.data) {
@ -426,193 +420,94 @@ module.exports = class extends Base {
// path: 'C:\\Users\\lucq\\AppData\\Local\\Temp\\upload_4ae3b14dacaa107076d3bddd471ebe39.html',
// name: 'exportbookmark-lcq-20200402084709.html',
// type: 'text/html',
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();
};
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 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, "");
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);
}
}
}
const file = this.file("file");
let bookmarks = [];
const file = this.file("file");
let fileName = 'uploadbookmark-' + this.ctx.state.user.username + '-' + think.datetime(new Date(), "YYYYMMDDHHmmss") + '.html';
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 originBookmarks = parseByPath(filePath);
Array.isArray(originBookmarks) && originBookmarks.length >= 0 && (originBookmarks[0].title = "未分类");
flatBookmarks(originBookmarks, originBookmarks[0].title, bookmarks); // 传上来的树级目录改为只有一级目录
let data = await fs.readFile(filePath);
let $ = cheerio.load(data.toString());
let anchors = $("dl").find("a");
anchors.each(async (i, e) => {
let url = $(e).attr("href");
let title = $(e).text() || "无标题";
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 })
});
}
let count = 0;
let repeat = 0;
let fail = 0;
let failStr = "";
let tags = await this.model("tags").where({ userId: this.ctx.state.user.id }).select();
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;
try {
await this.model("bookmarks").add(bookmark);
} catch (error) {
fail++;
failStr += bookmark.title + ",";
}
await this.model("bookmarks").add(bookmark);
count++;
} else {
repeat++;
}
}
this.json({ code: 0, data: count, msg: `书签传入${bookmarks.length}个,重复书签${repeat}个,${fail}个导入失败:${failStr}成功导入${count}个。` });
this.json({ code: 0, data: count, msg: `书签传入${bookmarks.length}个,重复书签${repeat}个,成功导入${count}个。` });
}
// 书签备份
async bookmarkBackupAction() {
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 init = '<TITLE>Bookmarks</TITLE><H1>Bookmarks</H1><DL id="0"></DL>';
let $ = cheerio.load(init, {
decodeEntities: false,
xmlMode: true,
});
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) {
let tagStr = ` <DT><H3 ADD_DATE="${time(tag.lastUse)}" LAST_MODIFIED="${time(tag.lastUse)}">${tag.name}</H3>\n <DL><p>\n`;
$('#0').append(`<DT><H3>${tag.name}</H3></DT><DL id="${tag.id}"></DL>`);
let bookmarks = await this.model('bookmarks').where({ tagId: tag.id }).select();
for (const bookmark of bookmarks) {
tagStr += ` <DT><A HREF="${bookmark.url}" ADD_DATE="${time(bookmark.createdAt)}" LAST_CLICK="${time(bookmark.lastClick)}" CLICK_COUNT="${bookmark.clickCount}">${bookmark.title}</A>\n`
$('#' + 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 += ` </DL><p>\n`;
middle += bookmarks.length > 0 ? tagStr : '';
}
let fileName = 'exportbookmark-' + this.ctx.state.user.username + '-' + think.datetime(new Date(), "YYYYMMDDHHmmss") + '.html';
let now = new Date().getTime()
let fileName = 'exportbookmark-' + this.ctx.state.user.username + '-' + now + '.html';
let filePath = path.join(think.ROOT_PATH, 'runtime', 'backup', fileName);
await fs.ensureFile(filePath);
await fs.writeFile(filePath, left + middle + right);
await fs.writeFile(filePath, $.xml());
this.json({ code: 0, data: fileName });
setTimeout(async () => {
@ -707,9 +602,6 @@ 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: `留言 添加成功` });
@ -801,29 +693,24 @@ 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();
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>`;
}
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 {
async indexAction() {
await this.display("index.html");
}
};
const Base = require('./base.js');
module.exports = class extends Base {
indexAction() {
return this.display();
}
};

View File

@ -37,14 +37,6 @@
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;
@ -61,25 +53,18 @@
</head>
<body ng-app="bookmarkApp">
<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>
<div class="ui container" id="js-container" style="position: absolute;left: 86;">
<!-- 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: 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>
<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="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>
@ -124,10 +109,5 @@
<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>

File diff suppressed because one or more lines are too long

View File

@ -1,65 +0,0 @@
<div class="ui segment js-note-card" style="padding: 14px 0px 0px 0px" ng-show="!loading || tags.length > 0">
<div class="ui container" style="padding-left: 14px">
<div class="ui label" style="margin: 3px 15px 8px 0px; cursor: default" ng-class="{green:tag.clicked}" ng-repeat="tag in tags" ng-click="clickTag(tag.id)" ng-show="tag.noteCount || add">{{ tag.name }} ({{ tag.noteCount || 0 }})</div>
<div class="ui label" style="margin: 3px 15px 8px 0px; cursor: default" ng-click="showAddNote()" data-tooltip="点击添加备忘。你也可以在任意界面按快捷键A(不区分大小写)增加备忘录。">
<i class="plus icon" style="margin-right: 0px"></i>
</div>
</div>
<div class="ui container" style="padding-left: 14px; padding-bottom: 14px" ng-show="add">
<div class="ui form">
<div class="required field">
<label>内容</label>
<textarea rows="12" placeholder="" ng-model="content" id="noteedit"></textarea>
</div>
<div class="field">
<div class="actions">
<div class="ui cancel button" ng-click="add=false;">取消</div>
<div class="ui green button" ng-click="addNote(false)" ng-show="!edit">确定</div>
<div class="ui green button" ng-click="updateNote()" ng-show="edit">更新</div>
</div>
</div>
</div>
</div>
<div class="ui divider" ng-show="notes.length > 0" style="margin: 0px"></div>
<div class="ui hidden info message js-note" ng-if="(!add) && notes.length == 0" style="margin-left: 14px; margin-right: 14px">
<i class="close icon" ng-click="closeNote()"></i>
<div class="content">
<div class="header">系统提示!</div>
<ul class="list">
<li>您可以在任意界面按快捷键A(不区分大小写)增加备忘录。双击备忘录可查看详情!</li>
</ul>
</div>
</div>
<div class="ui vertical segment" ng-repeat="note in notes" ng-click="noteClick(note)" ng-mouseover="setHoverNote(note)" ng-mouseleave="setHoverNote(null)" id="{{note.id}}" style="margin: 0px; padding: 10px 0px">
<pre class="note-content" title="单击查看详情C复制D删除E编辑" style="margin: 0px; padding-left: 14px; padding-right: 14px" ng-if="!note.detail">{{ note.brief }}</pre>
<pre class="note-content" style="margin: 0px; font-size: 16px; padding: 60px 14px" ng-if="note.detail">{{ note.content }}</pre>
<div class="ui right aligned grid" ng-show="note.detail">
<div class="sixteen wide column" style="margin: 0px 20px 0px 0px; padding: 20px 0px 0px 0px">
<div class="extra content" ng-show="true" ng-mouseleave="note.edit=false;" style="height: 50px">
<div class="ui mini label" ng-click="clickTag(note.tagId)" style="margin: 3px 0px 0px 10px; cursor: default">{{ note.tagName || "未分类" }}</div>
<span style="margin: 0 8px">
<span title="添加于{{note.createdAt}}" class="need_to_be_rendered" data-timeago="{{ note.createdAt }}"></span>
<span style="margin-left: -3px">添加</span>
</span>
<i ng-if="note.public == 0" class="black lock icon" title="点击公开备忘" ng-click="updatePublic(note, 1)"></i>
<i ng-if="note.public == 1" class="black open lock icon" title="点击不公开备忘" ng-click="updatePublic(note, 0)"></i>
<img class="ui mini spaced image" style="width: 16px; height: 16px; margin: 0 8px" ng-src="./images/delete.png" ng-click="delNote(note.id, note.content)" title="删除备忘" />
<label for="noteedit">
<img class="ui mini spaced image" style="width: 16px; height: 16px; margin: 0 8px" ng-src="./images/edit-bookmark.png" ng-click="editNote(note.id, note.content, note.tagId)" title="编辑备忘" />
</label>
<img class="ui mini spaced image" id="noteid{{note.id}}" style="width: 16px; height: 16px; margin: 0 8px" ng-src="./images/copy.png" id="url{{bookmark.id}}" ng-click="copy(note.content)" title="复制备忘" />
<i class="black share alternate icon" title="复制分享地址" ng-click="share(note)"></i>
<i class="black chevron up icon" title="收起详情" ng-click="noteClick(note, true, $event)"></i>
</div>
</div>
</div>
</div>
<div style="height: 20px" ng-show="notes.length === 0"></div>
<div class="ui grid" ng-show="totalItems>0" style="margin: 0px; padding: 0px 14px">
<div class="eight wide column" style="padding-top: 26px"><span ng-show="searchWord">通过搜索关键字"{{searchWord}}"(点击菜单"备忘录"重新查看所有)</span>共找到备忘一共约{{totalItems}}个</div>
<div class="eight wide column">
<pagination></pagination>
</div>
</div>
</div>
<div class="ui massive text centered inline loader js-hot-loader" style="margin: 10px 0px;" ng-class="{active:loading, disabled:!loading}">正在加载中...</div>

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://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>
<!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>

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,195 +1,191 @@
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;
}
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;
}

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

@ -1,11 +1,8 @@
app.controller('adviceCtr', ['$scope', '$state', '$timeout', '$window', 'pubSubService', 'dataService', function ($scope, $state, $timeout, $window, pubSubService, dataService) {
app.controller('adviceCtr', ['$scope', '$state', '$timeout', 'pubSubService', 'dataService', function ($scope, $state, $timeout, 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;
}
$window.location = "http://m.mybookmark.cn/#/tags";
return;
}
pubSubService.publish('Menus.active');

View File

@ -12,6 +12,31 @@ app.controller('bookmarkInfoCtr', ['$scope', '$state', '$timeout', '$sce', '$win
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,11 +1,8 @@
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()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
$window.location = "http://m.mybookmark.cn/#/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 < 50) {
if ($scope.tags.length < 30) {
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('标签个数总数不能超过50个不允许再添加新分类如有需求请联系管理员。', "提示");
toastr.error('标签个数总数不能超过30个不允许再添加新分类如有需求请联系管理员。', "提示");
}
}
$scope.addTag = async function (tag) {
console.log(tag);
if ($scope.tags.length >= 50) {
toastr.error('标签个数总数不能超过50个不允许再添加新分类如有需求请联系管理员。', "提示");
if ($scope.tags.length >= 30) {
toastr.error('标签个数总数不能超过30个不允许再添加新分类如有需求请联系管理员。', "提示");
return;
}
@ -188,9 +188,7 @@ 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')
$timeout(function () {
$scope.loadTags = false;
}, 10);
$scope.loadTags = false;
});
pubSubService.subscribe('TagCtr.storeBookmark', $scope, function (event, bookmark) {

View File

@ -1,10 +1,8 @@
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;
}
$window.location = "http://m.mybookmark.cn/#/tags";
return;
}
pubSubService.publish('Menus.active');

View File

@ -1,10 +1,8 @@
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;
}
$window.location = "http://m.mybookmark.cn/#/tags";
return;
}
pubSubService.publish('Menus.active');
const pageSize = 40;
@ -167,7 +165,7 @@ app.controller('hotCtr', ['$scope', '$state', '$sce', '$filter', '$window', '$ti
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 = articl.content.replace(/https:\/\/mmbiz.qpic.cn/gi, "http://img01.store.sogou.com/net/a/04/link?appid=100520029&url=https://mmbiz.qpic.cn").replace(/http:\/\/mmbiz.qpic.cn/gi, "http://img01.store.sogou.com/net/a/04/link?appid=100520029&url=https://mmbiz.qpic.cn");
bookmark.content = $sce.trustAsHtml(bookmark.content);
$scope.bookmarks.push(bookmark);
})

View File

@ -1,10 +1,8 @@
app.controller('loginCtr', ['$scope', '$filter', '$state', '$http', '$cookieStore', '$window', 'pubSubService', 'dataService', function ($scope, $filter, $state, $http, $cookieStore, $window, pubSubService, dataService) {
console.log("Hello loginCtr...");
if (dataService.smallDevice()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
$window.location = "http://m.mybookmark.cn/#/tags";
return;
}
pubSubService.publish('Menus.active');
@ -72,7 +70,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

@ -99,7 +99,7 @@ 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=b.lucq.fun&ch=3&ie=utf-8&wd=' + encodeURIComponent(keyword), '_blank');
$window.open('http://www.baidu.com/s?tn=mybookmark.cn&ch=3&ie=utf-8&wd=' + encodeURIComponent(keyword), '_blank');
} else if (searchOption == 5) {
console.log('search note, word = ', keyword);
$state.go('note', { keyword }, { reload: true })
@ -200,7 +200,7 @@ app.controller('menuCtr', ['$scope', '$stateParams', '$state', '$window', '$time
"#/bookmarks": dataService.LoginIndexBookmarks,
"#/tags": dataService.LoginIndexTags,
"#/note": dataService.LoginIndexNote,
"#/hot": $scope.login ? dataService.LoginIndexHot : dataService.NotLoginIndexHot,
"#/weixin-article": $scope.login ? dataService.LoginIndexHot : dataService.NotLoginIndexHot,
"#/settings": dataService.LoginIndexSettings,
"#/advice": dataService.LoginIndexAdvice,
"#/": $scope.login ? dataService.LoginIndexBookmarks : dataService.NotLoginIndexHome,

View File

@ -1,10 +1,8 @@
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()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
$window.location = "http://m.mybookmark.cn/#/tags";
return;
}
pubSubService.publish('Menus.active');
@ -365,7 +363,6 @@ 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,10 +1,8 @@
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()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
$window.location = "http://m.mybookmark.cn/#/tags";
return;
}
pubSubService.publish('Menus.active');

View File

@ -1,10 +1,8 @@
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()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
$window.location = "http://m.mybookmark.cn/#/tags";
return;
}
pubSubService.publish('Menus.active');

View File

@ -1,10 +1,8 @@
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()) {
if ($window.location.hostname.indexOf("b.lucq.fun") >= 0) {
$window.location = "http://mb.lucq.fun/#/tags";
return;
}
$window.location = "http://m.mybookmark.cn/#/tags";
return;
}
pubSubService.publish('Menus.active');
@ -29,7 +27,6 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
$scope.bookmarks = [];
$scope.totalPages = 0;
$scope.currentPage = 0;
$scope.pageSize = 80;
$scope.inputPage = '';
$scope.currentTagId = ($stateParams && $stateParams.tagId) || (-1);
$scope.editMode = false;
@ -48,7 +45,7 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
showType && ($scope.showType = showType);
$scope.loading = true;
let pageSize = ($scope.showMode == 'item') ? $scope.pageSize : 20;
let pageSize = ($scope.showMode == 'item') ? 50 : 20;
for (let tag of $scope.tags) {
tag.bookmarkClicked = (tag.id == $scope.currentTagId);
@ -88,10 +85,6 @@ 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);
}
$timeout(function () {
@ -280,7 +273,7 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
$scope.addTag = async function (tag) {
console.log(tag);
if ($scope.tags.length >= 50) {
if ($scope.tags.length >= 30) {
toastr.error('标签个数总数不能超过30个不允许再添加新分类如有需求请联系管理员。', "提示");
return;
}
@ -373,8 +366,7 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
bookmarkCount: '...',
bookmarkClicked: false,
name: '全部',
show: 1,
sort: -1
show: 1
})
let find = false;
@ -392,8 +384,6 @@ app.controller('tagsCtr', ['$scope', '$filter', '$state', '$window', '$statePara
tags[0].bookmarkClicked = true;
}
tags.sort((a, b) => a.sort - b.sort);
$timeout(() => {
$scope.loading = false;
$scope.tags = tags;

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