All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m22s
补齐系统用户重置密码入口与网盘下载提示;规范禁止 type=outline;顺带修复揽收单下游预报后刷新列表。 Co-authored-by: Cursor <cursoragent@cursor.com>
850 lines
24 KiB
Vue
850 lines
24 KiB
Vue
<template>
|
|
<a-drawer
|
|
:visible="visible"
|
|
:width="640"
|
|
unmount-on-close
|
|
:esc-to-close="true"
|
|
:mask-closable="!confirmLoading"
|
|
class="user-edit-drawer"
|
|
:footer="false"
|
|
@update:visible="onVisibleChange"
|
|
@cancel="handleCancel"
|
|
>
|
|
<div class="ue-drawer">
|
|
<header class="ue-drawer__header">
|
|
<div class="ue-drawer__title-row">
|
|
<div>
|
|
<p class="ue-drawer__eyebrow">{{ t('system.user.drawer.eyebrow') }}</p>
|
|
<h3 class="ue-drawer__title">
|
|
{{ isEdit ? t('system.user.modal.edit') : t('system.user.modal.add') }}
|
|
</h3>
|
|
</div>
|
|
<button type="button" class="ue-drawer__close" @click="handleCancel">
|
|
<icon-close />
|
|
</button>
|
|
</div>
|
|
<p class="ue-drawer__desc">{{ t('system.user.drawer.desc') }}</p>
|
|
</header>
|
|
|
|
<div class="ue-drawer__body">
|
|
<section class="ue-preview">
|
|
<div class="ue-preview__avatar">
|
|
<img v-if="avatarUrl" :src="avatarUrl" alt="" />
|
|
<span v-else>{{ previewInitial }}</span>
|
|
</div>
|
|
<div class="ue-preview__meta">
|
|
<h4 class="ue-preview__name">
|
|
{{ formState.name || t('system.user.drawer.nameFallback') }}
|
|
</h4>
|
|
<p class="ue-preview__account">
|
|
{{ formState.account || t('system.user.drawer.accountFallback') }}
|
|
<template v-if="formState.employeeNo"> · {{ formState.employeeNo }}</template>
|
|
</p>
|
|
<div class="ue-preview__tags">
|
|
<span v-if="formState.phone" class="ue-preview__tag">{{ formState.phone }}</span>
|
|
<span v-if="orgDisplayName" class="ue-preview__tag">{{ orgDisplayName }}</span>
|
|
<span
|
|
v-if="employeeFormState.employmentStatus"
|
|
class="ue-preview__tag ue-preview__tag--accent"
|
|
>
|
|
{{ employmentStatusText }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div v-if="isEdit && canResetPassword" class="ue-preview__actions">
|
|
<button
|
|
type="button"
|
|
class="ue-preview__action"
|
|
:disabled="resetPwdLoading"
|
|
@click="openResetPasswordModal"
|
|
>
|
|
{{ t('system.user.actions.resetPassword') }}
|
|
</button>
|
|
</div>
|
|
</section>
|
|
|
|
<a-form
|
|
ref="formRef"
|
|
class="ue-form"
|
|
layout="vertical"
|
|
:model="formState"
|
|
:rules="formRules"
|
|
:label-col-props="{ span: 24 }"
|
|
:wrapper-col-props="{ span: 24 }"
|
|
>
|
|
<section class="ue-section">
|
|
<div class="ue-section__head">
|
|
<span class="ue-section__index">01</span>
|
|
<div>
|
|
<h5 class="ue-section__title">{{ t('system.user.drawer.section.account') }}</h5>
|
|
<p class="ue-section__hint">{{ t('system.user.drawer.section.accountHint') }}</p>
|
|
</div>
|
|
</div>
|
|
<a-row :gutter="16">
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.name')" field="name">
|
|
<a-input
|
|
v-model="formState.name"
|
|
:placeholder="t('system.user.form.namePlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.account')" field="account">
|
|
<a-input
|
|
v-model="formState.account"
|
|
:placeholder="t('system.user.form.accountPlaceholder')"
|
|
:disabled="isEdit"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col v-if="!isEdit" :span="12">
|
|
<a-form-item :label="t('system.user.form.password')" field="password">
|
|
<a-input-password
|
|
v-model="formState.password"
|
|
:placeholder="t('system.user.form.passwordPlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.email')" field="email">
|
|
<a-input
|
|
v-model="formState.email"
|
|
:placeholder="t('system.user.form.emailPlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.organization')" field="organizationId">
|
|
<a-tree-select
|
|
v-model="formState.organizationId"
|
|
:placeholder="t('system.user.form.organizationPlaceholder')"
|
|
:data="organizationTreeData"
|
|
allow-clear
|
|
:loading="organizationLoading"
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.avatar')" field="avatar">
|
|
<a-input
|
|
v-model="formState.avatar"
|
|
:placeholder="t('system.user.form.avatarPlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col v-if="!isEdit" :span="24">
|
|
<a-form-item :label="t('system.user.form.roles')" field="roleIds">
|
|
<a-select
|
|
v-model="formState.roleIds"
|
|
multiple
|
|
allow-clear
|
|
allow-search
|
|
:placeholder="t('system.user.form.rolesPlaceholder')"
|
|
:options="roleOptions"
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col v-if="isEdit && advisorInviteUrl" :span="24">
|
|
<a-form-item :label="t('system.user.form.advisorInviteLink')">
|
|
<div class="ue-invite-row">
|
|
<a-input
|
|
class="ue-invite-input"
|
|
:model-value="advisorInviteUrl"
|
|
readonly
|
|
/>
|
|
<a-button type="primary" class="ue-invite-copy" @click="copyAdvisorInviteLink">
|
|
{{ t('system.user.actions.copyInviteLink') }}
|
|
</a-button>
|
|
</div>
|
|
<template #extra>
|
|
<span class="ue-invite-hint">{{ t('system.user.form.advisorInviteHint') }}</span>
|
|
</template>
|
|
</a-form-item>
|
|
</a-col>
|
|
</a-row>
|
|
</section>
|
|
|
|
<section class="ue-section">
|
|
<div class="ue-section__head">
|
|
<span class="ue-section__index">02</span>
|
|
<div>
|
|
<h5 class="ue-section__title">{{ t('system.user.drawer.section.contact') }}</h5>
|
|
<p class="ue-section__hint">{{ t('system.user.drawer.section.contactHint') }}</p>
|
|
</div>
|
|
</div>
|
|
<a-row :gutter="16">
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.phone')" field="phone">
|
|
<a-input
|
|
v-model="formState.phone"
|
|
:placeholder="t('system.user.form.phonePlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.employeeNo')" field="employeeNo">
|
|
<a-input
|
|
v-model="formState.employeeNo"
|
|
:placeholder="t('system.user.form.employeeNoPlaceholder')"
|
|
:readonly="isEdit"
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.wechatId')" field="wechatId">
|
|
<a-input
|
|
v-model="formState.wechatId"
|
|
:placeholder="t('system.user.form.wechatIdPlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.wecomKfUrl')" field="wecomKfUrl">
|
|
<a-input
|
|
v-model="formState.wecomKfUrl"
|
|
:placeholder="t('system.user.form.wecomKfUrlPlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-form-item :label="t('system.user.form.wechatQrCode')" field="wechatQrFiles">
|
|
<a-upload
|
|
v-model:file-list="formState.wechatQrFiles"
|
|
list-type="picture-card"
|
|
:limit="1"
|
|
accept="image/*"
|
|
:custom-request="handleWechatCustomRequest"
|
|
>
|
|
<template #upload-button>
|
|
<div class="ue-upload-placeholder">
|
|
<icon-plus />
|
|
<span>{{ t('system.user.form.wechatQrUpload') }}</span>
|
|
</div>
|
|
</template>
|
|
</a-upload>
|
|
</a-form-item>
|
|
</a-col>
|
|
</a-row>
|
|
</section>
|
|
|
|
<section class="ue-section">
|
|
<div class="ue-section__head">
|
|
<span class="ue-section__index">03</span>
|
|
<div>
|
|
<h5 class="ue-section__title">{{ t('system.employee.section') }}</h5>
|
|
<p class="ue-section__hint">{{ t('system.user.drawer.section.employeeHint') }}</p>
|
|
</div>
|
|
</div>
|
|
<EmployeeProfileFields :model-value="employeeFormState" hide-title dense />
|
|
</section>
|
|
</a-form>
|
|
</div>
|
|
|
|
<footer class="ue-drawer__footer">
|
|
<a-button :disabled="confirmLoading" @click="handleCancel">
|
|
{{ t('common.cancel') }}
|
|
</a-button>
|
|
<a-button type="primary" :loading="confirmLoading" @click="handleOk">
|
|
{{ t('common.save') }}
|
|
</a-button>
|
|
</footer>
|
|
</div>
|
|
|
|
<a-modal
|
|
v-model:visible="resetPwdVisible"
|
|
:title="t('system.user.modal.resetPasswordTitle')"
|
|
:ok-loading="resetPwdLoading"
|
|
unmount-on-close
|
|
@ok="handleResetPasswordSubmit"
|
|
@cancel="closeResetPasswordModal"
|
|
>
|
|
<p class="ue-reset-tip">
|
|
{{
|
|
t('system.user.modal.resetPasswordContentSingle', {
|
|
account: formState.account || t('system.user.drawer.accountFallback'),
|
|
})
|
|
}}
|
|
</p>
|
|
<a-form layout="vertical">
|
|
<a-form-item :label="t('system.user.form.newPassword')" required>
|
|
<a-input-password
|
|
v-model="resetPwdForm.newPassword"
|
|
:placeholder="t('system.user.form.newPasswordPlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item :label="t('system.user.form.confirmPassword')" required>
|
|
<a-input-password
|
|
v-model="resetPwdForm.confirmPassword"
|
|
:placeholder="t('system.user.form.confirmPasswordPlaceholder')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-form>
|
|
</a-modal>
|
|
</a-drawer>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, reactive, ref } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { Message } from '@arco-design/web-vue'
|
|
import { IconClose, IconPlus } from '@arco-design/web-vue/es/icon'
|
|
import EmployeeProfileFields from '@/components/EmployeeProfileFields/index.vue'
|
|
import { resetUserPassword } from '@/api/rbac/internal-user'
|
|
import type { EmployeeFormState } from '@/types/employee-profile'
|
|
import type { UserFormExtState } from '@/types/rbac-user-profile'
|
|
import { resolveAvatarUrl } from '@/utils/avatar'
|
|
import { buildAdvisorInviteRegisterUrl } from '@/utils/inviteLink'
|
|
import { hasPermission } from '@/utils/rbac-permission'
|
|
import { $baseMessage } from '@/utils'
|
|
import { readFileAsDataUrl } from '@/utils/image-data-url'
|
|
|
|
export type UserEditFormState = {
|
|
name?: string
|
|
account?: string
|
|
password?: string
|
|
email?: string
|
|
avatar?: string
|
|
organizationId?: string
|
|
roleIds?: string[]
|
|
} & UserFormExtState
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
visible: boolean
|
|
confirmLoading?: boolean
|
|
isEdit?: boolean
|
|
userId?: string
|
|
formState: UserEditFormState
|
|
employeeFormState: EmployeeFormState
|
|
organizationTreeData?: any[]
|
|
organizationLoading?: boolean
|
|
roleOptions?: Array<{ label: string; value: string }>
|
|
formRules?: Record<string, unknown>
|
|
}>(),
|
|
{
|
|
confirmLoading: false,
|
|
isEdit: false,
|
|
userId: '',
|
|
organizationTreeData: () => [],
|
|
organizationLoading: false,
|
|
roleOptions: () => [],
|
|
formRules: () => ({}),
|
|
},
|
|
)
|
|
|
|
const emit = defineEmits<{
|
|
'update:visible': [boolean]
|
|
ok: []
|
|
cancel: []
|
|
}>()
|
|
|
|
const { t } = useI18n()
|
|
const formRef = ref()
|
|
const resetPwdVisible = ref(false)
|
|
const resetPwdLoading = ref(false)
|
|
const resetPwdForm = reactive({
|
|
newPassword: '',
|
|
confirmPassword: '',
|
|
})
|
|
|
|
const canResetPassword = computed(
|
|
() => hasPermission('Rbac.Users.ResetPassword') || hasPermission('Rbac.Users.Update'),
|
|
)
|
|
|
|
const avatarUrl = computed(() => {
|
|
const raw = props.formState.avatar?.trim()
|
|
return raw ? resolveAvatarUrl(raw) : ''
|
|
})
|
|
|
|
const previewInitial = computed(() => {
|
|
const name = props.formState.name?.trim()
|
|
if (name) return name.slice(0, 1)
|
|
const account = props.formState.account?.trim()
|
|
return account ? account.slice(0, 1).toUpperCase() : 'U'
|
|
})
|
|
|
|
const orgDisplayName = computed(() => {
|
|
const id = props.formState.organizationId
|
|
if (!id) return ''
|
|
const find = (nodes: any[]): string => {
|
|
for (const node of nodes || []) {
|
|
if (node.value === id || node.key === id || node.id === id) {
|
|
return String(node.label || node.title || node.name || '')
|
|
}
|
|
const child = find(node.children || [])
|
|
if (child) return child
|
|
}
|
|
return ''
|
|
}
|
|
return find(props.organizationTreeData || [])
|
|
})
|
|
|
|
const employmentStatusText = computed(() => {
|
|
const map: Record<number, string> = {
|
|
1: t('system.employee.status.probation'),
|
|
2: t('system.employee.status.regular'),
|
|
3: t('system.employee.status.suspended'),
|
|
4: t('system.employee.status.resigned'),
|
|
}
|
|
return map[props.employeeFormState.employmentStatus] || ''
|
|
})
|
|
|
|
const advisorInviteUrl = computed(() =>
|
|
buildAdvisorInviteRegisterUrl(String(props.formState.account || '')),
|
|
)
|
|
|
|
async function copyAdvisorInviteLink() {
|
|
const url = advisorInviteUrl.value
|
|
if (!url) return
|
|
try {
|
|
await navigator.clipboard.writeText(url)
|
|
Message.success(t('system.user.message.copyInviteSuccess'))
|
|
} catch {
|
|
Message.error(t('system.user.message.copyInviteFailed'))
|
|
}
|
|
}
|
|
|
|
function openResetPasswordModal() {
|
|
if (!props.userId) {
|
|
Message.warning(t('system.user.message.resetPasswordFailed'))
|
|
return
|
|
}
|
|
resetPwdForm.newPassword = ''
|
|
resetPwdForm.confirmPassword = ''
|
|
resetPwdVisible.value = true
|
|
}
|
|
|
|
function closeResetPasswordModal() {
|
|
resetPwdVisible.value = false
|
|
resetPwdForm.newPassword = ''
|
|
resetPwdForm.confirmPassword = ''
|
|
}
|
|
|
|
async function handleResetPasswordSubmit() {
|
|
const newPassword = resetPwdForm.newPassword.trim()
|
|
const confirmPassword = resetPwdForm.confirmPassword.trim()
|
|
if (!newPassword) {
|
|
Message.warning(t('system.user.rules.password.required'))
|
|
return Promise.reject()
|
|
}
|
|
if (newPassword.length < 6) {
|
|
Message.warning(t('system.user.rules.password.minLength'))
|
|
return Promise.reject()
|
|
}
|
|
if (newPassword !== confirmPassword) {
|
|
Message.warning(t('system.user.rules.password.confirmMismatch'))
|
|
return Promise.reject()
|
|
}
|
|
if (!props.userId) {
|
|
Message.warning(t('system.user.message.resetPasswordFailed'))
|
|
return Promise.reject()
|
|
}
|
|
|
|
resetPwdLoading.value = true
|
|
try {
|
|
await resetUserPassword(props.userId, newPassword)
|
|
Message.success(t('system.user.message.resetPasswordSuccess'))
|
|
closeResetPasswordModal()
|
|
} catch (e: any) {
|
|
Message.error(e?.message || t('system.user.message.resetPasswordFailed'))
|
|
return Promise.reject(e)
|
|
} finally {
|
|
resetPwdLoading.value = false
|
|
}
|
|
}
|
|
|
|
function onVisibleChange(val: boolean) {
|
|
emit('update:visible', val)
|
|
}
|
|
|
|
function handleCancel() {
|
|
if (props.confirmLoading) return
|
|
emit('update:visible', false)
|
|
emit('cancel')
|
|
}
|
|
|
|
async function handleOk() {
|
|
try {
|
|
await formRef.value?.validate()
|
|
} catch {
|
|
return
|
|
}
|
|
emit('ok')
|
|
}
|
|
|
|
async function handleWechatCustomRequest(options: any) {
|
|
const { fileItem, onSuccess, onError } = options
|
|
const file = fileItem?.file as File | undefined
|
|
if (!file) {
|
|
onError?.()
|
|
return
|
|
}
|
|
try {
|
|
const dataUrl = await readFileAsDataUrl(
|
|
file,
|
|
undefined,
|
|
t('system.user.message.wechatQrTooLarge'),
|
|
)
|
|
fileItem.url = dataUrl
|
|
fileItem.status = 'done'
|
|
fileItem.response = { url: dataUrl }
|
|
onSuccess?.({ url: dataUrl })
|
|
} catch (e: any) {
|
|
fileItem.status = 'error'
|
|
$baseMessage(e?.message || t('system.user.message.wechatQrTooLarge'), 'warning')
|
|
onError?.(e)
|
|
}
|
|
}
|
|
|
|
defineExpose({
|
|
validate: () => formRef.value?.validate(),
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.ue-drawer {
|
|
--ue-ink: #1a2e38;
|
|
--ue-muted: rgba(26, 46, 56, 0.68);
|
|
--ue-soft: rgba(26, 46, 56, 0.48);
|
|
--ue-radius: 16px;
|
|
--ue-edge: var(--app-acrylic-border);
|
|
--ue-panel-bg: var(--app-acrylic-bg);
|
|
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
min-height: 0;
|
|
margin: -16px;
|
|
background: var(--app-page-bg, #f0f3f4);
|
|
}
|
|
|
|
.ue-drawer__header {
|
|
flex-shrink: 0;
|
|
padding: 18px 20px 14px;
|
|
border-bottom: 1px solid var(--ue-edge);
|
|
background: var(--ue-panel-bg);
|
|
backdrop-filter: var(--app-acrylic-blur);
|
|
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
|
}
|
|
|
|
.ue-drawer__title-row {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.ue-drawer__eyebrow {
|
|
margin: 0 0 4px;
|
|
font-size: 12px;
|
|
letter-spacing: 0.04em;
|
|
color: var(--app-primary-color);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.ue-drawer__title {
|
|
margin: 0;
|
|
font-size: 18px;
|
|
line-height: 1.35;
|
|
font-weight: 700;
|
|
color: var(--ue-ink);
|
|
}
|
|
|
|
.ue-drawer__desc {
|
|
margin: 8px 0 0;
|
|
font-size: 13px;
|
|
line-height: 1.55;
|
|
color: var(--ue-muted);
|
|
}
|
|
|
|
.ue-drawer__close {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 32px;
|
|
height: 32px;
|
|
border: 1px solid var(--ue-edge);
|
|
border-radius: 12px;
|
|
background: var(--ue-panel-bg);
|
|
color: var(--ue-muted);
|
|
cursor: pointer;
|
|
transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
|
|
|
&:hover {
|
|
border-color: color-mix(in srgb, var(--app-primary-color) 22%, var(--ue-edge));
|
|
background: var(--app-acrylic-hover-bg);
|
|
color: var(--app-primary-color);
|
|
}
|
|
}
|
|
|
|
.ue-drawer__body {
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow: auto;
|
|
padding: 16px 16px 10px;
|
|
}
|
|
|
|
.ue-preview {
|
|
display: flex;
|
|
gap: 14px;
|
|
align-items: center;
|
|
margin-bottom: 14px;
|
|
padding: 14px 16px;
|
|
border-radius: var(--ue-radius);
|
|
border: 1px solid var(--ue-edge);
|
|
background: var(--ue-panel-bg);
|
|
backdrop-filter: var(--app-acrylic-blur);
|
|
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
|
box-shadow: none;
|
|
color: var(--ue-ink);
|
|
}
|
|
|
|
.ue-preview__meta {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.ue-preview__actions {
|
|
flex-shrink: 0;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.ue-preview__action {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 32px;
|
|
padding: 0 12px;
|
|
border: 1px solid var(--ue-edge);
|
|
border-radius: 12px;
|
|
background: var(--ue-panel-bg);
|
|
color: var(--app-primary-color);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
|
|
|
&:hover:not(:disabled) {
|
|
border-color: color-mix(in srgb, var(--app-primary-color) 22%, var(--ue-edge));
|
|
background: var(--app-acrylic-hover-bg);
|
|
}
|
|
|
|
&:disabled {
|
|
opacity: 0.55;
|
|
cursor: not-allowed;
|
|
}
|
|
}
|
|
|
|
.ue-reset-tip {
|
|
margin: 0 0 12px;
|
|
font-size: 13px;
|
|
line-height: 1.5;
|
|
color: var(--ue-muted);
|
|
}
|
|
|
|
.ue-preview__avatar {
|
|
width: 52px;
|
|
height: 52px;
|
|
border-radius: 14px;
|
|
overflow: hidden;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
border: 1px solid var(--ue-edge);
|
|
background: color-mix(in srgb, var(--app-primary-color) 12%, #fff);
|
|
color: var(--app-primary-color);
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
}
|
|
|
|
.ue-preview__name {
|
|
margin: 0;
|
|
font-size: 17px;
|
|
font-weight: 700;
|
|
line-height: 1.3;
|
|
color: var(--ue-ink);
|
|
}
|
|
|
|
.ue-preview__account {
|
|
margin: 4px 0 0;
|
|
font-size: 12px;
|
|
color: var(--ue-soft);
|
|
}
|
|
|
|
.ue-preview__tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.ue-preview__tag {
|
|
display: inline-flex;
|
|
padding: 2px 8px;
|
|
border-radius: 999px;
|
|
font-size: 11px;
|
|
border: 1px solid var(--ue-edge);
|
|
background: color-mix(in srgb, #fff 55%, transparent);
|
|
color: var(--ue-muted);
|
|
|
|
&--accent {
|
|
border-color: color-mix(in srgb, var(--app-primary-color) 22%, var(--ue-edge));
|
|
background: color-mix(in srgb, var(--app-primary-color) 10%, #fff);
|
|
color: var(--app-primary-color);
|
|
}
|
|
}
|
|
|
|
.ue-section {
|
|
margin-bottom: 14px;
|
|
padding: 14px 14px 6px;
|
|
border-radius: var(--ue-radius);
|
|
border: 1px solid var(--ue-edge);
|
|
background: var(--ue-panel-bg);
|
|
backdrop-filter: var(--app-acrylic-blur);
|
|
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
|
box-shadow: none;
|
|
}
|
|
|
|
.ue-section__head {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: flex-start;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.ue-section__index {
|
|
flex-shrink: 0;
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 12px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
color: var(--app-primary-color);
|
|
border: 1px solid color-mix(in srgb, var(--app-primary-color) 18%, var(--ue-edge));
|
|
background: color-mix(in srgb, var(--app-primary-color) 10%, #fff);
|
|
}
|
|
|
|
.ue-section__title {
|
|
margin: 0;
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
color: var(--ue-ink);
|
|
}
|
|
|
|
.ue-section__hint {
|
|
margin: 2px 0 0;
|
|
font-size: 12px;
|
|
color: var(--ue-soft);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.ue-form {
|
|
:deep(.arco-form-item) {
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
:deep(.arco-form-item-label) {
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--ue-ink);
|
|
}
|
|
|
|
:deep(.arco-input-wrapper),
|
|
:deep(.arco-select-view-single),
|
|
:deep(.arco-select-view-multiple),
|
|
:deep(.arco-textarea-wrapper),
|
|
:deep(.arco-input-number),
|
|
:deep(.arco-picker) {
|
|
border-radius: 12px;
|
|
border-color: color-mix(in srgb, var(--ue-edge) 70%, #d0d5dd);
|
|
background: color-mix(in srgb, #fff 72%, transparent);
|
|
}
|
|
}
|
|
|
|
.ue-upload-placeholder {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--color-text-3);
|
|
font-size: 12px;
|
|
gap: 4px;
|
|
}
|
|
|
|
.ue-invite-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
width: 100%;
|
|
}
|
|
|
|
.ue-invite-input {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.ue-invite-copy {
|
|
flex-shrink: 0;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.ue-invite-hint {
|
|
color: var(--ue-soft);
|
|
font-size: 12px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.ue-drawer__footer {
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
padding: 14px 16px 16px;
|
|
border-top: 1px solid var(--ue-edge);
|
|
background: var(--ue-panel-bg);
|
|
backdrop-filter: var(--app-acrylic-blur);
|
|
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
|
}
|
|
</style>
|
|
|
|
<style lang="less">
|
|
.user-edit-drawer {
|
|
.arco-drawer-header {
|
|
display: none;
|
|
}
|
|
|
|
.arco-drawer-body {
|
|
padding: 16px;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
background: var(--app-page-bg, #f0f3f4);
|
|
}
|
|
|
|
.arco-drawer {
|
|
background: var(--app-page-bg, #f0f3f4);
|
|
}
|
|
}
|
|
</style>
|