支持自定义字段、维护捐赠者信息
This commit is contained in:
parent
dc390458b3
commit
dd4ee2c326
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -4,7 +4,7 @@
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$/password-xl-service" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="graalvm-21" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@ -3,7 +3,6 @@
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/password-xl.iml" filepath="$PROJECT_DIR$/password-xl.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/password-xl-service.main.iml" filepath="$PROJECT_DIR$/.idea/modules/password-xl-service.main.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/password-xl-web.iml" filepath="$PROJECT_DIR$/password-xl-web.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
|
@ -48,10 +48,10 @@
|
||||
|
||||
### 未来计划
|
||||
- [ ] 浏览器插件,自动找出密码并填入(安全问题?)
|
||||
- [ ] 安卓客户端、安卓密码管理器、指纹
|
||||
- [ ] 苹果客户端、密码管理器
|
||||
- [ ] PC客户端
|
||||
- [ ] Linux 客户端
|
||||
- [x] 安卓客户端
|
||||
- [x] 苹果客户端
|
||||
- [x] PC客户端
|
||||
- [x] Linux 客户端
|
||||
- [ ] 小程序客户端
|
||||
- [ ] 主题功能,默认,小棕熊,可爱,可换背景
|
||||
- [ ] 密钥文件存储
|
||||
@ -68,16 +68,16 @@
|
||||
- [x] 超时锁定 1
|
||||
- [x] 回收站 1
|
||||
- [x] 密码备份 2
|
||||
- [ ] 分词搜索 1
|
||||
- [ ] 分词搜索,近义词搜索(实现难度较大,暂不考虑) 1
|
||||
- [x] 手势密码
|
||||
- [ ] 自定义字段 1
|
||||
- [x] 自定义字段 1
|
||||
- [x] 注销账户 1
|
||||
- [x] 本地存储 3
|
||||
- [ ] 漫游式引导 2
|
||||
- [x] 漫游式引导 2
|
||||
- [ ] 阿里云注册指引、腾讯云注册指引 10
|
||||
- [ ] 私有部署前端教程、私有部署后端教程、打包代码 10
|
||||
- [ ] 部署服务,设置演示账号 4
|
||||
- [ ] 整体说明文档 10
|
||||
- [ ] 私有服务 6
|
||||
- [ ] 官方服务 12
|
||||
- [x] 私有部署前端教程、私有部署后端教程 10
|
||||
- [x] 部署服务,设置演示账号 4
|
||||
- [x] 整体说明文档 10
|
||||
- [x] 私有服务 6
|
||||
- [x] 官方服务 12
|
||||
- [ ] 密码防丢 1
|
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4580024 */
|
||||
src: url('iconfont.woff2?t=1720790383207') format('woff2'),
|
||||
url('iconfont.woff?t=1720790383207') format('woff'),
|
||||
url('iconfont.ttf?t=1720790383207') format('truetype');
|
||||
src: url('iconfont.woff2?t=1728618428208') format('woff2'),
|
||||
url('iconfont.woff?t=1728618428208') format('woff'),
|
||||
url('iconfont.ttf?t=1728618428208') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-clean:before {
|
||||
content: "\e626";
|
||||
}
|
||||
|
||||
.icon-money:before {
|
||||
content: "\e643";
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,6 +1,13 @@
|
||||
<!--关于项目-->
|
||||
<script setup lang="ts">
|
||||
import packageJson from '../../../../package.json'
|
||||
|
||||
const newFunctions = ref([
|
||||
'密码支持自定义信息',
|
||||
'导出、导入Excel',
|
||||
'支持Windows、Android客户端',
|
||||
'支持停用动态壁纸',
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -21,6 +28,14 @@ import packageJson from '../../../../package.json'
|
||||
<el-descriptions-item label="作者微信:">{{ packageJson.contributors[0].weChat }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="margin-top: 0">最近更新</h2>
|
||||
</div>
|
||||
<div>
|
||||
<ol style="padding-left: 25px;line-height: 26px">
|
||||
<li v-for="item in newFunctions">{{ item }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
@ -8,7 +8,7 @@ import {useRefStore} from "@/stores/RefStore.ts";
|
||||
const passwordStore = usePasswordStore()
|
||||
const refStore = useRefStore()
|
||||
|
||||
const headers = ['名称', '地址', '用户名', '密码', '标签', '备注', '创建时间', '收藏'];
|
||||
const headers = ['名称', '地址', '用户名', '密码', '标签', '备注', '创建时间', '收藏', '自定义信息'];
|
||||
|
||||
// 导出密码
|
||||
const exportExcel = async (downloadTemplate: boolean) => {
|
||||
@ -40,7 +40,16 @@ const startExportExcel = async (downloadTemplate: boolean) => {
|
||||
labels = getLabelNamesByIds(item.labels)
|
||||
}
|
||||
let createTime = formatterDate(item.addTime, 'YYYY-MM-DD HH:mm:ss');
|
||||
return [item.title, item.address, item.username, item.password, labels, item.remark, createTime, item.favorite ? '是' : '否'];
|
||||
let customFieldStr = ''
|
||||
if (item.customFields && item.customFields.length) {
|
||||
for (let i = 0; i < item.customFields.length; i++) {
|
||||
customFieldStr += item.customFields[i].key + ':' + item.customFields[i].val
|
||||
if (i !== item.customFields.length - 1) {
|
||||
customFieldStr += '\r\n'
|
||||
}
|
||||
}
|
||||
}
|
||||
return [item.title, item.address, item.username, item.password, labels, item.remark, createTime, item.favorite ? '是' : '否', customFieldStr];
|
||||
});
|
||||
console.log('导出密码,导出数据:', contents.length)
|
||||
// 添加数据行
|
||||
@ -79,6 +88,9 @@ const startExportExcel = async (downloadTemplate: boolean) => {
|
||||
case 8: // 收藏
|
||||
worksheet.getColumn(index).width = 10;
|
||||
break;
|
||||
case 9: // 自定义信息
|
||||
worksheet.getColumn(index).width = 20;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1,7 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import ExcelJS from 'exceljs';
|
||||
import {Label, Password, PasswordStatus} from "@/types";
|
||||
import {comparePassword, displaySize, formatterDate, getPasswordLabelNames, incrId, parseDate, parseLabels} from "@/utils/global.ts";
|
||||
import {
|
||||
comparePassword,
|
||||
displaySize,
|
||||
formatterDate,
|
||||
getPasswordLabelNames,
|
||||
incrId,
|
||||
parseDate,
|
||||
parseLabels
|
||||
} from "@/utils/global.ts";
|
||||
import {Buffer} from "buffer";
|
||||
import {usePasswordStore} from "@/stores/PasswordStore.ts";
|
||||
import {useRefStore} from "@/stores/RefStore.ts";
|
||||
@ -110,6 +118,7 @@ const importData = async (buffer: Buffer) => {
|
||||
addTime: headers.indexOf('创建时间'),
|
||||
favorite: headers.indexOf('收藏'),
|
||||
labels: headers.indexOf('标签'),
|
||||
customFields: headers.indexOf('自定义信息'),
|
||||
};
|
||||
|
||||
if (!columnIndex.title) {
|
||||
@ -139,6 +148,7 @@ const importData = async (buffer: Buffer) => {
|
||||
let addTime = columnIndex.addTime !== -1 ? row.getCell(columnIndex.addTime) : null
|
||||
let labels = columnIndex.labels !== -1 ? row.getCell(columnIndex.labels) : null
|
||||
let favorite = columnIndex.favorite !== -1 ? row.getCell(columnIndex.favorite) : null
|
||||
let customFields = columnIndex.customFields !== -1 ? row.getCell(columnIndex.customFields) : null
|
||||
|
||||
// 收藏
|
||||
let favoriteValue = favorite && favorite.value === '是'
|
||||
@ -176,12 +186,23 @@ const importData = async (buffer: Buffer) => {
|
||||
deleteTime: 0,
|
||||
favoriteTime: favoriteValue ? Date.now() : 0,
|
||||
favorite: favoriteValue,
|
||||
customFields: {},
|
||||
customFields: [],
|
||||
labels: passwordLabel,
|
||||
status: PasswordStatus.NORMAL,
|
||||
bgColor: '',
|
||||
};
|
||||
|
||||
if (customFields && customFields.value) {
|
||||
let fieldStr = customFields.value.split('\r\n');
|
||||
for (let j = 0; j < fieldStr.length; j++) {
|
||||
let field = fieldStr[j].split(":")
|
||||
password.customFields.push({
|
||||
key: field[0],
|
||||
val: field.length > 1 ? field[1] : ''
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 将密码对象添加到数组中
|
||||
importPasswords.value.push(password);
|
||||
}
|
||||
@ -404,6 +425,13 @@ defineExpose({
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="自定义信息" min-width="150px" prop="customFields">
|
||||
<template #default="scope">
|
||||
<el-tag v-for="field in scope.row.customFields" class="table-label">
|
||||
{{ field.key }}:{{ field.val }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<el-tooltip content="请选择要导入的密码" placement="top" :disabled="!importBtnDis()">
|
||||
|
@ -680,7 +680,7 @@ const isAndroid = () => {
|
||||
<div class="function-div">
|
||||
<div class="function-header" style="margin-bottom: 5px">
|
||||
<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">导出 Excel
|
||||
</el-button>
|
||||
</div>
|
||||
<el-divider class="function-line"/>
|
||||
@ -691,7 +691,7 @@ const isAndroid = () => {
|
||||
<div class="function-div">
|
||||
<div class="function-header" style="margin-bottom: 5px">
|
||||
<el-text tag="b">导入密码</el-text>
|
||||
<el-button plain type="primary" @click="refStore.importExcelRef.importExcel" size="small">导入
|
||||
<el-button plain type="primary" @click="refStore.importExcelRef.importExcel" size="small">导入 Excel
|
||||
</el-button>
|
||||
</div>
|
||||
<el-divider class="function-line"/>
|
||||
|
@ -3,6 +3,7 @@
|
||||
import packageJson from '../../../../package.json'
|
||||
|
||||
const donateArray = [
|
||||
{name: 'Seeking dream', money: 10, time: '2024-10-11'},
|
||||
{name: 'Ching', money: 10, time: '2024-07-13'},
|
||||
{name: '李春雨', money: 5, time: '2024-07-12'},
|
||||
{name: '张博文', money: 50, time: '2024-07-12'},
|
||||
@ -66,20 +67,20 @@ const donateArray = [
|
||||
</li>
|
||||
</ul>
|
||||
<el-row>
|
||||
<el-col :xs="{span:24}" :sm="{span:24}" :md="{span:9}">
|
||||
<div style="text-align: center;position: relative;left: -10px">
|
||||
<el-col :xs="{span:24}" :sm="{span:24}" :md="{span:9}" style="text-align: center">
|
||||
<div style="text-align: center;">
|
||||
<h3 style="margin-top: 4px">微信赞赏码</h3>
|
||||
<div style="padding: 5px 20px;text-align: center">
|
||||
<img alt="" style="width: 80%;border-radius: 50%" src="../../../assets/images/donate.png">
|
||||
<img alt="" style="width: 80%;max-width: 300px;border-radius: 50%" src="../../../assets/images/donate.png">
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="{span:24}" :sm="{span:24}" :md="{span:15}" style="text-align: center">
|
||||
<el-col :xs="{span:24}" :sm="{span:24}" :md="{span:15}" style="text-align: center;padding-right: 10px">
|
||||
<h3 style="margin-top: 4px">捐赠者列表</h3>
|
||||
<el-table :data="donateArray" border stripe>
|
||||
<el-table-column label="名称" prop="name"></el-table-column>
|
||||
<el-table-column label="时间" min-width="120px" prop="time"></el-table-column>
|
||||
<el-table-column label="金额" min-width="80px" prop="money"></el-table-column>
|
||||
<el-table-column label="时间" min-width="60px" prop="time"></el-table-column>
|
||||
<el-table-column label="金额" min-width="40px" prop="money"></el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -88,6 +88,14 @@ const cardStyle = (password: Password) => {
|
||||
};
|
||||
}
|
||||
|
||||
const getBackStype = () =>{
|
||||
if(settingStore.setting.dynamicBackground){
|
||||
return {'background-color':passwordStore.isDark?'rgba(0,0,0,0.1)':'rgba(255,255,255,0.1)'}
|
||||
}else{
|
||||
return {'background-color':passwordStore.isDark?'rgba(0,0,0,1)':'rgba(255,255,255,1)'}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -99,7 +107,7 @@ const cardStyle = (password: Password) => {
|
||||
style="display: grid;padding: 6px;"
|
||||
:style="{'grid-template-columns':'repeat('+getRowCount()+', 1fr)'}">
|
||||
<div v-for="password in passwordStore.visPasswordArray">
|
||||
<el-card body-style="height: 100%;" :style="{'background-color':passwordStore.isDark?'rgba(0,0,0,0.1)':'rgba(255,255,255,0.1)'}" class="password-card">
|
||||
<el-card body-style="height: 100%;" :style="getBackStype" class="password-card">
|
||||
<template #header>
|
||||
<div class="password-header-div" :style="cardStyle(password)">
|
||||
<div>
|
||||
@ -131,7 +139,7 @@ const cardStyle = (password: Password) => {
|
||||
&& !password.password
|
||||
&& !password.remark
|
||||
&& !password.labels.length
|
||||
&& !(password.customFields && Object.keys(password.customFields).length > 0)">
|
||||
&& !(password.customFields && password.customFields.length > 0)">
|
||||
<el-text style="margin: 20px 0">
|
||||
空空如也!
|
||||
</el-text>
|
||||
@ -187,11 +195,13 @@ const cardStyle = (password: Password) => {
|
||||
</el-text>
|
||||
<div class="clear"></div>
|
||||
</li>
|
||||
<li v-for="field in password.customFields">
|
||||
<el-text class="password-field-name">{{ field }}:</el-text>
|
||||
<el-text class="password-field-value">{{ password.customFields[field] }}</el-text>
|
||||
<div class="clear"></div>
|
||||
</li>
|
||||
<template v-for="field in password.customFields">
|
||||
<li v-if="field.key || field.val">
|
||||
<el-text class="password-field-name">{{ field.key }}:</el-text>
|
||||
<el-text class="password-field-value">{{ field.val }}</el-text>
|
||||
<div class="clear"></div>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<template #footer>
|
||||
<div style="display: flex;justify-content: space-between">
|
||||
@ -227,7 +237,6 @@ const cardStyle = (password: Password) => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100% - 12px);
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.password-strength {
|
||||
|
@ -44,7 +44,7 @@ const initPasswordForm = (): Password => {
|
||||
// 是否收藏
|
||||
favorite: false,
|
||||
// 自定义字段
|
||||
customFields: {},
|
||||
customFields: [],
|
||||
// 标签id列表
|
||||
labels: [],
|
||||
// 密码状态 正常 or 已删除
|
||||
@ -165,6 +165,13 @@ const getDefaultExpandedKeys = (): number[] => {
|
||||
return passwordStore.labelArray.map(label => label.id);
|
||||
}
|
||||
|
||||
const addField = () => {
|
||||
passwordForm.value.customFields.push({
|
||||
key: '',
|
||||
val: '',
|
||||
})
|
||||
}
|
||||
|
||||
// 保存密码
|
||||
const savePassword = async (passwordFormFormRef: any) => {
|
||||
console.log('保存密码')
|
||||
@ -217,6 +224,10 @@ const handleKeyDown = (event: KeyboardEvent) => {
|
||||
}
|
||||
};
|
||||
|
||||
const delField = (index) => {
|
||||
passwordForm.value.customFields.splice(index, 1)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
@ -247,7 +258,7 @@ onBeforeUnmount(() => {
|
||||
<el-input :ref="(el: any) => refStore.passwordFormTitleRef = el" v-model="passwordForm.title" autocomplete="new-password" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-input v-model="passwordForm.address" autocomplete="new-password" clearable></el-input>
|
||||
<el-input placeholder="https://" v-model="passwordForm.address" autocomplete="new-password" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-autocomplete
|
||||
@ -361,10 +372,22 @@ onBeforeUnmount(() => {
|
||||
class="bg-color-item">
|
||||
<span class="iconfont icon-check-mark" style="font-size: 14px" v-show="passwordForm.bgColor === color"></span>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item label="自定义">
|
||||
<el-card v-if="passwordForm.customFields && passwordForm.customFields.length > 0">
|
||||
<el-row v-for="(field,index) in passwordForm.customFields"
|
||||
:style="{'margin-bottom': index !== passwordForm.customFields.length - 1?'15px':'0'}">
|
||||
<el-input style="width: 30%" v-model="field.key" placeholder="名称"></el-input>
|
||||
<el-input style="width: 54%;margin-left: 3%" v-model="field.val" placeholder="内容"></el-input>
|
||||
<el-button style="width: 10%;margin-left: 3%;" @click="delField(index)" type="danger" plain>
|
||||
<span class="iconfont icon-clean"></span>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-button v-else @click="addField()" type="primary" plain>添加自定义信息</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="text-align: right">
|
||||
<div style="display: flex;justify-content: end">
|
||||
<el-button @click="savePassword(refStore.passwordFormFormRef)" :ref="(el: any) => refStore.passwordFormSaveBtnRef = el" type="primary">保存</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
@ -446,4 +469,5 @@ onBeforeUnmount(() => {
|
||||
text-align: center;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
</style>
|
@ -133,7 +133,7 @@ export class PasswordManagerImpl implements PasswordManager {
|
||||
deleteTime: 0,
|
||||
favoriteTime: 0,
|
||||
favorite: true,
|
||||
customFields: {},
|
||||
customFields: [],
|
||||
labels: [label.id],
|
||||
status: PasswordStatus.NORMAL,
|
||||
bgColor: ''
|
||||
|
@ -128,9 +128,14 @@ export const usePasswordStore = defineStore('passwordStore', {
|
||||
password.address,
|
||||
password.username,
|
||||
password.password,
|
||||
password.remark,
|
||||
...Object.entries(password.customFields).flat()
|
||||
password.remark
|
||||
].filter(Boolean);
|
||||
if (password.customFields) {
|
||||
for (let i = 0; i < password.customFields.length; i++) {
|
||||
searchFields.push(password.customFields[i].key);
|
||||
searchFields.push(password.customFields[i].val);
|
||||
}
|
||||
}
|
||||
for (const field of searchFields) {
|
||||
if (searchStr(this.filterCondition.searchText.trim(), field)) {
|
||||
textSearchResult = true
|
||||
|
@ -1,6 +1,12 @@
|
||||
// 密码
|
||||
import {Ref} from "vue";
|
||||
|
||||
// 自定义字段
|
||||
export interface CustomField {
|
||||
key: string;
|
||||
val: string;
|
||||
}
|
||||
|
||||
// 密码
|
||||
export interface Password {
|
||||
id: number,
|
||||
@ -14,7 +20,7 @@ export interface Password {
|
||||
deleteTime: number,
|
||||
favoriteTime: number,
|
||||
favorite: boolean,
|
||||
customFields: { [key: string]: string },
|
||||
customFields: CustomField[],
|
||||
labels: Array<number>,
|
||||
status: PasswordStatus,
|
||||
bgColor: string,
|
||||
@ -77,7 +83,7 @@ export interface Setting {
|
||||
autoGeneratePassword: boolean,// 添加密码时是否默认自动一次
|
||||
generateRule: GenerateRule, // 密码生成规则
|
||||
easyConfuseChat: string,// 易混淆字符
|
||||
customFields: Array<string>, // 默认自定义字段
|
||||
customFields: CustomField[], // 默认自定义字段
|
||||
timeoutLock: number, // 超时锁定(秒)
|
||||
passwordDisplayMode: PasswordDisplayMode, // 密码展示方式
|
||||
autoLogin: boolean,// 记住登录信息
|
||||
|
@ -49,8 +49,6 @@ export async function copyText(text: string, silent: boolean = false) {
|
||||
// 文本搜索
|
||||
export const searchStr = (searchText: string, value: string): boolean => {
|
||||
if (!value) return false;
|
||||
|
||||
|
||||
const lowerSearchText = searchText.toLowerCase();
|
||||
const lowerValue = value.toLowerCase();
|
||||
|
||||
@ -170,8 +168,9 @@ export const sharePassword = (password: Password) => {
|
||||
}
|
||||
// 自定义字段
|
||||
if (password.customFields) {
|
||||
for (let field in password.customFields) {
|
||||
text += field + ': ' + password.customFields[field] + '\r\n'
|
||||
for (let i = 0; i < password.customFields.length; i++) {
|
||||
let field = password.customFields[i];
|
||||
text += field.key + ': ' + field.val + '\r\n'
|
||||
}
|
||||
}
|
||||
if (password.remark) {
|
||||
|
@ -131,7 +131,6 @@ if (['xs', 'sm'].includes(displaySize().value) && settingStore.setting.passwordD
|
||||
.password-card {
|
||||
margin: 10px;
|
||||
height: calc(100vh - 24px);
|
||||
background-color: rgba(255, 255, 255, 0.4);
|
||||
backdrop-filter: blur(50px);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user