560 lines
16 KiB
Vue
560 lines
16 KiB
Vue
<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>
|