chore: update

This commit is contained in:
xiaoxian521 2024-04-10 18:30:08 +08:00
parent 309e31fcd9
commit 02b2568427
19 changed files with 391 additions and 340 deletions

View File

@ -1,4 +1,5 @@
buttons:
pureAccountSettings: Account Settings
pureLoginOut: LoginOut
pureLogin: Login
pureSystemSet: Open ProjectConfig
@ -10,7 +11,6 @@ buttons:
pureCloseAllTabs: Close AllTabs
pureContentFullScreen: Content FullScreen
pureContentExitFullScreen: Content ExitFullScreen
pureUserSettings: User Settings
menus:
pureHome: Home
pureLogin: Login
@ -176,4 +176,4 @@ login:
purePassWordRuleReg: The password format should be any combination of 8-18 digits
purePassWordSureReg: Please enter confirm password
purePassWordDifferentReg: The two passwords do not match!
purePassWordUpdateReg: Password has been updated
purePassWordUpdateReg: Password has been updated

View File

@ -1,4 +1,5 @@
buttons:
pureAccountSettings: 账户设置
pureLoginOut: 退出系统
pureLogin: 登录
pureSystemSet: 打开项目配置
@ -10,7 +11,6 @@ buttons:
pureCloseAllTabs: 关闭全部标签页
pureContentFullScreen: 内容区全屏
pureContentExitFullScreen: 内容区退出全屏
pureUserSettings: 个人设置
menus:
pureHome: 首页
pureLogin: 登录

21
mock/mine.ts Normal file
View File

@ -0,0 +1,21 @@
import { defineFakeRoute } from "vite-plugin-fake-server/client";
export default defineFakeRoute([
{
url: "/mine",
method: "get",
response: () => {
return {
success: true,
data: {
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "Coder",
email: "pureadmin@163.com",
phone: "15888886789",
description: "一个热爱开源的前端工程师"
}
};
}
}
]);

View File

@ -1,52 +0,0 @@
import { defineFakeRoute } from "vite-plugin-fake-server/client";
export default defineFakeRoute([
{
url: "/get-regions",
method: "get",
response: () => {
return {
success: true,
data: [
{
code: "001",
name: "中国",
children: [
{
code: "001001",
name: "北京市",
children: [
{
code: "001001001",
name: "东城区"
},
{
code: "001001002",
name: "西城区"
}
// 其他区
]
},
{
code: "001002",
name: "上海市",
children: [
{
code: "001002001",
name: "黄浦区"
},
{
code: "001002002",
name: "徐汇区"
}
// 其他区
]
}
// 其他城市
]
}
]
};
}
}
]);

View File

@ -1,21 +0,0 @@
import { defineFakeRoute } from "vite-plugin-fake-server/client";
export default defineFakeRoute([
{
url: "/get-user-info",
method: "get",
response: () => {
return {
success: true,
data: {
avatarUrl: "https://avatars.githubusercontent.com/u/44761321",
nickName: "企丸丸",
introduce: "我是幻兽帕鲁里的明星",
regionCode: "001002001",
address: "冰鸟密域祭坛地下城",
userName: "admin"
}
};
}
}
]);

View File

@ -23,8 +23,3 @@ export const formUpload = data => {
}
);
};
/**所在区域数据*/
export const getRegions = (params?: object) => {
return http.request<Result>("get", "/get-regions", { params });
};

View File

@ -29,12 +29,18 @@ export type RefreshTokenResult = {
};
export type UserInfo = {
avatarUrl: string;
nickName: string;
introduce: string;
regionCode: string;
address: string;
userName: string;
/** 头像 */
avatar: string;
/** 用户名 */
username: string;
/** 昵称 */
nickname: string;
/** 邮箱 */
email: string;
/** 联系电话 */
phone: string;
/** 简介 */
description: string;
};
export type UserInfoResult = {
@ -47,12 +53,12 @@ export const getLogin = (data?: object) => {
return http.request<UserResult>("post", "/login", { data });
};
/** 刷新token */
/** 刷新`token` */
export const refreshTokenApi = (data?: object) => {
return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
};
/**获取个人信息 */
export const getUserInfo = (data?: object) => {
return http.request<UserInfoResult>("get", "/get-user-info", { data });
/** 账户设置-个人信息 */
export const getMine = (data?: object) => {
return http.request<UserInfoResult>("get", "/mine", { data });
};

View File

@ -8,22 +8,22 @@ import Breadcrumb from "./sidebar/breadCrumb.vue";
import topCollapse from "./sidebar/topCollapse.vue";
import { useTranslationLang } from "../hooks/useTranslationLang";
import globalization from "@/assets/svg/globalization.svg?component";
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import Check from "@iconify-icons/ep/check";
import UserSettingsLine from "@iconify-icons/ri/user-settings-line";
const {
layout,
device,
logout,
handleOpenUserSettings,
onPanel,
pureApp,
username,
userAvatar,
avatarsStyle,
toggleSideBar,
toAccountSettings,
getDropdownItemStyle,
getDropdownItemClass
} = useNav();
@ -94,12 +94,12 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
</span>
<template #dropdown>
<el-dropdown-menu class="logout">
<el-dropdown-item @click="handleOpenUserSettings">
<el-dropdown-item @click="toAccountSettings">
<IconifyIconOffline
:icon="UserSettingsLine"
:icon="AccountSettingsIcon"
style="margin: 5px"
/>
{{ t("buttons.pureUserSettings") }}
{{ t("buttons.pureAccountSettings") }}
</el-dropdown-item>
<el-dropdown-item @click="logout">
<IconifyIconOffline
@ -187,7 +187,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
}
.logout {
max-width: 160px;
max-width: 120px;
::v-deep(.el-dropdown-menu__item) {
display: inline-flex;

View File

@ -9,10 +9,10 @@ import { useNav } from "@/layout/hooks/useNav";
import { useTranslationLang } from "../../hooks/useTranslationLang";
import { usePermissionStoreHook } from "@/store/modules/permission";
import globalization from "@/assets/svg/globalization.svg?component";
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import Check from "@iconify-icons/ep/check";
import UserSettingsLine from "@iconify-icons/ri/user-settings-line";
const menuRef = ref();
@ -27,9 +27,9 @@ const {
username,
userAvatar,
avatarsStyle,
toAccountSettings,
getDropdownItemStyle,
getDropdownItemClass,
handleOpenUserSettings
getDropdownItemClass
} = useNav();
const defaultActive = computed(() =>
@ -109,9 +109,12 @@ nextTick(() => {
<p v-if="username" class="dark:text-white">{{ username }}</p>
</span>
<template #dropdown>
<el-dropdown-item @click="handleOpenUserSettings">
<IconifyIconOffline :icon="UserSettingsLine" style="margin: 5px" />
{{ t("buttons.pureUserSettings") }}
<el-dropdown-item @click="toAccountSettings">
<IconifyIconOffline
:icon="AccountSettingsIcon"
style="margin: 5px"
/>
{{ t("buttons.pureAccountSettings") }}
</el-dropdown-item>
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">

View File

@ -12,10 +12,10 @@ import { getParentPaths, findRouteByPath } from "@/router/utils";
import { useTranslationLang } from "../../hooks/useTranslationLang";
import { usePermissionStoreHook } from "@/store/modules/permission";
import globalization from "@/assets/svg/globalization.svg?component";
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import Check from "@iconify-icons/ep/check";
import UserSettingsLine from "@iconify-icons/ri/user-settings-line";
const menuRef = ref();
const defaultActive = ref(null);
@ -31,9 +31,9 @@ const {
userAvatar,
getDivStyle,
avatarsStyle,
toAccountSettings,
getDropdownItemStyle,
getDropdownItemClass,
handleOpenUserSettings
getDropdownItemClass
} = useNav();
function getDefaultActive(routePath) {
@ -142,9 +142,12 @@ watch(
<p v-if="username" class="dark:text-white">{{ username }}</p>
</span>
<template #dropdown>
<el-dropdown-item @click="handleOpenUserSettings">
<IconifyIconOffline :icon="UserSettingsLine" style="margin: 5px" />
{{ t("buttons.pureUserSettings") }}
<el-dropdown-item @click="toAccountSettings">
<IconifyIconOffline
:icon="AccountSettingsIcon"
style="margin: 5px"
/>
{{ t("buttons.pureAccountSettings") }}
</el-dropdown-item>
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">

View File

@ -1,126 +0,0 @@
<script setup lang="ts">
import { ref } from "vue";
import { useNav } from "@/layout/hooks/useNav";
import leftLine from "@iconify-icons/ri/arrow-left-s-line";
import userLine from "@iconify-icons/ri/user-3-line";
import profileLine from "@iconify-icons/ri/profile-line";
import { useRouter } from "vue-router";
import { getUserInfo } from "@/api/user";
import UserInfoManage from "@/layout/components/userSettings/userInfoManage.vue";
import SafeManage from "@/layout/components/userSettings/safeManage.vue";
const router = useRouter();
const { getLogo, isCollapse } = useNav();
const userInfo = ref({
nickName: "",
avatarUrl: "",
userName: ""
});
getUserInfo().then(res => {
userInfo.value = res.data;
});
const handleBack = () => {
router.go(-1);
};
const witchPane = ref("userInfoManage");
const panes = [
{
key: "userInfoManage",
label: "基本设置",
icon: userLine,
component: UserInfoManage
},
{
key: "safeManage",
label: "安全设置",
icon: profileLine,
component: SafeManage
}
];
</script>
<template>
<el-container class="h-full">
<el-aside class="settings-sidebar px-2" width="210px">
<el-menu :default-active="witchPane" class="settings-menu">
<el-menu-item @click="handleBack">
<div class="flex items-center">
<IconifyIconOffline :icon="leftLine" />
<img :src="getLogo()" class="w-6 h-6" alt="logo" />
<span class="ml-2">返回</span>
</div>
</el-menu-item>
<li class="flex items-center ml-8 mt-4">
<div>
<el-avatar :size="80" :src="userInfo.avatarUrl" />
</div>
<div class="pl-4 flex-col">
<p>{{ userInfo.nickName }}</p>
<p>
<el-text class="mt-2" type="info">{{
userInfo.userName
}}</el-text>
</p>
</div>
</li>
<el-menu-item
v-for="item in panes"
:key="item.key"
:index="item.key"
@click="witchPane = item.key"
>
<div class="flex items-center">
<el-icon><IconifyIconOffline :icon="item.icon" /></el-icon>
<span>{{ item.label }}</span>
</div>
</el-menu-item>
</el-menu>
</el-aside>
<el-main>
<component :is="panes.find(item => item.key === witchPane).component" />
</el-main>
</el-container>
</template>
<style lang="scss" scoped>
.settings-sidebar {
background: $menuBg;
}
.settings-menu {
color: $subMenuActiveText;
background-color: transparent !important;
border: none !important;
::v-deep(.el-menu-item) {
position: relative;
background-color: transparent !important;
div {
z-index: 1;
}
&.is-active {
span {
color: #fff !important;
}
i {
color: #fff !important;
}
&::before {
position: absolute;
inset: 0 8px;
margin: 4px 0;
clear: both;
content: "";
background: var(--el-color-primary) !important;
border-radius: 3px;
}
}
}
}
</style>

View File

@ -1,56 +0,0 @@
<script setup lang="ts"></script>
<template>
<el-card shadow="never">
<template #header>安全设置</template>
<div class="flex items-center">
<div class="flex-1">
<p>账户密码</p>
<p class="wp-4">
<el-text class="mx-1" type="info">当前密码强度</el-text>
</p>
</div>
<div class="w-64">
<el-link type="primary" :underline="false">修改</el-link>
</div>
</div>
<el-divider />
<div class="flex items-center">
<div class="flex-1">
<p>密保手机</p>
<p class="wp-4">
<el-text class="mx-1" type="info">已经绑定手机138****1234</el-text>
</p>
</div>
<div class="w-64">
<el-link type="primary" :underline="false">修改</el-link>
</div>
</div>
<el-divider />
<div class="flex items-center">
<div class="flex-1">
<p>密保问题</p>
<p class="wp-4">
<el-text class="mx-1" type="info">
未设置密保问题密保问题可有效保护账户安全
</el-text>
</p>
</div>
<div class="w-64">
<el-link type="primary" :underline="false">设置</el-link>
</div>
</div>
<el-divider />
<div class="flex items-center">
<div class="flex-1">
<p>备用邮箱</p>
<p class="wp-4">
<el-text class="mx-1" type="info">已绑定邮箱ant***sign.com</el-text>
</p>
</div>
<div class="w-64">
<el-link type="primary" :underline="false">修改</el-link>
</div>
</div>
</el-card>
</template>

View File

@ -99,8 +99,8 @@ export function useNav() {
emitter.emit("openPanel");
}
function handleOpenUserSettings() {
router.push({ name: "UserSettings" });
function toAccountSettings() {
router.push({ name: "AccountSettings" });
}
function toggleSideBar() {
@ -163,8 +163,8 @@ export function useNav() {
userAvatar,
avatarsStyle,
tooltipEffect,
toAccountSettings,
getDropdownItemStyle,
getDropdownItemClass,
handleOpenUserSettings
getDropdownItemClass
};
}

View File

@ -40,13 +40,13 @@ export default [
}
},
{
path: "/user-settings",
name: "UserSettings",
component: () => import("@/layout/components/userSettings/index.vue"),
path: "/account-settings",
name: "AccountSettings",
component: () => import("@/views/account-settings/index.vue"),
meta: {
title: $t("buttons.pureUserSettings"),
title: $t("buttons.pureAccountSettings"),
showLink: false,
rank: 103
rank: 104
}
}
] satisfies Array<RouteConfigsTable>;

View File

@ -0,0 +1,48 @@
<script setup lang="ts">
//
</script>
<template>
<div class="ml-[120px] min-w-[180px] max-w-[70%]">
<h3 class="my-8">账户管理</h3>
<div class="flex items-center">
<div class="flex-1">
<p>账户密码</p>
<el-text class="mx-1" type="info">当前密码强度</el-text>
</div>
<el-button type="primary" text>修改</el-button>
</div>
<el-divider />
<div class="flex items-center">
<div class="flex-1">
<p>密保手机</p>
<el-text class="mx-1" type="info">已经绑定手机158****6789</el-text>
</div>
<el-button type="primary" text>修改</el-button>
</div>
<el-divider />
<div class="flex items-center">
<div class="flex-1">
<p>密保问题</p>
<el-text class="mx-1" type="info">
未设置密保问题密保问题可有效保护账户安全
</el-text>
</div>
<el-button type="primary" text>修改</el-button>
</div>
<el-divider />
<div class="flex items-center">
<div class="flex-1">
<p>备用邮箱</p>
<el-text class="mx-1" type="info">已绑定邮箱pure***@163.com</el-text>
</div>
<el-button type="primary" text>修改</el-button>
</div>
</div>
</template>
<style lang="scss" scoped>
.el-divider--horizontal {
border-top: 0.1px var(--el-border-color) var(--el-border-style);
}
</style>

View File

@ -0,0 +1,58 @@
<script setup lang="ts">
import { ref } from "vue";
import { message } from "@/utils/message";
const list = ref([
{
title: "账户密码",
illustrate: "其他用户的消息将以站内信的形式通知",
checked: true
},
{
title: "系统消息",
illustrate: "系统消息将以站内信的形式通知",
checked: true
},
{
title: "待办任务",
illustrate: "待办任务将以站内信的形式通知",
checked: false
}
]);
function onChange(val, item) {
message(`${item.title}设置成功`, { type: "success" });
}
</script>
<template>
<div class="ml-[120px] min-w-[180px] max-w-[70%]">
<h3 class="my-8">偏好设置</h3>
<div v-for="(item, index) in list" :key="index">
<div class="flex items-center">
<div class="flex-1">
<p>{{ item.title }}</p>
<p class="wp-4">
<el-text class="mx-1" type="info">
{{ item.illustrate }}
</el-text>
</p>
</div>
<el-switch
v-model="item.checked"
inline-prompt
active-text="是"
inactive-text="否"
@change="val => onChange(val, item)"
/>
</div>
<el-divider />
</div>
</div>
</template>
<style lang="scss" scoped>
.el-divider--horizontal {
border-top: 0.1px var(--el-border-color) var(--el-border-style);
}
</style>

View File

@ -8,8 +8,7 @@ import type { FormInstance, FormRules } from "element-plus";
import { formUpload } from "@/api/mock";
import { message } from "@/utils/message";
import { createFormData } from "@pureadmin/utils";
import { getRegions } from "@/api/mock";
import { getUserInfo, UserInfo } from "@/api/user";
import { getMine, UserInfo } from "@/api/user";
const { userAvatar, getLogo, username } = useNav();
const cropRef = ref();
const upload = ref();
@ -18,19 +17,19 @@ const userInfoFormRef = ref<FormInstance>();
//
const userInfoFormData = reactive({
avatarUrl: "",
nickName: "",
introduce: "",
regionCode: "",
address: ""
avatar: "",
nickname: "",
email: "",
phone: "",
description: ""
});
getUserInfo().then(res => {
getMine().then(res => {
Object.assign(userInfoFormData, res.data);
});
const rules = reactive<FormRules<UserInfo>>({
nickName: [
nickname: [
{ required: true, message: "昵称必填", trigger: "blur" },
{ min: 3, max: 5, message: "长度最小3最大16", trigger: "blur" }
]
@ -81,13 +80,6 @@ const handleSubmitImage = () => {
});
};
const options = ref(); //
//
getRegions().then(res => {
options.value = res.data;
});
//
const handleCascaderChange = value => {
console.log(value);
@ -104,11 +96,34 @@ const onSubmit = async (formEl: FormInstance) => {
}
});
};
function createFilter(queryString) {
return item => {
return item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0;
};
}
function querySearchEmail(queryString, callback) {
const emailList = [
{ value: "@qq.com" },
{ value: "@126.com" },
{ value: "@163.com" }
];
let results = [];
let queryList = [];
emailList.map(item =>
queryList.push({ value: queryString.split("@")[0] + item.value })
);
results = queryString
? queryList.filter(createFilter(queryString))
: queryList;
callback(results);
}
</script>
<template>
<el-card shadow="never">
<template #header>基本信息</template>
<div class="ml-[120px] min-w-[180px] max-w-[70%]">
<h3 class="my-8">个人信息</h3>
<el-form
ref="userInfoFormRef"
label-position="top"
@ -132,37 +147,42 @@ const onSubmit = async (formEl: FormInstance) => {
</el-button>
</el-upload>
</el-form-item>
<el-form-item label="昵称" prop="nickName">
<el-input v-model="userInfoFormData.nickName" />
</el-form-item>
<el-form-item label="简介">
<el-form-item label="昵称" prop="nickname">
<el-input
v-model="userInfoFormData.introduce"
type="textarea"
:autosize="{ minRows: 4, maxRows: 6 }"
v-model="userInfoFormData.nickname"
placeholder="请输入昵称"
/>
</el-form-item>
<el-form-item label="所在地区">
<el-cascader
v-model="userInfoFormData.regionCode"
:options="options"
:props="{ value: 'code', label: 'name', emitPath: false }"
placeholder="请选择"
@change="handleCascaderChange"
<el-form-item label="邮箱" prop="email">
<el-autocomplete
v-model="userInfoFormData.email"
:fetch-suggestions="querySearchEmail"
:trigger-on-focus="false"
placeholder="请输入邮箱"
clearable
class="w-full"
/>
</el-form-item>
<el-form-item label="街道地址">
<el-form-item label="联系电话">
<el-input
v-model="userInfoFormData.address"
placeholder="请输入"
v-model="userInfoFormData.phone"
placeholder="请输入联系电话"
clearable
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit(userInfoFormRef)"
>更新基本信息</el-button
>
<el-form-item label="简介">
<el-input
v-model="userInfoFormData.description"
placeholder="请输入简介"
type="textarea"
:autosize="{ minRows: 6, maxRows: 8 }"
maxlength="56"
show-word-limit
/>
</el-form-item>
<el-button type="primary" @click="onSubmit(userInfoFormRef)">
更新信息
</el-button>
</el-form>
<el-dialog
v-model="isShow"
@ -180,5 +200,5 @@ const onSubmit = async (formEl: FormInstance) => {
</div>
</template>
</el-dialog>
</el-card>
</div>
</template>

View File

@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div class="ml-[120px] min-w-[180px] max-w-[70%]">
<h3 class="my-8">安全日志</h3>
</div>
</template>

View File

@ -0,0 +1,145 @@
<script setup lang="ts">
import { ref } from "vue";
import { getMine } from "@/api/user";
import { useRouter } from "vue-router";
import { ReText } from "@/components/ReText";
import Profile from "./components/profile.vue";
import Preferences from "./components/preferences.vue";
import SecurityLog from "./components/securityLog.vue";
import AccountManagement from "./components/accountManagement.vue";
import leftLine from "@iconify-icons/ri/arrow-left-s-line";
import ProfileIcon from "@iconify-icons/ri/user-3-line";
import PreferencesIcon from "@iconify-icons/ri/settings-3-line";
import SecurityLogIcon from "@iconify-icons/ri/window-line";
import AccountManagementIcon from "@iconify-icons/ri/profile-line";
defineOptions({
name: "AccountSettings"
});
const router = useRouter();
const userInfo = ref({
avatar: "",
username: "",
nickname: ""
});
const panes = [
{
key: "profile",
label: "个人信息",
icon: ProfileIcon,
component: Profile
},
{
key: "preferences",
label: "偏好设置",
icon: PreferencesIcon,
component: Preferences
},
{
key: "securityLog",
label: "安全日志",
icon: SecurityLogIcon,
component: SecurityLog
},
{
key: "accountManagement",
label: "账户管理",
icon: AccountManagementIcon,
component: AccountManagement
}
];
const witchPane = ref("profile");
getMine().then(res => {
userInfo.value = res.data;
});
</script>
<template>
<el-container class="h-full">
<el-aside class="settings-sidebar px-2" width="240px">
<el-menu :default-active="witchPane" class="settings-menu">
<el-menu-item
class="hover:transition-all hover:duration-200 hover:text-base"
@click="router.go(-1)"
>
<div class="flex items-center">
<IconifyIconOffline :icon="leftLine" />
<span class="ml-2">返回</span>
</div>
</el-menu-item>
<div class="flex items-center ml-8 mt-4 mb-4">
<el-avatar :size="48" :src="userInfo.avatar" />
<div class="ml-4 flex flex-col max-w-[130px]">
<ReText class="font-bold !self-baseline">
{{ userInfo.nickname }}
</ReText>
<ReText class="!self-baseline" type="info">
{{ userInfo.username }}
</ReText>
</div>
</div>
<el-menu-item
v-for="item in panes"
:key="item.key"
:index="item.key"
@click="witchPane = item.key"
>
<div class="flex items-center">
<el-icon><IconifyIconOffline :icon="item.icon" /></el-icon>
<span>{{ item.label }}</span>
</div>
</el-menu-item>
</el-menu>
</el-aside>
<el-main>
<component :is="panes.find(item => item.key === witchPane).component" />
</el-main>
</el-container>
</template>
<style lang="scss" scoped>
.settings-sidebar {
overflow: hidden;
background: $menuBg;
border-right: 1px solid var(--pure-border-color);
}
.settings-menu {
color: $subMenuActiveText;
background-color: transparent !important;
border: none !important;
::v-deep(.el-menu-item) {
position: relative;
background-color: transparent !important;
div {
z-index: 1;
}
&.is-active {
span {
color: #fff !important;
}
i {
color: #fff !important;
}
&::before {
position: absolute;
inset: 0 8px;
margin: 4px 0;
clear: both;
content: "";
background: var(--el-color-primary) !important;
border-radius: 3px;
}
}
}
}
</style>