canary_fe/src/views/Home/Home.vue
2020-09-01 20:37:00 +08:00

560 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="home" ref="home">
<md-app md-waterfall md-mode="fixed">
<md-app-toolbar class="md-primary">
<div class="md-toolbar-section-start">
<md-button class="md-icon-button" @click="menuVisible = !menuVisible"><md-icon>menu</md-icon></md-button>
<span class="md-title page-title" @click="scrollTop()">{{ lang.title }}</span>
</div>
<div class="md-toolbar-section-end">
<div class="sroll-top-area" @click="scrollTop()"></div>
<md-button class="md-icon-button" @click="turnToSearch()"><md-icon>search</md-icon></md-button>
<md-menu md-align-trigger>
<md-button md-menu-trigger class="md-icon-button"><md-icon>more_vert</md-icon></md-button>
<md-menu-content>
<md-menu-item @click="changeSortType('Alphabetically')">{{ lang.menu[0] }}</md-menu-item>
<md-menu-item @click="changeSortType('Recently Used')">{{ lang.menu[1] }}</md-menu-item>
</md-menu-content>
</md-menu>
</div>
<div class="md-toolbar-row" v-if="search_start">
<md-autocomplete class="search" v-model="search_content" :md-options="titles" md-layout="box">
<label>{{ lang.search }}</label>
</md-autocomplete>
</div>
</md-app-toolbar>
<md-app-drawer :md-active.sync="menuVisible">
<div class="drawer-banner" @click="toggleVc()">
<md-icon class="default-avatar md-size-2x face">face</md-icon>
<p class="md-title">Canary Codebook</p>
<p class="md-caption">{{ user_infos.user_name }}</p>
</div>
<md-list>
<md-list-item @click="turnToPage('/account')">
<md-icon>person</md-icon>
<span class="md-list-item-text">{{ lang.drawer[0] }}</span>
</md-list-item>
<!-- <md-list-item @click="turnToPage('/faq')">
<md-icon>help</md-icon>
<span class="md-list-item-text">FAQ</span>
</md-list-item>
<md-list-item @click="turnToPage('/feedback')">
<md-icon>question_answer</md-icon>
<span class="md-list-item-text">Provide Feedback</span>
</md-list-item> -->
<md-list-item @click="turnToPage('/settings')">
<md-icon>settings</md-icon>
<span class="md-list-item-text">{{ lang.drawer[1] }}</span>
</md-list-item>
<md-list-item @click="turnToPage('/generator')">
<md-icon>extension</md-icon>
<span class="md-list-item-text">{{ lang.drawer[2] }}</span>
</md-list-item>
<md-list-item @click="turnToPage('/updatelog')">
<md-icon>event_note</md-icon>
<span class="md-list-item-text">{{ lang.drawer[3] }}</span>
</md-list-item>
<md-list-item v-clipboard:copy="web_addr" v-clipboard:success="onCopyUrl" v-clipboard:error="onErrorUrl">
<md-icon>reply</md-icon>
<span class="md-list-item-text">{{ lang.drawer[4] }}</span>
</md-list-item>
<md-list-item @click="downloadApk()">
<md-icon>file_download</md-icon>
<span class="md-list-item-text">{{ lang.drawer[5] }}</span>
</md-list-item>
<md-list-item @click="turnToUnlock('用户点击锁定')">
<md-icon>beenhere</md-icon>
<span class="md-list-item-text">{{ lang.drawer[6] }}</span>
</md-list-item>
</md-list>
</md-app-drawer>
<md-app-content>
<v-touch @swiperight="menuVisible = true" :swipe-options="{ direction: 'horizontal' }">
<div ref="list_placeholder" :style="`height:${search_start ? '86' : '40'}px`"></div>
<md-empty-state md-icon="devices_other" :md-label="lang.empty_state.label" :md-description="lang.empty_state.description" v-if="show_list.length == 0 && !search_start">
<md-button class="md-primary md-raised" @click="turnToAdd()">{{ lang.empty_state.button }}</md-button>
</md-empty-state>
<div class="code-card" v-for="(code, index) in show_list" :key="index" @click="turnToDetail(code)">
<p class="md-title">{{ code.title }}</p>
<p class="md-caption">{{ code.user_name }}</p>
<md-divider></md-divider>
<div style="margin-bottom: .3rem;"></div>
</div>
<md-speed-dial class="md-bottom-right" v-if="show_list.length != 0 || search_start">
<md-speed-dial-target class="md-primary" @click="turnToAdd()"><md-icon>add</md-icon></md-speed-dial-target>
</md-speed-dial>
<md-snackbar md-position="center" :md-active.sync="show_snackbar" md-persistent>
<span>{{ snakebar_msg }}</span>
</md-snackbar>
</v-touch>
</md-app-content>
</md-app>
</div>
</template>
<script>
// @ is an alias to /src
import { mapState, mapActions } from 'vuex';
import { encrypt, decrypt, encryptMainCode, decryptMainCode } from '@/utils/aes.js';
import { lang } from '@/utils/language.js';
import { setHtmlFontSize } from '@/utils/px2rem.js';
export default {
name: 'Home',
data() {
return {
// 显示抽屉开关
menuVisible: false,
// 显示高度控制md-app的最小高度防止抽屉高度塌陷
clientHeight: '',
// 显示的密码列表
show_list: [],
// 搜索暂存list
cache_list: [],
unlock: false,
show_snackbar: false,
snakebar_msg: '',
web_addr: 'https://canary.lacus.site',
titles: [],
search_content: '',
search_start: false,
lang: '',
// vcnsole模块
lastClickTime: 0,
count: 0,
// 滚动记录
scrollHeight: 0,
};
},
computed: {
...mapState(['user_infos', 'row_data', 'row_pwd', 'settings', 'home_state'])
},
methods: {
...mapActions(['setUserInfo', 'setRowData', 'setRowPwd', 'setSettings', 'setHomeState']),
// 修改md-app的最小高度
changeFixed(clientHeight) {
//动态修改样式
// console.log(clientHeight);
this.$refs.home.children[0].style.minHeight = clientHeight + 'px';
this.$refs.list_placeholder.parentNode.parentNode.style.maxHeight = clientHeight + 'px';
this.$refs.list_placeholder.parentNode.style.minHeight = clientHeight - 32 + 'px';
window.document.documentElement.setAttribute('data-theme', this.settings.is_dark_mode ? 'dark' : 'light');
},
// 初始化
init() {
// 刷新vuex
this.$store.replaceState(Object.assign(this.$store.state, JSON.parse(localStorage.getItem('storeState'))));
// 初始化
this.initUserInfo();
this.initSettings();
// 判断有无用户密码
if (Object.keys(this.row_pwd).length != 0) {
// 有密码,已经输入过了
let now = new Date().getTime();
if (now - this.row_pwd.create_time < this.settings.expired_time) {
// 上次写入时间距离现在在(默认五分钟)之内
this.unlock = true;
// 判断有无密码本
if (this.row_data) {
// 解密主密码
let main_code_decrpt = decryptMainCode(this.row_pwd.main_code);
let data_decrypt;
// 解密密码本
try {
data_decrypt = decrypt(main_code_decrpt, this.row_data);
} catch (e) {
// 不正确
// 跳转到解锁界面
console.log(e);
this.turnToUnlock('密码错误');
return;
}
// 刷新显示内容
let data_list = JSON.parse(data_decrypt);
this.show_list = data_list;
// 抽离title
this.separateTitle(data_list);
// 恢复原先状态
if(this.home_state) {
this.search_start = this.home_state.search_start
this.search_content = this.home_state.search_content
this.$nextTick(()=>{
this.$refs.list_placeholder.parentNode.parentNode.scrollTo({
top: this.home_state.scrollHeight
});
console.log('原状态恢复完成')
this.setHomeState(['', this])
console.log('主页状态缓存删除完成')
})
}
} else {
// 空密码本
this.show_list = [];
}
} else {
// 跳转到解锁界面
this.turnToUnlock('密码超时');
}
} else {
// 没有密码,跳转到解锁界面创建密码
this.turnToUnlock('无密码');
}
console.log(this.show_list.length != 0 ? '密码本存在' : '空密码本');
console.log(this.unlock ? '已解锁' : '未解锁');
},
// 初始化用户信息
initUserInfo() {
if (Object.keys(this.user_infos).length == 0) {
// 初始化用户信息
let user_infos = {
user_name: 'A Little Canary',
cid: 'Codebook',
row_login_pwd: '',
drivce: this.settings.is_chinese ? '暂无' : 'unknown',
cloud_drivce: this.settings.is_chinese ? '暂无' : 'unknown',
update_time: new Date().getTime()
};
this.setUserInfo([user_infos, this]);
console.log('用户信息覆写完成');
}
console.log('用户信息初始化完成');
},
// 初始化配置信息
initSettings() {
if (Object.keys(this.settings).length == 0) {
// 初始化配置信息
let settings = {
is_chinese: true,
is_dark_mode: false,
expired_time: 300000 // 5分钟
};
this.setSettings([settings, this]);
console.log('配置信息覆写完成');
}
if (this.settings.is_chinese) {
this.lang = lang().home.CHS;
} else {
this.lang = lang().home.EN;
}
console.log('语言配置完成');
console.log('配置信息初始化完成');
},
// 修改排序
changeSortType(type) {
// 没有内容拦截器
if (this.show_list.length == 0) {
console.log('修改排序无内容拦截');
return;
}
// 排序
if (type == 'Alphabetically') {
// 按首字母排序
this.show_list = this.show_list.sort((a, b) => a['title'].localeCompare(b['title']));
} else {
// 按使用频率排序
this.show_list = this.show_list.sort((a, b) => b['open_count'] - a['open_count']);
}
},
// 跳转到解锁界面
turnToUnlock(type) {
this.setHomeState(['', this])
console.log('主页状态缓存删除完成')
console.log(type);
this.setRowPwd(['', this]);
if (type == '密码超时') this.$router.push({ name: 'Unlock', params: { msg: this.lang.unlock_msg.expired } });
if (type == '密码错误') this.$router.push({ name: 'Unlock', params: { msg: this.lang.unlock_msg.wrong } });
else this.$router.push('/unlock');
},
// 跳转到搜索界面
turnToSearch() {
// 没有内容拦截器
if (this.show_list.length == 0) {
console.log('点击搜索无内容拦截');
return;
}
this.search_start = !this.search_start;
},
// 跳转到添加界面
turnToAdd() {
// 未解锁拦截器
if (!this.unlock) {
this.turnToUnlock('点击新增未解锁拦截');
return;
}
let home_state = {
search_start : this.search_start,
search_content: this.search_content,
scrollHeight: this.scrollHeight
}
this.setHomeState([home_state, this])
this.$router.push('/add');
},
turnToDetail(content) {
let home_state = {
search_start : this.search_start,
search_content: this.search_content,
scrollHeight: this.scrollHeight
}
this.setHomeState([home_state, this])
this.$router.push({ name: 'Detail', params: { code_content: content } });
},
// 跳转到某页面
turnToPage(url) {
let home_state = {
search_start : this.search_start,
search_content: this.search_content,
scrollHeight: this.scrollHeight
}
this.setHomeState([home_state, this])
this.$router.push(url);
},
// 复制成功
onCopyUrl(e) {
this.snakebar_msg = this.lang.copy.successful;
this.show_snackbar = true;
},
// 复制失败
onErrorUrl(e) {
this.snakebar_msg = this.lang.copy.failed;
this.show_snackbar = true;
},
// 分离数据title
separateTitle(data_list) {
let title_list = [];
for (let i in data_list) {
title_list.push(data_list[i].title);
}
this.titles = title_list;
console.log('数据头部分离完成');
},
// 模糊搜索
fuzzySearch(cache_list) {
// 空内容拦截器,显示全部密码
if (!this.search_content.trim()) {
this.show_list = cache_list;
return;
}
let data_list = [];
for (let i in cache_list) {
if (this.generReg(this.search_content.trim()).test(cache_list[i].title)) {
data_list.push(cache_list[i]);
}
}
this.show_list = data_list;
console.log('模糊搜索完成')
},
// 模糊搜索辅助轮
generReg(val) {
let head = '(.*)(';
let tail = ')(.*)';
let body = val.split('').join(')(.*)(');
return new RegExp(head + body + tail, 'i');
},
// 下载apk
downloadApk() {
window.open('https://assets.lacus.site/canary/canary.apk');
},
// 一键回顶
scrollTop() {
this.$refs.list_placeholder.parentNode.parentNode.scrollTo({
top: 0,
behavior: 'smooth'
});
console.log('一键回顶成功');
},
// 判断是否有class
hasClass(obj, cls) {
return obj.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
},
// 增加class
addClass(obj, cls) {
if (!this.hasClass(obj, cls)) obj.className += ' ' + cls;
},
// 改变class
toggleClass(obj, cls) {
if (this.hasClass(obj, cls)) {
this.removeClass(obj, cls);
} else {
this.addClass(obj, cls);
}
},
// 移除class
removeClass(obj, cls) {
if (this.hasClass(obj, cls)) {
var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
obj.className = obj.className.replace(reg, ' ');
}
},
// 点击处理vconsole
toggleVc() {
const nowTime = new Date().getTime();
if (nowTime - this.lastClickTime < 3000) {
this.count++;
} else {
this.count = 0;
}
this.lastClickTime = nowTime;
if (this.count >= 10) {
let vconDom = document.getElementById('__vconsole');
this.toggleClass(vconDom, 'vconsole-show');
this.count = 0;
}
},
// 记录滚动高度
scroll(e) {
this.scrollHeight = this.$refs.list_placeholder.parentNode.parentNode.scrollTop
}
},
created() {
this.lang = lang().home.CHS;
console.log('临时语言系统加载完成');
this.init();
},
mounted() {
// 获取浏览器可视区域高度
this.clientHeight = `${document.documentElement.clientHeight}`;
//document.body.clientWidth;
window.onresize = function temp() {
this.clientHeight = `${document.documentElement.clientHeight}`;
setHtmlFontSize();
}.bind(this);
this.$refs.list_placeholder.parentNode.parentNode.addEventListener('scroll', this.scroll)
},
watch: {
// 如果 `clientHeight` 发生改变,这个函数就会运行
clientHeight: function() {
this.changeFixed(this.clientHeight);
},
// 如果 `search_start` 发生改变,这个函数就会运行
search_start: function() {
if (this.search_start) {
this.cache_list = JSON.parse(JSON.stringify(this.show_list));
console.log('搜索阵列展开,缓存设置完成');
} else {
this.search_content = ''
this.show_list = JSON.parse(JSON.stringify(this.cache_list));
this.cache_list = [];
console.log('搜索阵列关闭,缓存清空完成');
}
},
// 如果 `search_content` 发生改变,这个函数就会运行
search_content: function() {
// 确保只有在搜索阵列展开后才会进行搜索
if(this.search_start) {
this.fuzzySearch(this.cache_list);
}
}
},
beforeDestroy() {},
components: {}
};
</script>
<style scoped lang="scss" type="text/scss">
@import '../../style/main';
.home {
width: 100%;
// max-width: 500px;
min-height: 100%;
background: #fff;
// margin: 0 auto;
.drawer-banner {
padding: 1rem 0.3rem 0.3rem 0.3rem;
box-sizing: border-box;
background: $main-color;
color: #fff;
position: relative;
.default-avatar {
color: #fff;
margin-bottom: 0.3rem;
}
.md-caption {
color: #fbfbfb;
}
}
.code-card {
p:first-of-type {
margin-top: 0.3rem;
}
p:last-of-type {
margin-bottom: 0.3rem;
}
// padding: 0.3rem 0;
// border-bottom: 1px #eee solid;
}
.md-toolbar {
position: fixed;
top: 0;
}
.md-toolbar-row {
order: 12;
// margin-bottom: .3rem !important;
}
.md-toolbar-section-start,
.md-toolbar-section-end,
.md-toolbar-row {
min-height: 56px;
}
.md-toolbar-row {
margin-top: -10px;
}
.md-speed-dial.md-bottom-right {
position: fixed !important;
}
}
// Demo purposes only
.md-drawer {
width: 240px;
max-width: calc(100vw - 125px);
}
.md-app-scroller > div {
overflow-y: scroll !important;
}
.face {
color: #fff !important;
}
.page-title {
flex: 1;
min-height: 56px;
line-height: 56px;
}
.sroll-top-area {
flex: 1;
min-height: 56px;
}
</style>