+
{{ item.reason }}
@@ -915,6 +977,7 @@ import {
type GrayMemberGroup,
type GrayMode,
type GraySelectionSource,
+ type PreflightSubmitResultDto,
type PublishMode,
type RnBundle,
type RnBundleInspectResult,
@@ -1485,9 +1548,30 @@ const selectedStores = ref
([])
const submitStoreMode = ref('MANUAL')
const submitStoreScheduledAt = ref('')
+// Preflight dialog state
+const showPreflight = ref(false)
+const preflightLoading = ref(false)
+const preflightResult = ref(null)
+const preflightSkipCheck = ref(false)
+
const showStoreReviewDetail = ref(false)
const storeReviewDetailVersion = ref(null)
-const storeReviewDetailItems = ref<{ store: string; state: string; reason?: string; stage?: string; submittedAt?: string; updatedAt?: string; batchId?: string; liveOnStore?: boolean; preExisting?: boolean }[]>([])
+type StoreReviewItem = {
+ store: string
+ state: string
+ reason?: string
+ stage?: string
+ submittedAt?: string
+ updatedAt?: string
+ batchId?: string
+ liveOnStore?: boolean
+ preExisting?: boolean
+ currentSubmissionLive?: boolean
+ nonCurrentRelease?: boolean
+ liveVersionName?: string
+ liveVersionCode?: string
+}
+const storeReviewDetailItems = ref([])
const storeReviewDetailLive = ref(false)
const cancellingReview = ref(false)
const retryingStores = ref>(new Set())
@@ -1584,15 +1668,32 @@ async function handleUpdatePublishSchedule() {
}
}
-function openSubmitStoreDialog(row: AppVersion) {
+async function openSubmitStoreDialog(row: AppVersion) {
submitStoreVersion.value = row
- // Exclude stores already in active review from the pre-selection
+ preflightResult.value = null
+ preflightSkipCheck.value = false
+ showPreflight.value = true
+ preflightLoading.value = true
+ try {
+ const res = await updateAdminApi.preflightStoreSubmission(row.id)
+ preflightResult.value = res.data.data
+ // Auto-open submit dialog if all allowed
+ const allAllowed = preflightResult.value?.stores.every(s => s.canSubmit) ?? false
+ if (allAllowed) {
+ showPreflight.value = false
+ showSubmitStore.value = true
+ }
+ } catch (e: any) {
+ ElMessage.error(e?.response?.data?.message || '前置检查失败')
+ } finally {
+ preflightLoading.value = false
+ }
+ // Fallback: pre-select stores
selectedStores.value = enabledStores.value
.map(s => s.type)
.filter(t => !isStoreActiveReview(row, t))
submitStoreMode.value = row.storeSubmitMode ?? 'MANUAL'
submitStoreScheduledAt.value = row.storeSubmitScheduledAt ?? ''
- showSubmitStore.value = true
}
function getSubmitDialogStoreState(version: AppVersion | null, storeType: string): string {
@@ -2143,6 +2244,54 @@ async function promptUnpublishApp(id: string) {
}
}
+async function promptDeleteAppVersion(row: AppVersion) {
+ try {
+ await ElMessageBox.confirm(
+ `确定删除版本 ${row.versionName}(${row.versionCode})吗?删除后不可恢复。`,
+ '删除确认',
+ { confirmButtonText: '删除', cancelButtonText: '取消', type: 'warning' },
+ )
+ await updateAdminApi.deleteAppVersion(row.id)
+ ElMessage.success('已删除')
+ await loadAppVersions()
+ await loadOperationLogs()
+ } catch (e: any) {
+ if (e !== 'cancel') {
+ ElMessage.error(e?.response?.data?.message || '删除失败')
+ }
+ }
+}
+
+function hasActiveStoreReview(row: AppVersion): boolean {
+ if (!row.storeReviewStatus) return false
+ const items = parseStoreReview(row.storeReviewStatus)
+ return items.some(i => i.state === 'SUBMITTING' || i.state === 'UNDER_REVIEW')
+}
+
+function preflightTagType(state: string): string {
+ return {
+ ONLINE: 'success',
+ UNDER_REVIEW: 'warning',
+ UNDER_REVIEW_XIAOMI: 'warning',
+ REJECTED: 'danger',
+ NOT_FOUND: 'info',
+ UNKNOWN: 'info',
+ QUERY_FAILED: 'danger',
+ }[state] ?? ''
+}
+
+function preflightStateLabel(state: string): string {
+ return {
+ ONLINE: '已上线',
+ UNDER_REVIEW: '审核中',
+ UNDER_REVIEW_XIAOMI: '审核中(版本号可能不准确)',
+ REJECTED: '已拒绝',
+ NOT_FOUND: '未找到',
+ UNKNOWN: '未知',
+ QUERY_FAILED: '查询失败',
+ }[state] ?? state
+}
+
async function promptUnpublishRn(id: string) {
const reason = await promptUnpublishReason('下架确认')
if (!reason) return
@@ -2179,6 +2328,13 @@ function reviewLabel(state: string): string {
}[state] ?? state
}
+function reviewDisplayLabel(item: StoreReviewItem): string {
+ if (item.state === 'APPROVED' && (item.nonCurrentRelease || item.currentSubmissionLive === false)) {
+ return '已上线(非本次发布)'
+ }
+ return reviewLabel(item.state)
+}
+
function reviewTagType(state: string): string {
return {
PENDING: 'info', SUBMITTING: 'primary', UNDER_REVIEW: 'warning',
@@ -2308,7 +2464,7 @@ function scheduleStoreReviewReload() {
}, 200)
}
-function parseStoreReview(json?: string): { store: string; state: string; reason?: string; stage?: string; submittedAt?: string; updatedAt?: string; batchId?: string; liveOnStore?: boolean; preExisting?: boolean }[] {
+function parseStoreReview(json?: string): StoreReviewItem[] {
if (!json) return []
try {
const m = JSON.parse(json) as Record
@@ -2328,6 +2484,10 @@ function parseStoreReview(json?: string): { store: string; state: string; reason
batchId: String(item.batchId ?? ''),
liveOnStore: item.liveOnStore === true,
preExisting: item.preExisting === true,
+ currentSubmissionLive: typeof item.currentSubmissionLive === 'boolean' ? item.currentSubmissionLive : undefined,
+ nonCurrentRelease: item.nonCurrentRelease === true,
+ liveVersionName: String(item.liveVersionName ?? ''),
+ liveVersionCode: String(item.liveVersionCode ?? ''),
}
}
return { store, state: String(value ?? ''), reason: '' }