接入前端登录权限底座

This commit is contained in:
andy committed 2026-07-09 20:42:16 +08:00
1 parent c190d5e3a8
commit e49b2ccc1d
27 files changed
+1745 -68

No files matched your search

@@ -9,7 +9,7 @@
</div>
<label
v-if="operations.length"
v-if="operations.length && showActions"
class="simulate-toggle"
>
<input
@@ -46,7 +46,10 @@
<ReservationStatusBadge :status="operation.operation_status" />
<div class="operation-item__actions">
<div
v-if="showActions"
class="operation-item__actions"
>
<button
type="button"
class="icon-button"
@@ -93,11 +96,14 @@ import { executeReservationOperaOperation, retryReservationOperaOperation } from
import type { ReservationOperaOperationResult } from '@/types/reservation'
import { formatReservationOperaOperation } from '@/utils/reservationDisplay'
const props = defineProps<{
const props = withDefaults(defineProps<{
taskId: string
operations: ReservationOperaOperationResult[]
executable: boolean
}>()
showActions?: boolean
}>(), {
showActions: true,
})
const emit = defineEmits<{
operationUpdated: [operation: ReservationOperaOperationResult]
@@ -117,8 +117,12 @@
:validation-errors="validationErrorMessages"
/>
<footer class="task-actions">
<footer
v-if="canEditTask || canConfirmTask"
class="task-actions"
>
<button
v-if="canEditTask"
type="button"
class="secondary-button"
:disabled="!canSave || mutationBusy !== null"
@@ -131,6 +135,7 @@
{{ t('task.saveDraft') }}
</button>
<button
v-if="canConfirmTask"
type="button"
class="primary-button"
:disabled="!canConfirm || mutationBusy !== null"
@@ -149,10 +154,14 @@
<ReservationOperaPanel
:task-id="detail.task_id"
:operations="detail.opera_operations"
:executable="detail.availability.executable"
:executable="detail.availability.executable && canExecuteOpera"
:show-actions="canExecuteOpera"
@operation-updated="replaceOperation"
/>
<ReservationAuditTimeline :items="audits" />
<ReservationAuditTimeline
v-if="canViewAudit"
:items="audits"
/>
</aside>
</div>
</template>
@@ -191,12 +200,14 @@ import {
} from '@/utils/reservationFieldRules'
import { formatReservationMaybeStableReason, formatReservationTaskCard } from '@/utils/reservationDisplay'
import { formatReservationDateTime } from '@/utils/reservationFormat'
import { useAuthStore } from '@/stores/authStore'
const props = defineProps<{
taskId: string
}>()
const { t } = useI18n()
const authStore = useAuthStore()
const loading = ref(false)
const errorMessage = ref('')
const actionErrorMessage = ref('')
@@ -208,8 +219,12 @@ const validationErrors = ref<ReservationFieldValidationErrors>({})
const validationSummaryActive = ref(false)
const mutationBusy = ref<'save' | 'confirm' | 'manual-convert' | null>(null)
const canSave = computed(() => Boolean(detail.value?.availability.editable))
const canConfirm = computed(() => Boolean(detail.value?.availability.confirmable))
const canEditTask = computed(() => authStore.hasPermission('RESERVATION_TASK_EDIT'))
const canConfirmTask = computed(() => authStore.hasPermission('RESERVATION_TASK_CONFIRM'))
const canExecuteOpera = computed(() => authStore.hasPermission('RESERVATION_OPERA_SIM_EXECUTE'))
const canViewAudit = computed(() => authStore.hasPermission('RESERVATION_AUDIT_READ'))
const canSave = computed(() => canEditTask.value && Boolean(detail.value?.availability.editable))
const canConfirm = computed(() => canConfirmTask.value && Boolean(detail.value?.availability.confirmable))
const isManualReviewTask = computed(() => (detail.value ? taskHasManualReviewCode(detail.value) : false))
const validationErrorMessages = computed(() => toValidationErrorMessages(validationErrors.value))
@@ -237,7 +252,12 @@ async function loadTask(taskId: string): Promise<void> {
try {
const [nextDetail, nextAudits] = await Promise.all([
fetchReservationTaskDetail(taskId),
fetchReservationTaskAudits(taskId),
canViewAudit.value
? fetchReservationTaskAudits(taskId)
: Promise.resolve({
task_id: taskId,
items: [],
}),
])
applyTaskData(nextDetail, nextAudits.items)
} catch (error) {
@@ -271,7 +291,12 @@ async function submitManualReviewConversion(request: ReservationManualReviewConv
const result = await convertReservationManualReviewTask(detail.value.task_id, request)
const [nextDetail, nextAudits] = await Promise.all([
fetchReservationTaskDetail(detail.value.task_id),
fetchReservationTaskAudits(detail.value.task_id),
canViewAudit.value
? fetchReservationTaskAudits(detail.value.task_id)
: Promise.resolve({
task_id: detail.value.task_id,
items: [],
}),
])
applyTaskData(nextDetail, nextAudits.items)
notice.value = t('task.manualConversion.succeeded', {
@@ -289,6 +314,9 @@ async function mutateTaskPayload(action: 'save' | 'confirm'): Promise<void> {
if (!detail.value) {
return
}
if ((action === 'save' && !canSave.value) || (action === 'confirm' && !canConfirm.value)) {
return
}
const taskId = detail.value.task_id
mutationBusy.value = action
actionErrorMessage.value = ''
@@ -310,7 +338,9 @@ async function mutateTaskPayload(action: 'save' | 'confirm'): Promise<void> {
confirmed_payload: result.confirmed_payload,
opera_operations: result.opera_operations,
}
await refreshAudits(taskId)
if (canViewAudit.value) {
await refreshAudits(taskId)
}
notice.value = action === 'save' ? t('task.savedLocally') : t('task.confirmedLocally')
} catch (error) {
actionErrorMessage.value = error instanceof Error ? error.message : String(error)