VIP 멤버십 공개 범위 적용 v1.5.5
This commit is contained in:
@@ -24,13 +24,22 @@ const passwordModalOpen = ref(false)
|
||||
const deleteModalOpen = ref(false)
|
||||
const isUpdatingPassword = ref(false)
|
||||
const isDeletingMember = ref(false)
|
||||
const isUpdatingRole = ref(false)
|
||||
const actionMessage = ref('')
|
||||
const actionError = ref('')
|
||||
|
||||
const roleOptions = [
|
||||
{ value: 'owner', label: '소유자' },
|
||||
{ value: 'admin', label: '관리자' },
|
||||
{ value: 'vip', label: 'VIP' },
|
||||
{ value: 'member', label: '멤버' }
|
||||
]
|
||||
|
||||
const form = reactive({
|
||||
username: '',
|
||||
email: '',
|
||||
avatarUrl: '',
|
||||
roleCode: 'member',
|
||||
labelsText: '',
|
||||
note: ''
|
||||
})
|
||||
@@ -53,6 +62,7 @@ const syncMemberForm = () => {
|
||||
form.username = member.username || ''
|
||||
form.email = member.email || ''
|
||||
form.avatarUrl = member.avatarUrl || ''
|
||||
form.roleCode = member.roleCode || 'member'
|
||||
form.labelsText = Array.isArray(member.labels) ? member.labels.join(', ') : ''
|
||||
form.note = member.note || ''
|
||||
}
|
||||
@@ -77,6 +87,8 @@ const normalizedLabels = computed(() => [...new Set(
|
||||
.filter(Boolean)
|
||||
)])
|
||||
|
||||
const currentRoleLabel = computed(() => roleOptions.find((option) => option.value === form.roleCode)?.label || '멤버')
|
||||
|
||||
/**
|
||||
* 회원 저장 요청 본문을 문자열로 직렬화한다.
|
||||
* @returns {string} 직렬화된 회원 입력값
|
||||
@@ -328,6 +340,40 @@ const updateMemberPassword = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 권한으로 회원 등급을 변경한다.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const updateMemberRole = async () => {
|
||||
if (isNewMember.value || isUpdatingRole.value) {
|
||||
return
|
||||
}
|
||||
|
||||
actionMessage.value = ''
|
||||
actionError.value = ''
|
||||
isUpdatingRole.value = true
|
||||
|
||||
try {
|
||||
const updated = await $fetch(`/admin/api/members/${props.member.id}/role`, {
|
||||
method: 'PUT',
|
||||
body: {
|
||||
role: form.roleCode
|
||||
}
|
||||
})
|
||||
|
||||
emit('saved', {
|
||||
...props.member,
|
||||
...updated
|
||||
})
|
||||
actionMessage.value = '멤버 등급이 변경되었습니다.'
|
||||
} catch (error) {
|
||||
form.roleCode = props.member?.roleCode || 'member'
|
||||
actionError.value = error?.data?.message || '멤버 등급 변경에 실패했습니다.'
|
||||
} finally {
|
||||
isUpdatingRole.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 권한으로 회원을 삭제한다.
|
||||
* @returns {Promise<void>}
|
||||
@@ -481,6 +527,7 @@ watch(() => props.member, () => {
|
||||
<div class="min-w-0">
|
||||
<h2 class="truncate text-lg font-semibold text-[#15171a]">{{ pageTitle }}</h2>
|
||||
<p class="mt-1 truncate text-sm text-[#657080]">{{ form.email || '이메일 없음' }}</p>
|
||||
<p class="mt-1 text-xs font-semibold uppercase tracking-[0.04em] text-[#9aa4b2]">{{ currentRoleLabel }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -532,6 +579,23 @@ watch(() => props.member, () => {
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="admin-member-form__field mt-5 block">
|
||||
<span class="admin-member-form__label mb-2 block text-sm font-semibold text-[#15171a]">멤버 등급</span>
|
||||
<select
|
||||
v-model="form.roleCode"
|
||||
class="admin-member-form__select h-12 w-full rounded-md border border-transparent bg-[#eef1f4] px-4 text-sm text-[#15171a] outline-none focus:border-[#c5ccd5] focus:bg-white focus:ring-2 focus:ring-[#dce2e8] disabled:opacity-60"
|
||||
:disabled="isNewMember || isUpdatingRole"
|
||||
@change="updateMemberRole"
|
||||
>
|
||||
<option v-for="option in roleOptions" :key="option.value" :value="option.value">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</select>
|
||||
<span class="admin-member-form__hint mt-2 block text-sm text-[#8a95a5]">
|
||||
VIP 이상 등급은 멤버십 게시물을 볼 수 있습니다.
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label class="admin-member-form__field mt-5 block">
|
||||
<span class="admin-member-form__label mb-2 block text-sm font-semibold text-[#15171a]">레이블</span>
|
||||
<input v-model="form.labelsText" class="admin-member-form__input h-12 w-full rounded-md border border-transparent bg-[#eef1f4] px-4 text-sm text-[#15171a] outline-none focus:border-[#c5ccd5] focus:bg-white focus:ring-2 focus:ring-[#dce2e8]" type="text" placeholder="쉼표로 구분">
|
||||
|
||||
@@ -1659,7 +1659,7 @@ defineExpose({
|
||||
</svg>
|
||||
</span>
|
||||
<span v-if="form.status === 'members'" class="admin-post-form__hint text-xs text-muted">
|
||||
로그인한 회원에게만 공개됩니다. 등급별 제한은 이후 멤버십 등급 기능에서 확장합니다.
|
||||
VIP 이상 등급 회원에게만 공개됩니다.
|
||||
</span>
|
||||
<span v-else-if="form.status === 'private'" class="admin-post-form__hint text-xs text-muted">
|
||||
공개 사용자 화면에서는 보이지 않습니다.
|
||||
|
||||
Reference in New Issue
Block a user