适配安卓客户端
This commit is contained in:
parent
a0050dd94c
commit
7bf39b8a6d
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@ -5,7 +5,7 @@
|
|||||||
<profile name="Gradle Imported" enabled="true">
|
<profile name="Gradle Imported" enabled="true">
|
||||||
<outputRelativeToContentRoot value="true" />
|
<outputRelativeToContentRoot value="true" />
|
||||||
<processorPath useClasspath="false">
|
<processorPath useClasspath="false">
|
||||||
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.32/17d46b3e205515e1e8efd3ee4d57ce8018914163/lombok-1.18.32.jar" />
|
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.34/ec547ef414ab1d2c040118fb9c1c265ada63af14/lombok-1.18.34.jar" />
|
||||||
</processorPath>
|
</processorPath>
|
||||||
<module name="password-xl-service.main" />
|
<module name="password-xl-service.main" />
|
||||||
</profile>
|
</profile>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.3.1'
|
id 'org.springframework.boot' version '3.3.2'
|
||||||
id 'io.spring.dependency-management' version '1.1.5'
|
id 'io.spring.dependency-management' version '1.1.6'
|
||||||
id 'org.graalvm.buildtools.native' version '0.10.2'
|
id 'org.graalvm.buildtools.native' version '0.10.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,15 +26,18 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
implementation 'cn.hutool:hutool-all:5.8.27'
|
implementation 'cn.hutool:hutool-all:5.8.29'
|
||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
implementation 'com.alibaba.fastjson2:fastjson2:2.0.51'
|
implementation 'com.alibaba.fastjson2:fastjson2:2.0.52'
|
||||||
implementation 'io.hotmoka:toml4j:0.7.3'
|
implementation 'io.hotmoka:toml4j:0.7.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.bootBuildImage {
|
tasks.bootBuildImage {
|
||||||
imageName = "${project.name}"
|
imageName = "${project.name}"
|
||||||
|
docker {
|
||||||
|
host = "////./pipe/dockerDesktopLinuxEngine"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('printVersion') {
|
tasks.register('printVersion') {
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import {contextBridge, ipcRenderer} from 'electron'
|
import {contextBridge, ipcRenderer} from 'electron'
|
||||||
|
|
||||||
// 设置环境变量
|
|
||||||
contextBridge.exposeInMainWorld('env', {
|
|
||||||
electron: true
|
|
||||||
})
|
|
||||||
|
|
||||||
// 开放给源码的API
|
// 开放给源码的API
|
||||||
contextBridge.exposeInMainWorld('electronAPI', {
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
getFile: async (fileName) => ipcRenderer.invoke('get-file', fileName),
|
getFile: async (fileName) => ipcRenderer.invoke('get-file', fileName),
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.8 KiB |
1
password-xl-web/src/components.d.ts
vendored
1
password-xl-web/src/components.d.ts
vendored
@ -8,6 +8,7 @@ export {}
|
|||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
About: typeof import('./components/common/setting/About.vue')['default']
|
About: typeof import('./components/common/setting/About.vue')['default']
|
||||||
|
AndroidLoginForm: typeof import('./components/login/AndroidLoginForm.vue')['default']
|
||||||
BackupAndRecovery: typeof import('./components/common/setting/BackupAndRecovery.vue')['default']
|
BackupAndRecovery: typeof import('./components/common/setting/BackupAndRecovery.vue')['default']
|
||||||
CancelAccount: typeof import('./components/common/setting/CancelAccount.vue')['default']
|
CancelAccount: typeof import('./components/common/setting/CancelAccount.vue')['default']
|
||||||
CommonProblem: typeof import('./components/common/setting/CommonProblem.vue')['default']
|
CommonProblem: typeof import('./components/common/setting/CommonProblem.vue')['default']
|
||||||
|
@ -14,7 +14,7 @@ const visFastLogin = ref(false)
|
|||||||
// 触发快速登录提示
|
// 触发快速登录提示
|
||||||
const fastLoginTip = (form: any) => {
|
const fastLoginTip = (form: any) => {
|
||||||
console.log('快速登录提示')
|
console.log('快速登录提示')
|
||||||
if (location.href.indexOf('autoLogin') !== -1 || (window.env && window.env.electron)) {
|
if (location.href.indexOf('autoLogin') !== -1 || window.electronAPI || window.androidAPI) {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -46,9 +46,9 @@ const config = {
|
|||||||
// 每行/每列手势点数量
|
// 每行/每列手势点数量
|
||||||
pointCellSize: 3,
|
pointCellSize: 3,
|
||||||
// 手势点在其所在区域的占比
|
// 手势点在其所在区域的占比
|
||||||
cellPointRadio: 0.3,
|
cellPointRadio: 0.33,
|
||||||
// 手势点颜色
|
// 手势点颜色
|
||||||
gesturePointColor: '#d5d5d5',
|
gesturePointColor: '#cacaca',
|
||||||
// 手势点错误时的颜色
|
// 手势点错误时的颜色
|
||||||
gesturePointErrorColor: '#F56C6C',
|
gesturePointErrorColor: '#F56C6C',
|
||||||
// 圆心占比
|
// 圆心占比
|
||||||
@ -289,7 +289,7 @@ const mousemove = (e: MouseEvent) => {
|
|||||||
|
|
||||||
// 鼠标抬起
|
// 鼠标抬起
|
||||||
const mouseup = () => {
|
const mouseup = () => {
|
||||||
if(!pressed.value) return
|
if (!pressed.value) return
|
||||||
pressed.value = false
|
pressed.value = false
|
||||||
hoverPoint.value = null
|
hoverPoint.value = null
|
||||||
canvasUp()
|
canvasUp()
|
||||||
@ -297,7 +297,7 @@ const mouseup = () => {
|
|||||||
|
|
||||||
// 鼠标离开
|
// 鼠标离开
|
||||||
const mouseleave = () => {
|
const mouseleave = () => {
|
||||||
if(!pressed.value) return
|
if (!pressed.value) return
|
||||||
pressed.value = false
|
pressed.value = false
|
||||||
hoverPoint.value = null
|
hoverPoint.value = null
|
||||||
canvasUp()
|
canvasUp()
|
||||||
@ -330,12 +330,23 @@ const touchmove = (e: TouchEvent) => {
|
|||||||
|
|
||||||
// 手指抬起
|
// 手指抬起
|
||||||
const touchend = () => {
|
const touchend = () => {
|
||||||
if(!pressed.value) return
|
if (!pressed.value) return
|
||||||
pressed.value = false
|
pressed.value = false
|
||||||
hoverPoint.value = null
|
hoverPoint.value = null
|
||||||
canvasUp()
|
canvasUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createHDCanvas(canvas: any, w: number, h: number) {
|
||||||
|
const ratio = window.devicePixelRatio || 1;
|
||||||
|
canvas.width = w * ratio; // 实际渲染像素
|
||||||
|
canvas.height = h * ratio; // 实际渲染像素
|
||||||
|
canvas.style.width = `${w}px`; // 控制显示大小
|
||||||
|
canvas.style.height = `${h}px`; // 控制显示大小
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
ctx.scale(ratio, ratio)
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化画板
|
// 初始化画板
|
||||||
const initCanvas = () => {
|
const initCanvas = () => {
|
||||||
if (!gestureDivRef.value || !canvasRef.value) return
|
if (!gestureDivRef.value || !canvasRef.value) return
|
||||||
@ -344,7 +355,8 @@ const initCanvas = () => {
|
|||||||
console.log('画板大小:', size)
|
console.log('画板大小:', size)
|
||||||
canvasRef.value.width = size
|
canvasRef.value.width = size
|
||||||
canvasRef.value.height = size
|
canvasRef.value.height = size
|
||||||
ctx = canvasRef.value.getContext('2d') as CanvasRenderingContext2D
|
|
||||||
|
ctx = createHDCanvas(canvasRef.value, size, size) as CanvasRenderingContext2D
|
||||||
|
|
||||||
let incrId = 0
|
let incrId = 0
|
||||||
allPointArray.length = 0
|
allPointArray.length = 0
|
||||||
|
@ -194,6 +194,7 @@ defineExpose({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
|
top="20vh"
|
||||||
:width="['xs', 'sm'].includes(displaySize().value)?'95%':'400px'"
|
:width="['xs', 'sm'].includes(displaySize().value)?'95%':'400px'"
|
||||||
v-model="visVerify"
|
v-model="visVerify"
|
||||||
:close-on-click-modal="passwordStore.serviceStatus !== ServiceStatus.NO_LOGIN"
|
:close-on-click-modal="passwordStore.serviceStatus !== ServiceStatus.NO_LOGIN"
|
||||||
|
@ -9,17 +9,16 @@ import packageJson from '../../../../package.json'
|
|||||||
</div>
|
</div>
|
||||||
<div style="padding: 10px;">
|
<div style="padding: 10px;">
|
||||||
<el-descriptions :column="1">
|
<el-descriptions :column="1">
|
||||||
<el-descriptions-item label="项目名称:">{{ packageJson.name}}</el-descriptions-item>
|
<el-descriptions-item label="项目名称:">{{ packageJson.name}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="当前版本:">{{ packageJson.version }}</el-descriptions-item>
|
<el-descriptions-item label="当前版本:">{{ packageJson.version }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="官方网站:">
|
<el-descriptions-item label="官方网站:">
|
||||||
<el-link type="primary" target="_blank" :href="packageJson.homepage">{{ packageJson.homepage }}</el-link>
|
<el-link type="primary" target="_blank" :href="packageJson.homepage">{{ packageJson.homepage }}</el-link>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="开源地址:">
|
<el-descriptions-item label="开源地址:">
|
||||||
<el-link type="primary" target="_blank" :href="packageJson.repository.url">{{ packageJson.repository.url }}</el-link>
|
<el-link type="primary" target="_blank" :href="packageJson.repository.url">{{ packageJson.repository.url }}</el-link>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="作者名称:">{{ packageJson.contributors[0].name }}</el-descriptions-item>
|
<el-descriptions-item label="作者邮箱:">{{ packageJson.contributors[0].email }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="作者邮箱:">{{ packageJson.contributors[0].email }}</el-descriptions-item>
|
<el-descriptions-item label="作者微信:">{{ packageJson.contributors[0].weChat }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="作者微信:">{{ packageJson.contributors[0].weChat }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -6,19 +6,22 @@ import {useLoginStore} from "@/stores/LoginStore.ts";
|
|||||||
const loginStore = useLoginStore()
|
const loginStore = useLoginStore()
|
||||||
|
|
||||||
const isElectron = () => {
|
const isElectron = () => {
|
||||||
return window.env && window.env.electron
|
return !!window.electronAPI
|
||||||
|
}
|
||||||
|
const isAndroid = () => {
|
||||||
|
return !!window.androidAPI
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-form label-width="110px">
|
<el-form label-width="80px">
|
||||||
<template v-if="loginStore.loginType === 'oss'">
|
<template v-if="loginStore.loginType === 'oss'">
|
||||||
<div style="text-align: center;margin-bottom: 5px;width: 100%">
|
<div style="text-align: center;margin-bottom: 5px;width: 100%">
|
||||||
<img alt="" class="login-type-image" style="height: 28px" src="@/assets/images/login/oss.png">
|
<img alt="" class="login-type-image" style="height: 28px" src="@/assets/images/login/oss.png">
|
||||||
<el-text style="font-size: 24px">阿里云OSS</el-text>
|
<el-text style="font-size: 24px">阿里云OSS</el-text>
|
||||||
</div>
|
</div>
|
||||||
<el-divider></el-divider>
|
<el-divider></el-divider>
|
||||||
<div style="padding: 0 50px 0 20px">
|
<div>
|
||||||
<el-form-item label="region">
|
<el-form-item label="region">
|
||||||
<el-input :model-value="loginStore.loginForm.region" :readonly="true">
|
<el-input :model-value="loginStore.loginForm.region" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
@ -55,7 +58,7 @@ const isElectron = () => {
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="快速登录链接" v-if="!isElectron()">
|
<el-form-item label="快速登录链接" v-if="!isElectron() && !isAndroid()">
|
||||||
<el-input :model-value="getFastLoginLink(loginStore.loginForm)" :readonly="true">
|
<el-input :model-value="getFastLoginLink(loginStore.loginForm)" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button @click="copyText(getFastLoginLink(loginStore.loginForm))" class="copy-btn">
|
<el-button @click="copyText(getFastLoginLink(loginStore.loginForm))" class="copy-btn">
|
||||||
@ -72,7 +75,7 @@ const isElectron = () => {
|
|||||||
<el-text style="font-size: 24px">腾讯云COS</el-text>
|
<el-text style="font-size: 24px">腾讯云COS</el-text>
|
||||||
</div>
|
</div>
|
||||||
<el-divider></el-divider>
|
<el-divider></el-divider>
|
||||||
<div style="padding: 0 50px 0 20px">
|
<div>
|
||||||
<el-form-item label="region">
|
<el-form-item label="region">
|
||||||
<el-input :model-value="loginStore.loginForm.region" :readonly="true">
|
<el-input :model-value="loginStore.loginForm.region" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
@ -109,7 +112,7 @@ const isElectron = () => {
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="快速登录链接" v-if="!isElectron()">
|
<el-form-item label="快速登录链接" v-if="!isElectron() && !isAndroid()">
|
||||||
<el-input :model-value="getFastLoginLink(loginStore.loginForm)" :readonly="true">
|
<el-input :model-value="getFastLoginLink(loginStore.loginForm)" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button @click="copyText(getFastLoginLink(loginStore.loginForm))" class="copy-btn">
|
<el-button @click="copyText(getFastLoginLink(loginStore.loginForm))" class="copy-btn">
|
||||||
@ -126,7 +129,7 @@ const isElectron = () => {
|
|||||||
<el-text style="font-size: 24px">私有服务</el-text>
|
<el-text style="font-size: 24px">私有服务</el-text>
|
||||||
</div>
|
</div>
|
||||||
<el-divider></el-divider>
|
<el-divider></el-divider>
|
||||||
<div style="padding: 0 50px 0 20px">
|
<div>
|
||||||
<el-form-item label="服务地址">
|
<el-form-item label="服务地址">
|
||||||
<el-input :model-value="loginStore.loginForm.serverUrl" :readonly="true">
|
<el-input :model-value="loginStore.loginForm.serverUrl" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
@ -154,7 +157,7 @@ const isElectron = () => {
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="快速登录链接" v-if="!isElectron()">
|
<el-form-item label="快速登录链接" v-if="!isElectron() && !isAndroid()">
|
||||||
<el-input :model-value="getFastLoginLink(loginStore.loginForm)" :readonly="true">
|
<el-input :model-value="getFastLoginLink(loginStore.loginForm)" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button @click="copyText(getFastLoginLink(loginStore.loginForm))" class="copy-btn">
|
<el-button @click="copyText(getFastLoginLink(loginStore.loginForm))" class="copy-btn">
|
||||||
@ -167,9 +170,9 @@ const isElectron = () => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div style="text-align: center;margin-top: 50px;width: 100%">
|
<div style="text-align: center;margin-top: 50px;width: 100%">
|
||||||
<img alt="" class="login-type-image" style="height: 150px" src="@/assets/images/login/local.png">
|
<img alt="" class="login-type-image" style="height: 130px" src="@/assets/images/login/local.png">
|
||||||
<div style="margin-top: 40px">
|
<div style="margin-top: 25px">
|
||||||
<el-text style="font-size: 24px">您正在使用本地文件存储</el-text>
|
<el-text style="font-size: 22px">您正在使用本地文件存储</el-text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding: 0 50px 0 20px">
|
<div style="padding: 0 50px 0 20px">
|
||||||
|
@ -321,6 +321,11 @@ watch(() => settingStore.setting.generateRule, (newValue: GenerateRule) => {
|
|||||||
}, {
|
}, {
|
||||||
deep: true
|
deep: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const isAndroid = () => {
|
||||||
|
return !!window.androidAPI
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -488,9 +493,8 @@ watch(() => settingStore.setting.generateRule, (newValue: GenerateRule) => {
|
|||||||
<el-scrollbar :height="scrollbarHeight()">
|
<el-scrollbar :height="scrollbarHeight()">
|
||||||
<div class="function-div">
|
<div class="function-div">
|
||||||
<div class="function-header" style="margin-bottom: 5px">
|
<div class="function-header" style="margin-bottom: 5px">
|
||||||
<el-text tag="b">默认排序规则</el-text>
|
<el-text tag="b">密码排序</el-text>
|
||||||
<div>
|
<div>
|
||||||
默认根据
|
|
||||||
<el-select v-model="settingStore.setting.sortField" size="small" style="width: 90px;">
|
<el-select v-model="settingStore.setting.sortField" size="small" style="width: 90px;">
|
||||||
<el-option value="addTime" label="添加时间"/>
|
<el-option value="addTime" label="添加时间"/>
|
||||||
<el-option value="updateTime" label="修改时间"/>
|
<el-option value="updateTime" label="修改时间"/>
|
||||||
@ -503,7 +507,6 @@ watch(() => settingStore.setting.generateRule, (newValue: GenerateRule) => {
|
|||||||
<el-option :value="Sort.ASC" label="正序"/>
|
<el-option :value="Sort.ASC" label="正序"/>
|
||||||
<el-option :value="Sort.DESC" label="倒序"/>
|
<el-option :value="Sort.DESC" label="倒序"/>
|
||||||
</el-select>
|
</el-select>
|
||||||
排列
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-divider class="function-line"/>
|
<el-divider class="function-line"/>
|
||||||
@ -524,7 +527,7 @@ watch(() => settingStore.setting.generateRule, (newValue: GenerateRule) => {
|
|||||||
<div class="function-div">
|
<div class="function-div">
|
||||||
<div class="function-header" style="margin-bottom: 5px">
|
<div class="function-header" style="margin-bottom: 5px">
|
||||||
<el-text tag="b">在密码列表中显示时间</el-text>
|
<el-text tag="b">在密码列表中显示时间</el-text>
|
||||||
<el-select v-model="settingStore.setting.showTimeForTable" size="small" style="width: 120px;">
|
<el-select v-model="settingStore.setting.showTimeForTable" size="small" style="width: 100px;">
|
||||||
<el-option value="no" label="不显示时间"/>
|
<el-option value="no" label="不显示时间"/>
|
||||||
<el-option value="addTime" label="显示添加时间"/>
|
<el-option value="addTime" label="显示添加时间"/>
|
||||||
<el-option value="updateTime" label="显示修改时间"/>
|
<el-option value="updateTime" label="显示修改时间"/>
|
||||||
@ -641,7 +644,7 @@ watch(() => settingStore.setting.generateRule, (newValue: GenerateRule) => {
|
|||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane>
|
<el-tab-pane v-if="!isAndroid()">
|
||||||
<template #label>
|
<template #label>
|
||||||
<el-text>
|
<el-text>
|
||||||
<span class="iconfont icon-recovery action-icon" style="color: #ffc400"></span>
|
<span class="iconfont icon-recovery action-icon" style="color: #ffc400"></span>
|
||||||
@ -649,67 +652,67 @@ watch(() => settingStore.setting.generateRule, (newValue: GenerateRule) => {
|
|||||||
</el-text>
|
</el-text>
|
||||||
</template>
|
</template>
|
||||||
<el-scrollbar :height="scrollbarHeight()">
|
<el-scrollbar :height="scrollbarHeight()">
|
||||||
<div class="function-div">
|
<div class="function-div">
|
||||||
<div class="function-header" style="margin-bottom: 5px">
|
<div class="function-header" style="margin-bottom: 5px">
|
||||||
<el-text tag="b">备份密码</el-text>
|
<el-text tag="b">备份密码</el-text>
|
||||||
<el-button plain type="primary" @click="refStore.backupAndRecoveryRef.backup(false)" size="small">备份
|
<el-button plain type="primary" @click="refStore.backupAndRecoveryRef.backup(false)" size="small">备份
|
||||||
</el-button>
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-divider class="function-line"/>
|
||||||
|
<el-text type="info" tag="p" style="text-indent: 10px">
|
||||||
|
密码备份功能可以安全的将加密后的密码文件导出,在需要时通过
|
||||||
|
<el-text>恢复备份密码</el-text>
|
||||||
|
功能还原。
|
||||||
|
</el-text>
|
||||||
</div>
|
</div>
|
||||||
<el-divider class="function-line"/>
|
<div class="function-div">
|
||||||
<el-text type="info" tag="p" style="text-indent: 10px">
|
<div class="function-header" style="margin-bottom: 5px">
|
||||||
密码备份功能可以安全的将加密后的密码文件导出,在需要时通过
|
<el-text tag="b">恢复备份密码</el-text>
|
||||||
<el-text>恢复备份密码</el-text>
|
<el-button plain type="primary" @click="refStore.backupAndRecoveryRef.recovery" size="small">恢复
|
||||||
功能还原。
|
</el-button>
|
||||||
</el-text>
|
</div>
|
||||||
</div>
|
<el-divider class="function-line"/>
|
||||||
<div class="function-div">
|
<el-text type="info" tag="p" style="text-indent: 10px">
|
||||||
<div class="function-header" style="margin-bottom: 5px">
|
此功能可以将备份文件中的密码恢复到密码列表。在验证备份文件的主密码后,您可以选择合并或覆盖方式恢复密码。
|
||||||
<el-text tag="b">恢复备份密码</el-text>
|
</el-text>
|
||||||
<el-button plain type="primary" @click="refStore.backupAndRecoveryRef.recovery" size="small">恢复
|
|
||||||
</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
<el-divider class="function-line"/>
|
|
||||||
<el-text type="info" tag="p" style="text-indent: 10px">
|
|
||||||
此功能可以将备份文件中的密码恢复到密码列表。在验证备份文件的主密码后,您可以选择合并或覆盖方式恢复密码。
|
|
||||||
</el-text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="function-div">
|
<div class="function-div">
|
||||||
<div class="function-header" style="margin-bottom: 5px">
|
<div class="function-header" style="margin-bottom: 5px">
|
||||||
<el-text tag="b">导出密码</el-text>
|
<el-text tag="b">导出密码</el-text>
|
||||||
<el-button plain type="primary" @click="refStore.exportExcelRef.exportExcel(false)" size="small">导出
|
<el-button plain type="primary" @click="refStore.exportExcelRef.exportExcel(false)" size="small">导出
|
||||||
</el-button>
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-divider class="function-line"/>
|
||||||
|
<el-text type="info" tag="p" style="text-indent: 10px">
|
||||||
|
导出密码会将敏感信息明文存储在excel表格中,请注意密码安全。若您仅需要迁移账号或备份密码建议使用密码备份与恢复功能更加高效安全。
|
||||||
|
</el-text>
|
||||||
</div>
|
</div>
|
||||||
<el-divider class="function-line"/>
|
<div class="function-div">
|
||||||
<el-text type="info" tag="p" style="text-indent: 10px">
|
<div class="function-header" style="margin-bottom: 5px">
|
||||||
导出密码会将敏感信息明文存储在excel表格中,请注意密码安全。若您仅需要迁移账号或备份密码建议使用密码备份与恢复功能更加高效安全。
|
<el-text tag="b">导入密码</el-text>
|
||||||
</el-text>
|
<el-button plain type="primary" @click="refStore.importExcelRef.importExcel" size="small">导入
|
||||||
</div>
|
</el-button>
|
||||||
<div class="function-div">
|
</div>
|
||||||
<div class="function-header" style="margin-bottom: 5px">
|
<el-divider class="function-line"/>
|
||||||
<el-text tag="b">导入密码</el-text>
|
<el-text type="info" tag="p" style="text-indent: 10px">
|
||||||
<el-button plain type="primary" @click="refStore.importExcelRef.importExcel" size="small">导入
|
密码导入功能支持按照密码模板导入excel文件中的密码,您可以通过下载模板功能获取密码模板。
|
||||||
</el-button>
|
</el-text>
|
||||||
</div>
|
</div>
|
||||||
<el-divider class="function-line"/>
|
<div class="function-div">
|
||||||
<el-text type="info" tag="p" style="text-indent: 10px">
|
<div class="function-header" style="margin-bottom: 5px">
|
||||||
密码导入功能支持按照密码模板导入excel文件中的密码,您可以通过下载模板功能获取密码模板。
|
<el-text tag="b">下载导入模板</el-text>
|
||||||
</el-text>
|
<el-button plain type="primary" @click="refStore.exportExcelRef.exportExcel(true)" size="small">下载
|
||||||
</div>
|
</el-button>
|
||||||
<div class="function-div">
|
</div>
|
||||||
<div class="function-header" style="margin-bottom: 5px">
|
<el-divider class="function-line"/>
|
||||||
<el-text tag="b">下载导入模板</el-text>
|
<el-text type="info" tag="p" style="text-indent: 10px">
|
||||||
<el-button plain type="primary" @click="refStore.exportExcelRef.exportExcel(true)" size="small">下载
|
您可在下载的Excel模板中按照要求填写您的密码列表(
|
||||||
</el-button>
|
<el-text type="danger">密码名称为必填</el-text>
|
||||||
|
),然后使用密码导入功能导入您的密码。
|
||||||
|
</el-text>
|
||||||
</div>
|
</div>
|
||||||
<el-divider class="function-line"/>
|
</el-scrollbar>
|
||||||
<el-text type="info" tag="p" style="text-indent: 10px">
|
|
||||||
您可在下载的Excel模板中按照要求填写您的密码列表(
|
|
||||||
<el-text type="danger">密码名称为必填</el-text>
|
|
||||||
),然后使用密码导入功能导入您的密码。
|
|
||||||
</el-text>
|
|
||||||
</div>
|
|
||||||
</el-scrollbar>
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane>
|
<el-tab-pane>
|
||||||
<template #label>
|
<template #label>
|
||||||
@ -752,7 +755,7 @@ watch(() => settingStore.setting.generateRule, (newValue: GenerateRule) => {
|
|||||||
</el-text>
|
</el-text>
|
||||||
</template>
|
</template>
|
||||||
<el-scrollbar :height="scrollbarHeight()">
|
<el-scrollbar :height="scrollbarHeight()">
|
||||||
<div style="padding: 0 15px">
|
<div style="padding: 0">
|
||||||
<About></About>
|
<About></About>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
@ -765,7 +768,7 @@ watch(() => settingStore.setting.generateRule, (newValue: GenerateRule) => {
|
|||||||
</el-text>
|
</el-text>
|
||||||
</template>
|
</template>
|
||||||
<el-scrollbar :height="scrollbarHeight()">
|
<el-scrollbar :height="scrollbarHeight()">
|
||||||
<div style="padding: 0 15px">
|
<div style="padding: 0">
|
||||||
<SupportMe></SupportMe>
|
<SupportMe></SupportMe>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
@ -93,7 +93,7 @@ const cardStyle = (password: Password) => {
|
|||||||
<template>
|
<template>
|
||||||
<EmptyList v-if="!passwordStore.visPasswordArray.length"></EmptyList>
|
<EmptyList v-if="!passwordStore.visPasswordArray.length"></EmptyList>
|
||||||
<el-scrollbar
|
<el-scrollbar
|
||||||
height="calc(100vh - 90px)"
|
height="calc(100vh - 85px)"
|
||||||
v-if="passwordStore.visPasswordArray.length">
|
v-if="passwordStore.visPasswordArray.length">
|
||||||
<div
|
<div
|
||||||
style="display: grid;padding: 6px;"
|
style="display: grid;padding: 6px;"
|
||||||
@ -138,7 +138,7 @@ const cardStyle = (password: Password) => {
|
|||||||
</li>
|
</li>
|
||||||
<li v-if="password.address">
|
<li v-if="password.address">
|
||||||
<el-text class="password-field-name">地址:</el-text>
|
<el-text class="password-field-name">地址:</el-text>
|
||||||
<el-text class="password-field-value" style="max-width: 20vw">
|
<el-text class="password-field-value">
|
||||||
<el-link v-if="isUrl(password.address)" type="primary" :href="password.address" target="_blank">
|
<el-link v-if="isUrl(password.address)" type="primary" :href="password.address" target="_blank">
|
||||||
{{ password.address }}
|
{{ password.address }}
|
||||||
</el-link>
|
</el-link>
|
||||||
|
@ -97,7 +97,7 @@ const tableRowStyle = (data: { row: any, rowIndex: number }): CSSProperties => {
|
|||||||
<el-table
|
<el-table
|
||||||
:data="passwordStore.visPasswordArray"
|
:data="passwordStore.visPasswordArray"
|
||||||
ref="passwordTableRef"
|
ref="passwordTableRef"
|
||||||
height="calc(100vh - 120px)"
|
height="calc(100vh - 120px)"
|
||||||
style="background-color: rgba(0,0,0,0);"
|
style="background-color: rgba(0,0,0,0);"
|
||||||
:header-row-style="{'background-color':'rgba(0,0,0,0)'}"
|
:header-row-style="{'background-color':'rgba(0,0,0,0)'}"
|
||||||
:header-cell-style="{'background-color':'rgba(0,0,0,0)'}"
|
:header-cell-style="{'background-color':'rgba(0,0,0,0)'}"
|
||||||
|
47
password-xl-web/src/components/login/AndroidLoginForm.vue
Normal file
47
password-xl-web/src/components/login/AndroidLoginForm.vue
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import {usePasswordStore} from "@/stores/PasswordStore.ts";
|
||||||
|
import {RespData} from "@/types";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {browserFingerprint, encryptAES} from "@/utils/security.ts";
|
||||||
|
import {DatabaseForAndroid} from "@/database/DatabaseForAndroid.ts";
|
||||||
|
|
||||||
|
const passwordStore = usePasswordStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const useLocalLogin = async () => {
|
||||||
|
console.log('android 点击登录')
|
||||||
|
let database = new DatabaseForAndroid();
|
||||||
|
|
||||||
|
// 初始化密码管理器
|
||||||
|
passwordStore.passwordManager.login(database).then((resp: RespData) => {
|
||||||
|
if (!resp.status) {
|
||||||
|
console.log('android 登录失败:', resp)
|
||||||
|
ElNotification.error({title: '登录失败', message: resp.message})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用浏览器指纹加密登录信息
|
||||||
|
// 此刻想法:因为此时刚刚登录还没有主密码,因此将登录信息用浏览器指纹加密后保存在session中,如果存在pinia中页面一刷新就没了,用户体验不好
|
||||||
|
let fingerprint = browserFingerprint()
|
||||||
|
let loginForm = {loginType: 'android'}
|
||||||
|
let ciphertext = encryptAES(fingerprint, JSON.stringify(loginForm));
|
||||||
|
// 保存到sessionStorage
|
||||||
|
sessionStorage.setItem('loginForm', ciphertext)
|
||||||
|
|
||||||
|
console.log('android 登录 跳转首页')
|
||||||
|
router.push('/')
|
||||||
|
}).catch(() => null)
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
useLocalLogin
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -1,20 +1,31 @@
|
|||||||
<!--登录类型选择组件-->
|
<!--登录类型选择组件-->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ElectronLoginForm from "@/components/login/ElectronLoginForm.vue";
|
import ElectronLoginForm from "@/components/login/ElectronLoginForm.vue";
|
||||||
|
import AndroidLoginForm from "@/components/login/AndroidLoginForm.vue";
|
||||||
|
|
||||||
const electronLoginFormRef = ref()
|
const electronLoginFormRef = ref()
|
||||||
|
const androidLoginFormRef = ref()
|
||||||
const emits = defineEmits(['loginTypeChange'])
|
const emits = defineEmits(['loginTypeChange'])
|
||||||
|
|
||||||
const isElectron = () => {
|
const isElectron = () => {
|
||||||
return window.env && window.env.electron
|
return !!window.electronAPI
|
||||||
|
}
|
||||||
|
const isAndroid = () => {
|
||||||
|
return !!window.androidAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
const electronStore = () => {
|
const electronStore = () => {
|
||||||
console.log('使用本地存储electron')
|
console.log('使用本地存储electron')
|
||||||
emits('loginTypeChange','electrons')
|
emits('loginTypeChange','electron')
|
||||||
electronLoginFormRef.value.useLocalLogin()
|
electronLoginFormRef.value.useLocalLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const androidStore = () => {
|
||||||
|
console.log('使用本地存储android')
|
||||||
|
emits('loginTypeChange','android')
|
||||||
|
androidLoginFormRef.value.useLocalLogin()
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -45,7 +56,13 @@ const electronStore = () => {
|
|||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12" v-if="isElectron()">
|
<el-col :span="12" v-if="isElectron()">
|
||||||
<div class="login-type-item electron" @click="electronStore">
|
<div class="login-type-item local" @click="electronStore">
|
||||||
|
<img alt="" src="../../assets/images/login/local.png">
|
||||||
|
<div><el-text>本地存储</el-text></div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" v-if="isAndroid()">
|
||||||
|
<div class="login-type-item local" @click="androidStore">
|
||||||
<img alt="" src="../../assets/images/login/local.png">
|
<img alt="" src="../../assets/images/login/local.png">
|
||||||
<div><el-text>本地存储</el-text></div>
|
<div><el-text>本地存储</el-text></div>
|
||||||
</div>
|
</div>
|
||||||
@ -60,6 +77,7 @@ const electronStore = () => {
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<ElectronLoginForm ref="electronLoginFormRef"></ElectronLoginForm>
|
<ElectronLoginForm ref="electronLoginFormRef"></ElectronLoginForm>
|
||||||
|
<AndroidLoginForm ref="androidLoginFormRef"></AndroidLoginForm>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -137,12 +155,5 @@ const electronStore = () => {
|
|||||||
background: rgba(40, 193, 39, 0.4);
|
background: rgba(40, 193, 39, 0.4);
|
||||||
box-shadow: 0 0 10px #bbb;
|
box-shadow: 0 0 10px #bbb;
|
||||||
}
|
}
|
||||||
.login-type-item.electron {
|
|
||||||
background: rgba(40, 193, 39, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-type-item.electron:hover {
|
|
||||||
background: rgba(40, 193, 39, 0.4);
|
|
||||||
box-shadow: 0 0 10px #bbb;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
80
password-xl-web/src/database/DatabaseForAndroid.ts
Normal file
80
password-xl-web/src/database/DatabaseForAndroid.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* android存储引擎
|
||||||
|
*/
|
||||||
|
import {Database, RespData} from "@/types";
|
||||||
|
|
||||||
|
export class DatabaseForAndroid implements Database {
|
||||||
|
|
||||||
|
// 文件名称定义
|
||||||
|
private fileNames = {
|
||||||
|
store: 'store.json',
|
||||||
|
setting: 'setting.json',
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录并验证文件权限、初始化基本信息
|
||||||
|
async login(_form: any): Promise<RespData> {
|
||||||
|
console.log('使用android存储')
|
||||||
|
return Promise.resolve({status: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取密码数据
|
||||||
|
async getStoreData(): Promise<string> {
|
||||||
|
return this.getFile(this.fileNames.store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置密码数据
|
||||||
|
async setStoreData(text: string) {
|
||||||
|
return this.uploadFile(this.fileNames.store, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除密码数据
|
||||||
|
async deleteStoreData() {
|
||||||
|
return this.deleteFile(this.fileNames.store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设置
|
||||||
|
async getSettingData(): Promise<string> {
|
||||||
|
return this.getFile(this.fileNames.setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新设置
|
||||||
|
async setSettingData(text: string) {
|
||||||
|
return this.uploadFile(this.fileNames.setting, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除设置数据
|
||||||
|
async deleteSettingData() {
|
||||||
|
return this.deleteFile(this.fileNames.setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取android文件
|
||||||
|
private async getFile(fileName: string): Promise<string> {
|
||||||
|
console.log('获取android文件', fileName)
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
let data = await window.androidAPI.getFile(fileName)
|
||||||
|
if (data) {
|
||||||
|
resolve(data);
|
||||||
|
} else {
|
||||||
|
resolve('')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传android文件
|
||||||
|
private async uploadFile(fileName: string, content: string): Promise<RespData> {
|
||||||
|
console.log('上传android文件', fileName)
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
window.androidAPI.uploadFile(fileName, content)
|
||||||
|
resolve({status: true})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除android文件
|
||||||
|
private async deleteFile(fileName: string): Promise<RespData> {
|
||||||
|
console.log('删除android文件', fileName)
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
window.androidAPI.deleteFile(fileName)
|
||||||
|
resolve({status: true})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import {useSettingStore} from "@/stores/SettingStore.ts";
|
|||||||
import {useRefStore} from "@/stores/RefStore.ts";
|
import {useRefStore} from "@/stores/RefStore.ts";
|
||||||
import {DatabaseForPrivate} from "@/database/DatabaseForPrivate.ts";
|
import {DatabaseForPrivate} from "@/database/DatabaseForPrivate.ts";
|
||||||
import {DatabaseForElectron} from "@/database/DatabaseForElectron.ts";
|
import {DatabaseForElectron} from "@/database/DatabaseForElectron.ts";
|
||||||
|
import {DatabaseForAndroid} from "@/database/DatabaseForAndroid.ts";
|
||||||
|
|
||||||
|
|
||||||
export const useLoginStore = defineStore('loginStore', {
|
export const useLoginStore = defineStore('loginStore', {
|
||||||
@ -30,8 +31,10 @@ export const useLoginStore = defineStore('loginStore', {
|
|||||||
database = new DatabaseForCOS()
|
database = new DatabaseForCOS()
|
||||||
} else if (loginForm.loginType === 'private') {
|
} else if (loginForm.loginType === 'private') {
|
||||||
database = new DatabaseForPrivate()
|
database = new DatabaseForPrivate()
|
||||||
} else if (loginForm.loginType === 'electron') {
|
} else if (loginForm.loginType === 'electron') {
|
||||||
database = new DatabaseForElectron()
|
database = new DatabaseForElectron()
|
||||||
|
} else if (loginForm.loginType === 'android') {
|
||||||
|
database = new DatabaseForAndroid()
|
||||||
} else {
|
} else {
|
||||||
console.error('未知的登录类型,无法自动登录:', loginForm.loginType)
|
console.error('未知的登录类型,无法自动登录:', loginForm.loginType)
|
||||||
resolve(false)
|
resolve(false)
|
||||||
|
@ -90,7 +90,7 @@ export const usePasswordStore = defineStore('passwordStore', {
|
|||||||
return array
|
return array
|
||||||
},
|
},
|
||||||
// 收藏的密码列表
|
// 收藏的密码列表
|
||||||
favoritePasswordArray(): Array<Password>{
|
favoritePasswordArray(): Array<Password> {
|
||||||
console.log('获取收藏的密码列表')
|
console.log('获取收藏的密码列表')
|
||||||
let array = this.passwordArray.filter(p => p.favorite)
|
let array = this.passwordArray.filter(p => p.favorite)
|
||||||
.sort((a: Password, b: Password) => {
|
.sort((a: Password, b: Password) => {
|
||||||
@ -195,21 +195,18 @@ export const usePasswordStore = defineStore('passwordStore', {
|
|||||||
let isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
let isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
||||||
useDark().value = isDarkTheme.matches
|
useDark().value = isDarkTheme.matches
|
||||||
this.topicMode = isDarkTheme.matches ? TopicMode.DARK : TopicMode.LIGHT
|
this.topicMode = isDarkTheme.matches ? TopicMode.DARK : TopicMode.LIGHT
|
||||||
if (window.env && window.env.electron) {
|
window.electronAPI?.setTopic('system');
|
||||||
window.electronAPI.setTopic('system');
|
window.androidAPI?.setTopic('system');
|
||||||
}
|
|
||||||
} else if (topic === TopicMode.DARK) {
|
} else if (topic === TopicMode.DARK) {
|
||||||
useDark().value = true
|
useDark().value = true
|
||||||
this.topicMode = TopicMode.DARK
|
this.topicMode = TopicMode.DARK
|
||||||
if (window.env && window.env.electron) {
|
window.electronAPI?.setTopic(this.topicMode);
|
||||||
window.electronAPI.setTopic(this.topicMode);
|
window.androidAPI?.setTopic(this.topicMode);
|
||||||
}
|
|
||||||
} else if (topic === TopicMode.LIGHT) {
|
} else if (topic === TopicMode.LIGHT) {
|
||||||
useDark().value = false
|
useDark().value = false
|
||||||
this.topicMode = TopicMode.LIGHT
|
this.topicMode = TopicMode.LIGHT
|
||||||
if (window.env && window.env.electron) {
|
window.electronAPI?.setTopic(this.topicMode);
|
||||||
window.electronAPI.setTopic(this.topicMode);
|
window.androidAPI?.setTopic(this.topicMode);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 全局加载
|
// 全局加载
|
||||||
|
11
password-xl-web/src/types/types.d.ts
vendored
11
password-xl-web/src/types/types.d.ts
vendored
@ -31,13 +31,8 @@ interface FileSystemWritableFileStream extends WritableStream {
|
|||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Env {
|
|
||||||
electron: true
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
env: Env;
|
|
||||||
showOpenFilePicker: (options?: FilePickerOptions) => Promise<FileHandle[]>;
|
showOpenFilePicker: (options?: FilePickerOptions) => Promise<FileHandle[]>;
|
||||||
showSaveFilePicker: (options?: SaveFilePickerOptions) => Promise<FileHandle>;
|
showSaveFilePicker: (options?: SaveFilePickerOptions) => Promise<FileHandle>;
|
||||||
electronAPI: {
|
electronAPI: {
|
||||||
@ -46,5 +41,11 @@ declare global {
|
|||||||
deleteFile(fileName: string): Promise<RespData>;
|
deleteFile(fileName: string): Promise<RespData>;
|
||||||
setTopic(topic: string): void;
|
setTopic(topic: string): void;
|
||||||
}
|
}
|
||||||
|
androidAPI: {
|
||||||
|
getFile(fileName: string): Promise<string>;
|
||||||
|
uploadFile(fileName: string, content: string): Promise<RespData>;
|
||||||
|
deleteFile(fileName: string): Promise<RespData>;
|
||||||
|
setTopic(topic: string): void;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -75,7 +75,7 @@ if (['xs', 'sm'].includes(displaySize().value) && settingStore.setting.passwordD
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 手机版 -->
|
<!-- 手机版 -->
|
||||||
<div v-else style="backdrop-filter: blur(50px);height: calc(100vh - 70px)"
|
<div v-else style="backdrop-filter: blur(50px);height: 100vh"
|
||||||
:style="{'background-color': passwordStore.isDark?'rgba(0,0,0,0.4)':'rgba(255,255,255,0.4)'}">
|
:style="{'background-color': passwordStore.isDark?'rgba(0,0,0,0.4)':'rgba(255,255,255,0.4)'}">
|
||||||
<!-- 密码表头 -->
|
<!-- 密码表头 -->
|
||||||
<PasswordHeader></PasswordHeader>
|
<PasswordHeader></PasswordHeader>
|
||||||
|
@ -12,7 +12,7 @@ const loginStep = ref(1)
|
|||||||
const loginTypeChange = (type: string) => {
|
const loginTypeChange = (type: string) => {
|
||||||
console.log('登录,选择了登录方式:', type)
|
console.log('登录,选择了登录方式:', type)
|
||||||
loginStore.loginType = type;
|
loginStore.loginType = type;
|
||||||
if (type === 'electron') {
|
if (type === 'electron' || type === 'android') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user