Commit 0d05de2e authored by nick zheng's avatar nick zheng

Merge branch 'master' of https://gitlab.gsstcloud.com/poc/poc-fe into beta

parents 666fc2a6 d3157a29
......@@ -8,7 +8,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_vbf79y4vtuk.css" />
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_a5ytfgvaagl.css" />
<title>Model Link</title>
</head>
......
......@@ -2,9 +2,8 @@
import { computed, h, ref, watchEffect } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Plus } from '@icon-park/vue-next'
import { Plus, Logout, Me } from '@icon-park/vue-next'
import type { MenuOption } from 'naive-ui'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import { useUserStore } from '@/store/modules/user'
// import LanguageSetting from '@/components/language-setting/language-setting.vue'
......@@ -46,12 +45,17 @@ const menuOptions = computed<MenuOption[]>(() => {
]
})
const avatarOptions = computed(() => {
const userConfigOptions = computed(() => {
return [
{
label: () => h('div', {}, t('common_module.logout')),
key: 'logout',
icon: () => h(CustomIcon, { icon: 'teenyicons:logout-solid' }),
label: () => h('div', '个人设置'),
key: 'PersonalSettings',
icon: () => h(Me, { theme: 'outline', size: 14, strokeWidth: 3 }),
},
{
label: () => h('div', t('common_module.logout')),
key: 'Logout',
icon: () => h(Logout, { theme: 'outline', size: 14, strokeWidth: 3 }),
},
]
})
......@@ -64,8 +68,8 @@ function handleToPersonAppSettingPage() {
router.push({ name: 'PersonalAppSetting' })
}
function handleDropdownSelect(key: string) {
if (key === 'logout') {
function handleUserConfigOptionsSelect(key: string) {
if (key === 'Logout') {
userStore.logout().then(() => {
router.push({ name: 'Login' })
})
......@@ -119,28 +123,28 @@ function handleMenuValueChange(key: string) {
<div class="mb-[20px] mt-6">
<div>
<NDropdown
<n-dropdown
v-if="userStore.isLogin"
trigger="click"
placement="top"
:options="avatarOptions"
@select="handleDropdownSelect"
:options="userConfigOptions"
@select="handleUserConfigOptionsSelect"
>
<div
class="flex h-full cursor-pointer items-center rounded-[6px] px-[12px] py-[4px] transition hover:bg-[#F3F3F5]"
>
<NAvatar round :size="40" object-fit="cover" :src="userStore.userInfo.avatarUrl || defaultAvatar" />
<n-avatar round :size="40" object-fit="cover" :src="userStore.userInfo.avatarUrl || defaultAvatar" />
<div class="ml-3 line-clamp-1 max-w-[140px] select-none break-all text-base">
<div class="ml-3 line-clamp-1 max-w-[140px] flex-1 select-none break-all text-base">
{{ userStore.userInfo.nickName || t('common_module.not_login_text') }}
</div>
</div>
</NDropdown>
</n-dropdown>
<div v-else>
<NButton type="primary" class="w-full! rounded-md!" @click="handleToLogin">
<n-button type="primary" class="w-full! rounded-md!" @click="handleToLogin">
{{ t('common_module.login_now') }}
</NButton>
</n-button>
</div>
</div>
......@@ -152,16 +156,16 @@ function handleMenuValueChange(key: string) {
</template>
<style lang="scss">
.v-binder-follower-container {
.n-dropdown-menu {
--n-border-radius: 10px !important;
--n-box-shadow: -3px 3px 4px 0px #f2f2f2 !important;
padding: 6px 0 !important;
}
.n-dropdown-menu .n-dropdown-option-body {
padding: 0 10px;
}
}
// .v-binder-follower-container {
// .n-dropdown-menu {
// --n-border-radius: 10px !important;
// --n-box-shadow: -3px 3px 4px 0px #f2f2f2 !important;
// padding: 6px 0 !important;
// }
// .n-dropdown-menu .n-dropdown-option-body {
// padding: 0 10px;
// }
// }
</style>
import { type RouteRecordRaw } from 'vue-router'
import Layout from '@/layout/index.vue'
export default [
{
path: '/personal/settings',
meta: {
rank: 1001,
title: '',
},
component: Layout,
children: [
{
path: '',
name: 'PersonalSettings',
meta: {
rank: 1001,
title: 'router_title_module.personal_settings',
},
component: () => import('@/views/personal-settings/personal-settings.vue'),
},
],
},
] as RouteRecordRaw[]
......@@ -23,10 +23,10 @@ const defaultLanguageOptions = [
label: '中文繁體',
key: 'zh-HK',
},
// {
// label: 'English',
// key: 'en',
// },
{
label: 'English',
key: 'en',
},
]
const localeKey = ss.get('i18nextLng') || defaultLocale
......
......@@ -9,6 +9,8 @@ function createDefaultUserInfoFactory(): UserInfo {
avatarUrl: '',
nickName: '',
mobilePhone: '',
remark: '',
email: '',
}
}
......
......@@ -3,6 +3,8 @@ export interface UserInfo {
mobilePhone: string
nickName: string
avatarUrl: string
remark: string
email: string
}
export interface UserState {
......
......@@ -93,7 +93,7 @@ const emailLoginFormRules = shallowReadonly<FormRules>({
const phoneNumberAreaOptions = computed(() => {
return [
{
label: `+86 ${t('login_module.mainland_china')}`,
label: `+86${t('login_module.mainland_china')}`,
value: '+86',
},
{
......
<script setup lang="ts">
import { useUserStore } from '@/store/modules/user'
import { computed, reactive, ref, shallowReadonly, useTemplateRef, nextTick } from 'vue'
import type { FormItemRule, FormInst, InputInst } from 'naive-ui'
import isEmail from 'validator/es/lib/isEmail'
import { useSystemLanguageStore } from '@/store/modules/system-language'
import { useRouter } from 'vue-router'
const userStore = useUserStore()
const systemLanguageStore = useSystemLanguageStore()
const router = useRouter()
const inputRefs = useTemplateRef<InputInst | InputInst[]>('inputRefs')
const avatarInputRef = useTemplateRef('avatarInputRef')
const emailInfoFormRef = useTemplateRef<FormInst>('emailInfoFormRef')
const passwordInfoFormRef = useTemplateRef<FormInst>('passwordInfoFormRef')
const userInfoForm = reactive({
nickName: userStore.userInfo.nickName,
remark: userStore.userInfo.remark,
})
const userInfoFormItemEdit = reactive({
nickName: false,
remark: false,
})
/* 邮箱绑定 */
const isShowMailboxBindingModal = ref(false)
const mailboxBindingSubmitBtnLoading = ref(false)
const emailInfoForm = ref({
email: '',
})
const emailInfoFormRules = shallowReadonly({
email: {
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
if (!value) {
return new Error('请填写邮箱地址')
} else if (!isEmail(value)) {
return new Error('请填写正确邮箱地址')
}
return
},
},
})
/* 密码修改 */
const isShowPasswordChangeModal = ref(false)
const passwordChangeSubmitBtnLoading = ref(false)
const passwordInfoForm = ref({
password: '',
confirmPassword: '',
})
const passwordFormRules = shallowReadonly({
password: {
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
if (!value) {
return new Error('请输入新密码')
} else if (value.length <= 6) {
return new Error('密码长度不能小于6位')
}
return
},
},
confirmPassword: {
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
if (!value) {
return new Error('请输入确定密码')
} else if (value !== passwordInfoForm.value.password) {
return new Error('确认密码与密码不一致')
}
return
},
},
})
const currentLanguage = computed({
get() {
return systemLanguageStore.currentLanguageInfo.key
},
set(value: I18n.LangType) {
systemLanguageStore.updateCurrentLanguageInfo(value)
router.go(0)
},
})
const userInfo = computed(() => userStore.userInfo)
const languageOptions = computed(() => {
return systemLanguageStore.languageOptions.map((item) => ({ value: item.key, label: item.label }))
})
function handleAvatarUpdate() {
avatarInputRef.value && avatarInputRef.value.click()
}
function handleAvatarUpload(e: Event) {
const target = e.target as HTMLInputElement
const file = target.files && target.files[0]
if (file) {
const reader = new FileReader()
reader.onload = (e) => {
const result = e.target?.result
console.log('🟰🟰🟰🟰🟰🟰test🟰🟰🟰🟰🟰🟰')
console.log(result)
}
}
}
function handleUserInfoFormItemEditUpdate(key: keyof typeof userInfoFormItemEdit, status: boolean) {
userInfoFormItemEdit[key] = status
nextTick(() => {
if (inputRefs.value && !Array.isArray(inputRefs.value)) {
inputRefs.value.focus()
}
})
}
function onModalAfterLeave() {
emailInfoForm.value.email = ''
}
function handleMailboxBindingSubmit() {
emailInfoFormRef.value?.validate((errors) => {
if (errors) return ''
console.log('🟰🟰🟰🟰🟰🟰提交🟰🟰🟰🟰🟰🟰')
})
}
function handlePasswordChangeSubmit() {
passwordInfoFormRef.value?.validate((errors) => {
if (errors) return ''
console.log('🟰🟰🟰🟰🟰🟰提交🟰🟰🟰🟰🟰🟰')
})
}
</script>
<template>
<div class="h-full flex-col rounded-[20px] bg-white p-6 shadow-[0_2px_2px_#0000000a]">
<div class="ml-[56px] pt-[6px]">
<ul>
<li class="font-600 flex items-center text-[18px]">
<i class="iconfont icon-personal-info text-theme-color"></i>
<span class="ml-[4px]">基础信息设置</span>
</li>
<li class="mb-[30px] mt-[24px] flex items-center">
<h4 class="font-600 list-item-label mr-[19px] text-[14px]">头像:</h4>
<div
class="group relative h-[60px] w-[60px] cursor-pointer overflow-hidden rounded-full bg-[#EAECEC]"
@click="handleAvatarUpdate"
>
<img class="h-full w-full object-cover" :src="userInfo.avatarUrl" alt="Avatar" />
<div
class="flex-center absolute bottom-0 h-[26px] w-full bg-[rgba(0,0,0,0.4)] text-[10px] text-white opacity-0 transition group-hover:opacity-100"
>
<span>更换</span>
</div>
<input ref="avatarInputRef" type="file" class="hidden" accept="image/*" @change="handleAvatarUpload" />
</div>
</li>
<li class="mb-[30px] mt-[24px] flex items-center">
<h4 class="font-600 list-item-label mr-[19px] text-[14px]">账号昵称:</h4>
<div v-if="userInfoFormItemEdit.nickName" class="flex">
<div class="flex w-[220px] items-center">
<n-input ref="inputRefs" v-model:value="userInfoForm.nickName" type="text" size="small" />
</div>
<div class="ml-[20px]">
<n-button class="!mr-[6px]" size="tiny" @click="handleUserInfoFormItemEditUpdate('nickName', false)">
<i class="iconfont icon-close"></i>
</n-button>
<n-button type="primary" size="tiny" @click="handleUserInfoFormItemEditUpdate('nickName', true)">
<i class="iconfont icon-queren"></i>
</n-button>
</div>
</div>
<div v-else class="flex items-center text-[14px] text-[#999]">
<span class="mr-[5px]">{{ userInfo.nickName || '-' }}</span>
<i
class="iconfont icon-edit1 cursor-pointer px-[5px]"
@click="handleUserInfoFormItemEditUpdate('nickName', true)"
></i>
</div>
</li>
<li class="mb-[30px] mt-[24px] flex items-center">
<h4 class="font-600 list-item-label mr-[19px] text-[14px]">个人简介:</h4>
<div v-if="userInfoFormItemEdit.remark" class="flex">
<div class="flex w-[220px] items-center">
<n-input
ref="inputRefs"
v-model:value="userInfoForm.remark"
type="textarea"
size="small"
:autosize="{ minRows: 4, maxRows: 4 }"
/>
</div>
<div class="ml-[20px] flex items-center">
<n-button class="!mr-[6px]" size="tiny" @click="handleUserInfoFormItemEditUpdate('remark', false)">
<i class="iconfont icon-close"></i>
</n-button>
<n-button type="primary" size="tiny" @click="handleUserInfoFormItemEditUpdate('remark', true)">
<i class="iconfont icon-queren"></i>
</n-button>
</div>
</div>
<div v-else class="flex items-center text-[14px] text-[#999]">
<span class="mr-[5px]">{{ userInfo.remark || '-' }}</span>
<i
class="iconfont icon-edit1 cursor-pointer px-[5px]"
@click="handleUserInfoFormItemEditUpdate('remark', true)"
></i>
</div>
</li>
<li class="mb-[30px] mt-[24px] flex items-center">
<h4 class="font-600 list-item-label mr-[19px] text-[14px]">手机号:</h4>
<div class="text-[14px] text-[#999]">{{ userInfo.mobilePhone || '-' }}</div>
</li>
<li class="mb-[30px] mt-[24px] flex items-center">
<h4 class="font-600 list-item-label mr-[19px] text-[14px]">邮箱:</h4>
<div class="text-[14px] text-[#999]">
<span v-if="userInfo.email">{{ userInfo.email }}</span>
<span v-else class="text-theme-color cursor-pointer select-none" @click="isShowMailboxBindingModal = true">
点击绑定邮箱
</span>
</div>
</li>
<li class="mb-[30px] mt-[24px] flex items-center">
<h4 class="font-600 list-item-label mr-[19px] text-[14px]">密码:</h4>
<div
class="text-theme-color cursor-pointer select-none text-[14px]"
@click="isShowPasswordChangeModal = true"
>
点击设置密码
</div>
</li>
</ul>
<ul class="mt-[60px]">
<li class="font-600 flex items-center text-[18px]">
<i class="iconfont icon-language text-theme-color"></i>
<span class="ml-[4px]">语言设置</span>
</li>
<li class="mb-[8px] ml-[121px]">
<h5 class="text-[12px] text-[#999]">设定您的首选语言,用于界面和AI回应。</h5>
</li>
<li class="mb-[30px] flex items-center">
<h4 class="font-600 list-item-label mr-[19px] text-[14px]">语言:</h4>
<div class="w-[200px]">
<n-select v-model:value="currentLanguage" :options="languageOptions" />
</div>
</li>
</ul>
</div>
<n-modal v-model:show="isShowMailboxBindingModal" :mask-closable="false" :on-after-leave="onModalAfterLeave">
<n-card
class="!w-[600px]"
title="邮箱绑定"
:bordered="false"
size="medium"
closable
@close="() => (isShowMailboxBindingModal = false)"
>
<n-form
ref="emailInfoFormRef"
label-placement="left"
label-width="auto"
:model="emailInfoForm"
:rules="emailInfoFormRules"
>
<n-form-item label="邮箱地址" path="email">
<n-input v-model:value="emailInfoForm.email" placeholder="请输入邮箱地址" />
</n-form-item>
</n-form>
<template #footer>
<div class="text-end">
<n-space justify="end">
<n-button @click="() => (isShowMailboxBindingModal = false)">取消</n-button>
<n-button type="primary" :loading="mailboxBindingSubmitBtnLoading" @click="handleMailboxBindingSubmit">
确定
</n-button>
</n-space>
</div>
</template>
</n-card>
</n-modal>
<n-modal v-model:show="isShowPasswordChangeModal" :mask-closable="false" :on-after-leave="onModalAfterLeave">
<n-card
class="!w-[600px]"
title="密码修改"
:bordered="false"
size="medium"
closable
@close="() => (isShowPasswordChangeModal = false)"
>
<n-form
ref="passwordInfoFormRef"
label-placement="left"
label-width="auto"
:model="passwordInfoForm"
:rules="passwordFormRules"
>
<n-form-item label="密码" path="password">
<n-input v-model:value="passwordInfoForm.password" placeholder="请输入密码" />
</n-form-item>
<n-form-item label="确认密码" path="confirmPassword">
<n-input v-model:value="passwordInfoForm.confirmPassword" placeholder="请输入确定密码" />
</n-form-item>
</n-form>
<template #footer>
<div class="text-end">
<n-space justify="end">
<n-button @click="() => (isShowPasswordChangeModal = false)">取消</n-button>
<n-button type="primary" :loading="passwordChangeSubmitBtnLoading" @click="handlePasswordChangeSubmit">
确定
</n-button>
</n-space>
</div>
</template>
</n-card>
</n-modal>
</div>
</template>
<style lang="scss" scoped>
.list-item-label {
width: 100px;
text-align: end;
}
</style>
......@@ -167,6 +167,7 @@ declare namespace I18n {
multi_model_dialogue: string
explore: string
application_square: string
personal_settings: string
}
personal_space_module: {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment