main
vue 189 lines 4.41 KB
Raw
1 <template>
2 <div class="page">
3 <n-card class="header flex flex-col" content-class="p-0!">
4 <div class="user-info flex flex-wrap">
5 <div class="propic">
6 <n-avatar :size="100" :src="userPic" round :img-props="{ alt: 'avatar' }" />
7 <ImageCropper
8 v-if="propicEnabled"
9 v-slot="{ openCropper }"
10 shape="circle"
11 placeholder="Select your profile picture"
12 @crop="setCroppedImage"
13 >
14 <Icon :name="EditIcon" :size="16" class="edit" @click="openCropper()" />
15 </ImageCropper>
16 </div>
17 <div class="info flex grow flex-col justify-center">
18 <div class="name">
19 <h1>{{ userName }}</h1>
20 </div>
21 <div class="details flex flex-wrap">
22 <div class="item">
23 <n-tooltip placement="top">
24 <template #trigger>
25 <div class="tooltip-wrap">
26 <Icon :name="RoleIcon" />
27 <span>{{ userRole }}</span>
28 </div>
29 </template>
30 <span>Role</span>
31 </n-tooltip>
32 </div>
33 <div class="item">
34 <n-tooltip placement="top">
35 <template #trigger>
36 <div class="tooltip-wrap">
37 <Icon :name="CustomerCodeIcon" />
38 <span>{{ userCustomerCode }}</span>
39 </div>
40 </template>
41 <span>Customer Code</span>
42 </n-tooltip>
43 </div>
44 </div>
45 </div>
46 <div class="actions">
47 <ImageCropper
48 v-if="propicEnabled"
49 v-slot="{ openCropper }"
50 shape="circle"
51 placeholder="Select your profile picture"
52 @crop="setCroppedImage"
53 >
54 <n-button size="large" type="primary" @click="openCropper()">Edit profile image</n-button>
55 </ImageCropper>
56 </div>
57 </div>
58 <div class="section-selector">
59 <n-tabs v-model:value="tabActive">
60 <n-tab name="settings">Settings</n-tab>
61 <n-tab name="security">Security</n-tab>
62 </n-tabs>
63 </div>
64 </n-card>
65 <div class="main">
66 <n-tabs v-model:value="tabActive" tab-class="hidden!" animated>
67 <n-tab-pane name="settings">
68 <ProfileSettings />
69 </n-tab-pane>
70 <n-tab-pane name="security">
71 <div class="flex flex-col gap-4">
72 <ChangePasswordCard />
73 </div>
74 </n-tab-pane>
75 </n-tabs>
76 </div>
77 </div>
78 </template>
79
80 <script lang="ts" setup>
81 import type { ImageCropperResult } from "@/components/common/ImageCropper.vue"
82 import { NAvatar, NButton, NCard, NTab, NTabPane, NTabs, NTooltip } from "naive-ui"
83 import { ref } from "vue"
84 import ChangePasswordCard from "@/components/auth/ChangePasswordCard.vue"
85 import Icon from "@/components/common/Icon.vue"
86 import ImageCropper from "@/components/common/ImageCropper.vue"
87 import ProfileSettings from "@/components/profile/ProfileSettings.vue"
88 import { useAuthStore } from "@/stores/auth"
89
90 const propicEnabled = false
91
92 const RoleIcon = "tabler:user"
93 const EditIcon = "uil:image-edit"
94 const CustomerCodeIcon = "carbon:hashtag"
95
96 const tabActive = ref("settings")
97 const authStore = useAuthStore()
98
99 const userRole = authStore.userRoleName
100 const userName = authStore.userName
101 const userCustomerCode = authStore.userCustomerCode
102 const userPic = ref(authStore.userPic)
103
104 function setCroppedImage(result: ImageCropperResult) {
105 const canvas = result.canvas as HTMLCanvasElement
106 userPic.value = canvas.toDataURL()
107 }
108 </script>
109
110 <style lang="scss" scoped>
111 .page {
112 .header {
113 .user-info {
114 gap: 30px;
115 padding: 30px;
116 padding-bottom: 20px;
117 border-block-end: 1px solid var(--border-color);
118 container-type: inline-size;
119
120 .propic {
121 position: relative;
122 height: 100px;
123
124 .edit {
125 display: none;
126 align-items: center;
127 justify-content: center;
128 background-color: var(--primary-color);
129 color: var(--bg-default-color);
130 position: absolute;
131 width: 26px;
132 height: 26px;
133 border-radius: 50%;
134 top: -1px;
135 right: -1px;
136 border: 1px solid var(--bg-default-color);
137 cursor: pointer;
138 }
139 }
140 .info {
141 .name {
142 margin-bottom: 12px;
143
144 @media (max-width: 450px) {
145 h1 {
146 font-size: 28px;
147 }
148 }
149 }
150
151 .details {
152 gap: 24px;
153
154 .item {
155 .tooltip-wrap {
156 display: flex;
157 align-items: center;
158 gap: 8px;
159 line-height: 1;
160 }
161 }
162 }
163 }
164
165 @container (max-width: 900px) {
166 .propic {
167 .edit {
168 display: flex;
169 }
170 }
171 }
172 }
173 .section-selector {
174 padding: 0px 30px;
175 padding-top: 15px;
176
177 :deep() {
178 .n-tabs .n-tabs-tab {
179 padding-bottom: 20px;
180 }
181 }
182 }
183 }
184
185 .main {
186 margin-top: 18px;
187 }
188 }
189 </style>