增加忽略商品
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import { createTask, listTasksByOrderId, updateTask } from '../../repositories/task-repo.js'
|
import { createTask, listTasksByOrderId, updateTask } from '../../repositories/task-repo.js'
|
||||||
import {
|
import {
|
||||||
getFulfillmentProfileByKey,
|
|
||||||
listFulfillmentProfileRequirements,
|
listFulfillmentProfileRequirements,
|
||||||
resolveFulfillmentBinding,
|
resolveFulfillmentBinding,
|
||||||
} from '../../repositories/fulfillment-profile-repo.js'
|
} from '../../repositories/fulfillment-profile-repo.js'
|
||||||
@@ -34,10 +33,11 @@ export async function syncDeliveryTasksForOrder(order, orderItems) {
|
|||||||
platform: order.platform,
|
platform: order.platform,
|
||||||
shopId: order.shop_id,
|
shopId: order.shop_id,
|
||||||
})
|
})
|
||||||
const profile = binding || await getFulfillmentProfileByKey('manual_review')
|
|
||||||
if (!profile) {
|
if (!binding) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
const profile = binding
|
||||||
const requirements = await listFulfillmentProfileRequirements(profile.profile_id || profile.id)
|
const requirements = await listFulfillmentProfileRequirements(profile.profile_id || profile.id)
|
||||||
const primaryRequirement = requirements.find((requirement) => requirement.is_required !== false) || requirements[0] || null
|
const primaryRequirement = requirements.find((requirement) => requirement.is_required !== false) || requirements[0] || null
|
||||||
const quantity = Math.max(1, Number(item.quantity || 1))
|
const quantity = Math.max(1, Number(item.quantity || 1))
|
||||||
|
|||||||
@@ -26,6 +26,37 @@ export async function upsertOrderFromWebhook(event) {
|
|||||||
existingOrderId: existing?.id || null,
|
existingOrderId: existing?.id || null,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const resolvedItems = await Promise.all(
|
||||||
|
event.items.map((item) => resolveOrderItemForFulfillment({
|
||||||
|
provider: event.provider,
|
||||||
|
platform: event.platform,
|
||||||
|
shopId: event.shopId,
|
||||||
|
item,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
const configuredItems = resolvedItems.filter((item) => item.isConfigured)
|
||||||
|
|
||||||
|
if (configuredItems.length === 0) {
|
||||||
|
logWebhook('[order-service]', 'Webhook 订单已忽略:未命中任何已配置履约商品', {
|
||||||
|
provider: event.provider,
|
||||||
|
platform: event.platform,
|
||||||
|
shopId: event.shopId,
|
||||||
|
shopName: event.shopName,
|
||||||
|
platformOrderId: event.platformOrderId,
|
||||||
|
existingOrderId: existing?.id || null,
|
||||||
|
rawItemCount: event.items.length,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
ignored: true,
|
||||||
|
ignoreReason: 'unconfigured_items',
|
||||||
|
order: null,
|
||||||
|
orderItems: [],
|
||||||
|
tasks: [],
|
||||||
|
messageDeliveries: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const basePayload = {
|
const basePayload = {
|
||||||
provider: event.provider,
|
provider: event.provider,
|
||||||
platform: event.platform,
|
platform: event.platform,
|
||||||
@@ -54,18 +85,9 @@ export async function upsertOrderFromWebhook(event) {
|
|||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
})
|
})
|
||||||
|
|
||||||
const resolvedItems = await Promise.all(
|
|
||||||
event.items.map((item) => resolveOrderItemForFulfillment({
|
|
||||||
provider: event.provider,
|
|
||||||
platform: event.platform,
|
|
||||||
shopId: event.shopId,
|
|
||||||
item,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
|
|
||||||
const orderItems = await replaceOrderItems(
|
const orderItems = await replaceOrderItems(
|
||||||
order.id,
|
order.id,
|
||||||
resolvedItems.map((item) => ({
|
configuredItems.map((item) => ({
|
||||||
skuCode: item.skuCode,
|
skuCode: item.skuCode,
|
||||||
skuName: item.skuName,
|
skuName: item.skuName,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
@@ -88,7 +110,7 @@ export async function upsertOrderFromWebhook(event) {
|
|||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
orderItemCount: orderItems.length,
|
orderItemCount: orderItems.length,
|
||||||
taskCount: tasks.length,
|
taskCount: tasks.length,
|
||||||
resolvedSkuCodes: resolvedItems.map((item) => item.skuCode),
|
resolvedSkuCodes: configuredItems.map((item) => item.skuCode),
|
||||||
})
|
})
|
||||||
|
|
||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { resolveFulfillmentBinding } from '../../repositories/fulfillment-profile-repo.js'
|
||||||
import { resolveProductMatchRule } from '../../repositories/product-match-rule-repo.js'
|
import { resolveProductMatchRule } from '../../repositories/product-match-rule-repo.js'
|
||||||
|
|
||||||
export async function resolveOrderItemForFulfillment({
|
export async function resolveOrderItemForFulfillment({
|
||||||
@@ -5,6 +6,105 @@ export async function resolveOrderItemForFulfillment({
|
|||||||
platform = '',
|
platform = '',
|
||||||
shopId = '',
|
shopId = '',
|
||||||
item = {},
|
item = {},
|
||||||
|
}) {
|
||||||
|
const candidate = await resolveConfiguredItemCandidate({
|
||||||
|
provider,
|
||||||
|
platform,
|
||||||
|
shopId,
|
||||||
|
item,
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
externalItemId,
|
||||||
|
externalSkuCode,
|
||||||
|
externalSkuName,
|
||||||
|
externalSkuNameNormalized,
|
||||||
|
matchedRule,
|
||||||
|
binding,
|
||||||
|
} = candidate
|
||||||
|
|
||||||
|
const resolvedSkuCode = pickFirstNonEmpty([
|
||||||
|
matchedRule?.resolved_sku_code,
|
||||||
|
externalSkuCode,
|
||||||
|
externalItemId,
|
||||||
|
])
|
||||||
|
const resolvedSkuName = pickFirstNonEmpty([
|
||||||
|
readConfigValue(matchedRule?.config_json, 'resolvedSkuName'),
|
||||||
|
readConfigValue(matchedRule?.config_json, 'internalProductName'),
|
||||||
|
item.skuName,
|
||||||
|
externalSkuName,
|
||||||
|
resolvedSkuCode,
|
||||||
|
])
|
||||||
|
const snapshot = {
|
||||||
|
...(isPlainObject(item.snapshot) ? item.snapshot : {}),
|
||||||
|
externalItemId,
|
||||||
|
externalSkuCode,
|
||||||
|
externalSkuName,
|
||||||
|
externalSkuNameNormalized,
|
||||||
|
resolvedSkuCode,
|
||||||
|
matchedProductRuleId: matchedRule ? Number(matchedRule.id) : null,
|
||||||
|
matchedProductRuleBy: String(matchedRule?.matched_by || '').trim(),
|
||||||
|
matchedFulfillmentBindingId: binding ? Number(binding.id) : null,
|
||||||
|
matchedFulfillmentProfileKey: String(binding?.profile_key || '').trim(),
|
||||||
|
isConfigured: candidate.isConfigured,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
itemId: externalItemId,
|
||||||
|
externalItemId,
|
||||||
|
externalSkuCode,
|
||||||
|
externalSkuName,
|
||||||
|
skuCode: resolvedSkuCode,
|
||||||
|
skuName: resolvedSkuName,
|
||||||
|
snapshot,
|
||||||
|
isConfigured: candidate.isConfigured,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function hasConfiguredOrderItems({
|
||||||
|
provider = '',
|
||||||
|
platform = '',
|
||||||
|
shopId = '',
|
||||||
|
items = [],
|
||||||
|
}) {
|
||||||
|
const candidates = await Promise.all(
|
||||||
|
(Array.isArray(items) ? items : []).map((item) => resolveConfiguredItemCandidate({
|
||||||
|
provider,
|
||||||
|
platform,
|
||||||
|
shopId,
|
||||||
|
item,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
|
return candidates.some((item) => item.isConfigured)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeProductName(value) {
|
||||||
|
return String(value || '')
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[【】\[\]()()]/g, ' ')
|
||||||
|
.replace(/(自动发货|秒发|极速发货|官方直充|官方充值)/gi, ' ')
|
||||||
|
.replace(/[^\p{L}\p{N}]+/gu, ' ')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickFirstNonEmpty(values) {
|
||||||
|
for (const value of values) {
|
||||||
|
const normalized = String(value || '').trim()
|
||||||
|
if (normalized) {
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveConfiguredItemCandidate({
|
||||||
|
provider = '',
|
||||||
|
platform = '',
|
||||||
|
shopId = '',
|
||||||
|
item = {},
|
||||||
}) {
|
}) {
|
||||||
const externalItemId = pickFirstNonEmpty([
|
const externalItemId = pickFirstNonEmpty([
|
||||||
item.externalItemId,
|
item.externalItemId,
|
||||||
@@ -30,61 +130,30 @@ export async function resolveOrderItemForFulfillment({
|
|||||||
externalSkuCode,
|
externalSkuCode,
|
||||||
externalSkuNameNormalized,
|
externalSkuNameNormalized,
|
||||||
})
|
})
|
||||||
|
|
||||||
const resolvedSkuCode = pickFirstNonEmpty([
|
const resolvedSkuCode = pickFirstNonEmpty([
|
||||||
matchedRule?.resolved_sku_code,
|
matchedRule?.resolved_sku_code,
|
||||||
externalSkuCode,
|
externalSkuCode,
|
||||||
externalItemId,
|
externalItemId,
|
||||||
])
|
])
|
||||||
const resolvedSkuName = pickFirstNonEmpty([
|
const binding = resolvedSkuCode
|
||||||
readConfigValue(matchedRule?.config_json, 'resolvedSkuName'),
|
? await resolveFulfillmentBinding({
|
||||||
readConfigValue(matchedRule?.config_json, 'internalProductName'),
|
skuCode: resolvedSkuCode,
|
||||||
item.skuName,
|
provider,
|
||||||
externalSkuName,
|
platform,
|
||||||
resolvedSkuCode,
|
shopId,
|
||||||
])
|
})
|
||||||
const snapshot = {
|
: null
|
||||||
...(isPlainObject(item.snapshot) ? item.snapshot : {}),
|
|
||||||
|
return {
|
||||||
externalItemId,
|
externalItemId,
|
||||||
externalSkuCode,
|
externalSkuCode,
|
||||||
externalSkuName,
|
externalSkuName,
|
||||||
externalSkuNameNormalized,
|
externalSkuNameNormalized,
|
||||||
|
matchedRule,
|
||||||
resolvedSkuCode,
|
resolvedSkuCode,
|
||||||
matchedProductRuleId: matchedRule ? Number(matchedRule.id) : null,
|
binding,
|
||||||
matchedProductRuleBy: String(matchedRule?.matched_by || '').trim(),
|
isConfigured: Boolean(binding),
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
itemId: externalItemId,
|
|
||||||
externalItemId,
|
|
||||||
externalSkuCode,
|
|
||||||
externalSkuName,
|
|
||||||
skuCode: resolvedSkuCode,
|
|
||||||
skuName: resolvedSkuName,
|
|
||||||
snapshot,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeProductName(value) {
|
|
||||||
return String(value || '')
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/[【】\[\]()()]/g, ' ')
|
|
||||||
.replace(/(自动发货|秒发|极速发货|官方直充|官方充值)/gi, ' ')
|
|
||||||
.replace(/[^\p{L}\p{N}]+/gu, ' ')
|
|
||||||
.replace(/\s+/g, ' ')
|
|
||||||
.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
function pickFirstNonEmpty(values) {
|
|
||||||
for (const value of values) {
|
|
||||||
const normalized = String(value || '').trim()
|
|
||||||
if (normalized) {
|
|
||||||
return normalized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function readConfigValue(rawValue, key) {
|
function readConfigValue(rawValue, key) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { runtimeConfig } from '../../config/runtime.js'
|
|||||||
import { createWebhookEvent, updateWebhookEvent } from '../../repositories/webhook-event-repo.js'
|
import { createWebhookEvent, updateWebhookEvent } from '../../repositories/webhook-event-repo.js'
|
||||||
import { enrichAgisoXianyuTradeOrder } from '../platforms/agiso/xianyu/order-detail-service.js'
|
import { enrichAgisoXianyuTradeOrder } from '../platforms/agiso/xianyu/order-detail-service.js'
|
||||||
import { upsertOrderFromWebhook } from './order-service.js'
|
import { upsertOrderFromWebhook } from './order-service.js'
|
||||||
|
import { hasConfiguredOrderItems } from './product-match-service.js'
|
||||||
import { createHttpError } from '../../utils/http.js'
|
import { createHttpError } from '../../utils/http.js'
|
||||||
import { parseJsonObject } from '../../utils/json.js'
|
import { parseJsonObject } from '../../utils/json.js'
|
||||||
import { logWebhook } from '../../utils/logger.js'
|
import { logWebhook } from '../../utils/logger.js'
|
||||||
@@ -89,6 +90,52 @@ async function executeAgisoTradeWebhook(parsed, webhookEventId, { requestId = ''
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shouldProcess = await hasConfiguredOrderItems({
|
||||||
|
provider: parsed.provider,
|
||||||
|
platform: parsed.platform,
|
||||||
|
shopId: parsed.shopId,
|
||||||
|
items: parsed.items,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!shouldProcess) {
|
||||||
|
await updateWebhookEvent(webhookEventId, {
|
||||||
|
processed: true,
|
||||||
|
process_error: 'ignored_unconfigured_product',
|
||||||
|
related_order_id: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
logWebhook('[webhook-service/agiso]', 'Webhook 已忽略:未命中任何已配置履约商品', {
|
||||||
|
requestId,
|
||||||
|
webhookEventId,
|
||||||
|
provider: parsed.provider,
|
||||||
|
platform: parsed.platform,
|
||||||
|
shopId: parsed.shopId,
|
||||||
|
shopName: parsed.shopName,
|
||||||
|
platformOrderId: parsed.platformOrderId,
|
||||||
|
rawItemCount: Array.isArray(parsed.items) ? parsed.items.length : 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
accepted: true,
|
||||||
|
ignored: true,
|
||||||
|
ignoreReason: 'unconfigured_product',
|
||||||
|
provider: parsed.provider,
|
||||||
|
platform: parsed.platform,
|
||||||
|
shopId: parsed.shopId,
|
||||||
|
shopName: parsed.shopName,
|
||||||
|
platformRaw: parsed.platformRaw,
|
||||||
|
eventType: parsed.eventType,
|
||||||
|
platformOrderId: parsed.platformOrderId,
|
||||||
|
orderId: null,
|
||||||
|
totalAmountFen: Number(parsed.totalAmount || 0),
|
||||||
|
enriched: false,
|
||||||
|
enrichReason: 'skipped_unconfigured_product',
|
||||||
|
taskCount: 0,
|
||||||
|
messageDeliveries: [],
|
||||||
|
tasks: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const enriched = await enrichTradeBeforeUpsert(parsed, {
|
const enriched = await enrichTradeBeforeUpsert(parsed, {
|
||||||
requestId,
|
requestId,
|
||||||
webhookEventId,
|
webhookEventId,
|
||||||
@@ -96,8 +143,8 @@ async function executeAgisoTradeWebhook(parsed, webhookEventId, { requestId = ''
|
|||||||
const result = await upsertOrderFromWebhook(enriched.parsed)
|
const result = await upsertOrderFromWebhook(enriched.parsed)
|
||||||
await updateWebhookEvent(webhookEventId, {
|
await updateWebhookEvent(webhookEventId, {
|
||||||
processed: true,
|
processed: true,
|
||||||
process_error: '',
|
process_error: result.ignored ? 'ignored_unconfigured_product' : '',
|
||||||
related_order_id: result.order.id,
|
related_order_id: result.order?.id || null,
|
||||||
})
|
})
|
||||||
|
|
||||||
logWebhook('[webhook-service/agiso]', 'Webhook 业务处理成功', {
|
logWebhook('[webhook-service/agiso]', 'Webhook 业务处理成功', {
|
||||||
@@ -110,7 +157,9 @@ async function executeAgisoTradeWebhook(parsed, webhookEventId, { requestId = ''
|
|||||||
eventKey: parsed.eventKey,
|
eventKey: parsed.eventKey,
|
||||||
eventType: parsed.eventType,
|
eventType: parsed.eventType,
|
||||||
platformOrderId: enriched.parsed.platformOrderId,
|
platformOrderId: enriched.parsed.platformOrderId,
|
||||||
orderId: result.order.id,
|
orderId: result.order?.id || null,
|
||||||
|
ignored: Boolean(result.ignored),
|
||||||
|
ignoreReason: String(result.ignoreReason || ''),
|
||||||
enriched: enriched.enriched,
|
enriched: enriched.enriched,
|
||||||
enrichReason: enriched.reason || '',
|
enrichReason: enriched.reason || '',
|
||||||
totalAmountFen: Number(enriched.parsed.totalAmount || 0),
|
totalAmountFen: Number(enriched.parsed.totalAmount || 0),
|
||||||
@@ -127,8 +176,10 @@ async function executeAgisoTradeWebhook(parsed, webhookEventId, { requestId = ''
|
|||||||
platformRaw: enriched.parsed.platformRaw,
|
platformRaw: enriched.parsed.platformRaw,
|
||||||
eventType: enriched.parsed.eventType,
|
eventType: enriched.parsed.eventType,
|
||||||
platformOrderId: enriched.parsed.platformOrderId,
|
platformOrderId: enriched.parsed.platformOrderId,
|
||||||
orderId: result.order.id,
|
orderId: result.order?.id || null,
|
||||||
totalAmountFen: Number(enriched.parsed.totalAmount || 0),
|
totalAmountFen: Number(enriched.parsed.totalAmount || 0),
|
||||||
|
ignored: Boolean(result.ignored),
|
||||||
|
ignoreReason: String(result.ignoreReason || ''),
|
||||||
enriched: enriched.enriched,
|
enriched: enriched.enriched,
|
||||||
enrichReason: enriched.reason || '',
|
enrichReason: enriched.reason || '',
|
||||||
taskCount: result.tasks.length,
|
taskCount: result.tasks.length,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ElMessage } from 'element-plus'
|
import { showError } from '@/lib/feedback'
|
||||||
|
|
||||||
export type TencentActionError = Error & {
|
export type TencentActionError = Error & {
|
||||||
errorCode?: string
|
errorCode?: string
|
||||||
@@ -8,7 +8,7 @@ export type TencentActionError = Error & {
|
|||||||
export function notifyTencentActionError(error: unknown, fallbackMessage: string) {
|
export function notifyTencentActionError(error: unknown, fallbackMessage: string) {
|
||||||
const message = resolveTencentActionMessage(error, fallbackMessage)
|
const message = resolveTencentActionMessage(error, fallbackMessage)
|
||||||
console.error(error)
|
console.error(error)
|
||||||
ElMessage.error(message)
|
showError(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveTencentActionMessage(error: unknown, fallbackMessage: string) {
|
export function resolveTencentActionMessage(error: unknown, fallbackMessage: string) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
|
import { showSuccess } from '@/lib/feedback'
|
||||||
import {
|
import {
|
||||||
confirmClaimRole,
|
confirmClaimRole,
|
||||||
createClaimSession,
|
createClaimSession,
|
||||||
@@ -262,7 +262,7 @@ export function useClaimPage(token: string) {
|
|||||||
|
|
||||||
detail.value = mergeClaimDetailData(detail.value, response.data)
|
detail.value = mergeClaimDetailData(detail.value, response.data)
|
||||||
roleConfirmed.value = true
|
roleConfirmed.value = true
|
||||||
ElMessage.success(response.msg || '角色已确认')
|
showSuccess(response.msg || '角色已确认')
|
||||||
restartPollingIfNeeded(response.data)
|
restartPollingIfNeeded(response.data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifyTencentActionError(error, '角色确认失败')
|
notifyTencentActionError(error, '角色确认失败')
|
||||||
@@ -289,7 +289,7 @@ export function useClaimPage(token: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
detail.value = mergeClaimDetailData(detail.value, response.data)
|
detail.value = mergeClaimDetailData(detail.value, response.data)
|
||||||
ElMessage.success(response.msg || '兑换完成')
|
showSuccess(response.msg || '兑换完成')
|
||||||
restartPollingIfNeeded(response.data)
|
restartPollingIfNeeded(response.data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifyTencentActionError(error, '领取兑换失败')
|
notifyTencentActionError(error, '领取兑换失败')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
|
import { showError, showSuccess } from '@/lib/feedback'
|
||||||
import {
|
import {
|
||||||
createTencentBrowserSession,
|
createTencentBrowserSession,
|
||||||
refreshTencentBrowserSessionPage,
|
refreshTencentBrowserSessionPage,
|
||||||
@@ -154,12 +154,12 @@ export function useTencentBrowserSessionPage() {
|
|||||||
|
|
||||||
async function redeemNow() {
|
async function redeemNow() {
|
||||||
if (!session.value?.sessionId) {
|
if (!session.value?.sessionId) {
|
||||||
ElMessage.error('请先创建浏览器会话')
|
showError('请先创建浏览器会话')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!redeemCode.value.trim()) {
|
if (!redeemCode.value.trim()) {
|
||||||
ElMessage.error('请输入兑换码')
|
showError('请输入兑换码')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ export function useTencentBrowserSessionPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
session.value = response.data
|
session.value = response.data
|
||||||
ElMessage.success(response.msg || '兑换完成')
|
showSuccess(response.msg || '兑换完成')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifyTencentActionError(error, '浏览器兑换失败')
|
notifyTencentActionError(error, '浏览器兑换失败')
|
||||||
await refreshSession({ silent: true })
|
await refreshSession({ silent: true })
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import type { ElMessageBoxOptions, MessageOptions } from 'element-plus'
|
||||||
|
|
||||||
|
import 'element-plus/theme-chalk/el-message.css'
|
||||||
|
import 'element-plus/theme-chalk/el-message-box.css'
|
||||||
|
|
||||||
|
const baseMessageOptions: Partial<MessageOptions> = {
|
||||||
|
grouping: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseConfirmOptions: ElMessageBoxOptions = {
|
||||||
|
type: 'warning',
|
||||||
|
confirmButtonText: '继续执行',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
closeOnClickModal: false,
|
||||||
|
closeOnPressEscape: false,
|
||||||
|
autofocus: false,
|
||||||
|
distinguishCancelAndClose: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const basePromptOptions: ElMessageBoxOptions = {
|
||||||
|
confirmButtonText: '提交',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
closeOnClickModal: false,
|
||||||
|
closeOnPressEscape: false,
|
||||||
|
autofocus: false,
|
||||||
|
distinguishCancelAndClose: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showSuccess(message: string, options: Partial<MessageOptions> = {}) {
|
||||||
|
return ElMessage.success({
|
||||||
|
...baseMessageOptions,
|
||||||
|
...options,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showError(message: string, options: Partial<MessageOptions> = {}) {
|
||||||
|
return ElMessage.error({
|
||||||
|
...baseMessageOptions,
|
||||||
|
...options,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showConfirm(message: string, title = '确认操作', options: ElMessageBoxOptions = {}) {
|
||||||
|
return ElMessageBox.confirm(message, title, {
|
||||||
|
...baseConfirmOptions,
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showPrompt(message: string, title: string, options: ElMessageBoxOptions = {}) {
|
||||||
|
return ElMessageBox.prompt(message, title, {
|
||||||
|
...basePromptOptions,
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFeedbackDismissed(error: unknown) {
|
||||||
|
return error === 'cancel' || error === 'close'
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, onMounted, ref } from 'vue'
|
import { nextTick, onMounted, ref } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
|
import { showSuccess } from '@/lib/feedback'
|
||||||
import {
|
import {
|
||||||
fetchAdminFulfillmentBindingConfigs,
|
fetchAdminFulfillmentBindingConfigs,
|
||||||
saveAdminFulfillmentBindingConfigs,
|
saveAdminFulfillmentBindingConfigs,
|
||||||
@@ -366,7 +366,7 @@ async function saveConfigs() {
|
|||||||
const response = await saveAdminFulfillmentBindingConfigs({ bindings: payload })
|
const response = await saveAdminFulfillmentBindingConfigs({ bindings: payload })
|
||||||
filePath.value = response.data.filePath
|
filePath.value = response.data.filePath
|
||||||
bindings.value = response.data.bindings.map(mapEditableBinding)
|
bindings.value = response.data.bindings.map(mapEditableBinding)
|
||||||
ElMessage.success('履约配置已保存')
|
showSuccess('履约配置已保存')
|
||||||
await loadConfigs()
|
await loadConfigs()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
validationState.value = {
|
validationState.value = {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
import AdminPaginationBar from '@/components/admin/AdminPaginationBar.vue'
|
import AdminPaginationBar from '@/components/admin/AdminPaginationBar.vue'
|
||||||
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
||||||
|
import { showConfirm, showError, showSuccess } from '@/lib/feedback'
|
||||||
import {
|
import {
|
||||||
createAdminInventoryItem,
|
createAdminInventoryItem,
|
||||||
fetchAdminInventoryItems,
|
fetchAdminInventoryItems,
|
||||||
@@ -238,7 +238,7 @@ function useGroupAsEntry(group: InventoryPoolGroup) {
|
|||||||
if (group.batchSummary !== '未分批' && !group.batchSummary.includes(' / ')) {
|
if (group.batchSummary !== '未分批' && !group.batchSummary.includes(' / ')) {
|
||||||
entryBatchNo.value = group.batchSummary
|
entryBatchNo.value = group.batchSummary
|
||||||
}
|
}
|
||||||
ElMessage.success(`已把 ${group.skuCode} / ${group.credentialType} 填入入库表单`)
|
showSuccess(`已把 ${group.skuCode} / ${group.credentialType} 填入入库表单`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildImportRows() {
|
function buildImportRows() {
|
||||||
@@ -322,7 +322,7 @@ function resetFilters() {
|
|||||||
|
|
||||||
async function submitCreate() {
|
async function submitCreate() {
|
||||||
if (!entrySkuCode.value.trim() || !singleDisplayValue.value.trim()) {
|
if (!entrySkuCode.value.trim() || !singleDisplayValue.value.trim()) {
|
||||||
ElMessage.error('请先填写内部履约 SKU 和凭据内容')
|
showError('请先填写内部履约 SKU 和凭据内容')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,11 +336,11 @@ async function submitCreate() {
|
|||||||
displayValue: singleDisplayValue.value.trim(),
|
displayValue: singleDisplayValue.value.trim(),
|
||||||
})
|
})
|
||||||
|
|
||||||
ElMessage.success('库存项已新增')
|
showSuccess('库存项已新增')
|
||||||
singleDisplayValue.value = ''
|
singleDisplayValue.value = ''
|
||||||
await loadInventoryItems(1)
|
await loadInventoryItems(1)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '新增库存项失败')
|
showError(error instanceof Error ? error.message : '新增库存项失败')
|
||||||
} finally {
|
} finally {
|
||||||
creating.value = false
|
creating.value = false
|
||||||
}
|
}
|
||||||
@@ -353,7 +353,7 @@ async function submitImport() {
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|
||||||
if (rawLines.length === 0) {
|
if (rawLines.length === 0) {
|
||||||
ElMessage.error('请至少输入一条待导入库存')
|
showError('请至少输入一条待导入库存')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,11 +363,11 @@ async function submitImport() {
|
|||||||
const rows = buildImportRows()
|
const rows = buildImportRows()
|
||||||
const response = await importAdminInventoryItems({ rows })
|
const response = await importAdminInventoryItems({ rows })
|
||||||
|
|
||||||
ElMessage.success(`导入完成,新增 ${response.data.created} 条,重复 ${response.data.duplicated} 条`)
|
showSuccess(`导入完成,新增 ${response.data.created} 条,重复 ${response.data.duplicated} 条`)
|
||||||
bulkText.value = ''
|
bulkText.value = ''
|
||||||
await loadInventoryItems(1)
|
await loadInventoryItems(1)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '导入失败')
|
showError(error instanceof Error ? error.message : '导入失败')
|
||||||
} finally {
|
} finally {
|
||||||
importing.value = false
|
importing.value = false
|
||||||
}
|
}
|
||||||
@@ -380,7 +380,7 @@ async function runRowAction(
|
|||||||
confirmText: string,
|
confirmText: string,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(confirmText, '确认操作', {
|
await showConfirm(confirmText, '确认操作', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: '继续执行',
|
confirmButtonText: '继续执行',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
@@ -393,10 +393,10 @@ async function runRowAction(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await action()
|
await action()
|
||||||
ElMessage.success(successMessage)
|
showSuccess(successMessage)
|
||||||
await loadInventoryItems()
|
await loadInventoryItems()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '操作失败')
|
showError(error instanceof Error ? error.message : '操作失败')
|
||||||
} finally {
|
} finally {
|
||||||
actionLoadingId.value = null
|
actionLoadingId.value = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
|
import { showSuccess } from '@/lib/feedback'
|
||||||
import { logoutAdmin } from '@/services/admin'
|
import { logoutAdmin } from '@/services/admin'
|
||||||
import { clearAdminSession, getAdminRole, getAdminTokenExpiresAt, getAdminUsername } from '@/utils/admin-auth'
|
import { clearAdminSession, getAdminRole, getAdminTokenExpiresAt, getAdminUsername } from '@/utils/admin-auth'
|
||||||
import { formatAdminDateTime } from '@/utils/admin-time'
|
import { formatAdminDateTime } from '@/utils/admin-time'
|
||||||
@@ -36,7 +36,7 @@ async function submitLogout() {
|
|||||||
// 后台退出是无状态的,本地清理优先
|
// 后台退出是无状态的,本地清理优先
|
||||||
} finally {
|
} finally {
|
||||||
clearAdminSession()
|
clearAdminSession()
|
||||||
ElMessage.success('已退出后台')
|
showSuccess('已退出后台')
|
||||||
await router.replace('/admin/login')
|
await router.replace('/admin/login')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
|
import { showError, showSuccess } from '@/lib/feedback'
|
||||||
import { loginAdmin } from '@/services/admin'
|
import { loginAdmin } from '@/services/admin'
|
||||||
import { setAdminSession } from '@/utils/admin-auth'
|
import { setAdminSession } from '@/utils/admin-auth'
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ const password = ref('')
|
|||||||
|
|
||||||
async function submitLogin() {
|
async function submitLogin() {
|
||||||
if (!username.value.trim() || !password.value.trim()) {
|
if (!username.value.trim() || !password.value.trim()) {
|
||||||
ElMessage.error('请输入账号和密码')
|
showError('请输入账号和密码')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,10 +26,10 @@ async function submitLogin() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
setAdminSession(response.data.token, response.data.expiresAt, response.data.user)
|
setAdminSession(response.data.token, response.data.expiresAt, response.data.user)
|
||||||
ElMessage.success('登录成功')
|
showSuccess('登录成功')
|
||||||
await router.replace('/admin/dashboard')
|
await router.replace('/admin/dashboard')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '后台登录失败')
|
showError(error instanceof Error ? error.message : '后台登录失败')
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
|
import { showError, showSuccess } from '@/lib/feedback'
|
||||||
import { fetchAdminAgisoShopConfigs, saveAdminAgisoShopConfigs } from '@/services/admin'
|
import { fetchAdminAgisoShopConfigs, saveAdminAgisoShopConfigs } from '@/services/admin'
|
||||||
import type { AdminAgisoObservedShopItem, AdminAgisoShopConfigItem } from '@/types/admin'
|
import type { AdminAgisoObservedShopItem, AdminAgisoShopConfigItem } from '@/types/admin'
|
||||||
import { hasAdminRole } from '@/utils/admin-auth'
|
import { hasAdminRole } from '@/utils/admin-auth'
|
||||||
@@ -102,10 +102,10 @@ async function saveConfigs() {
|
|||||||
const response = await saveAdminAgisoShopConfigs({ shops: payload })
|
const response = await saveAdminAgisoShopConfigs({ shops: payload })
|
||||||
filePath.value = response.data.filePath
|
filePath.value = response.data.filePath
|
||||||
shops.value = response.data.shops.map(mapEditableShop)
|
shops.value = response.data.shops.map(mapEditableShop)
|
||||||
ElMessage.success('店铺配置已保存')
|
showSuccess('店铺配置已保存')
|
||||||
await loadConfigs()
|
await loadConfigs()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '保存店铺配置失败')
|
showError(error instanceof Error ? error.message : '保存店铺配置失败')
|
||||||
} finally {
|
} finally {
|
||||||
saving.value = false
|
saving.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
||||||
|
import { showConfirm, showError, showSuccess } from '@/lib/feedback'
|
||||||
import {
|
import {
|
||||||
closeAdminTask,
|
closeAdminTask,
|
||||||
completeAdminTaskManualDispatch,
|
completeAdminTaskManualDispatch,
|
||||||
@@ -54,7 +54,7 @@ async function runAction(
|
|||||||
confirmText: string,
|
confirmText: string,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(confirmText, '确认操作', {
|
await showConfirm(confirmText, '确认操作', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: '继续执行',
|
confirmButtonText: '继续执行',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
@@ -70,10 +70,10 @@ async function runAction(
|
|||||||
if (response.data.claimUrl) {
|
if (response.data.claimUrl) {
|
||||||
lastClaimUrl.value = response.data.claimUrl
|
lastClaimUrl.value = response.data.claimUrl
|
||||||
}
|
}
|
||||||
ElMessage.success(successMessage)
|
showSuccess(successMessage)
|
||||||
await loadDetail()
|
await loadDetail()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '操作失败')
|
showError(error instanceof Error ? error.message : '操作失败')
|
||||||
} finally {
|
} finally {
|
||||||
actionLoading.value = false
|
actionLoading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
import AdminPaginationBar from '@/components/admin/AdminPaginationBar.vue'
|
import AdminPaginationBar from '@/components/admin/AdminPaginationBar.vue'
|
||||||
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
||||||
|
import { showConfirm, showError, showSuccess } from '@/lib/feedback'
|
||||||
import {
|
import {
|
||||||
closeAdminTask,
|
closeAdminTask,
|
||||||
fetchAdminTasks,
|
fetchAdminTasks,
|
||||||
@@ -102,7 +102,7 @@ async function runAction(
|
|||||||
confirmText: string,
|
confirmText: string,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(confirmText, '确认操作', {
|
await showConfirm(confirmText, '确认操作', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: '继续执行',
|
confirmButtonText: '继续执行',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
@@ -118,11 +118,11 @@ async function runAction(
|
|||||||
if (response.data.claimUrl) {
|
if (response.data.claimUrl) {
|
||||||
lastClaimUrl.value = response.data.claimUrl
|
lastClaimUrl.value = response.data.claimUrl
|
||||||
}
|
}
|
||||||
ElMessage.success(successMessage)
|
showSuccess(successMessage)
|
||||||
closeActionMenu()
|
closeActionMenu()
|
||||||
await loadTasks()
|
await loadTasks()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '操作失败')
|
showError(error instanceof Error ? error.message : '操作失败')
|
||||||
} finally {
|
} finally {
|
||||||
actionLoadingId.value = null
|
actionLoadingId.value = null
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ function handleActionCommand(command: `${AdminTaskActionKey}:${number}`) {
|
|||||||
const item = items.value.find((entry) => entry.taskId === taskId)
|
const item = items.value.find((entry) => entry.taskId === taskId)
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
ElMessage.error('未找到对应任务')
|
showError('未找到对应任务')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
import AdminPaginationBar from '@/components/admin/AdminPaginationBar.vue'
|
import AdminPaginationBar from '@/components/admin/AdminPaginationBar.vue'
|
||||||
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
||||||
|
import { isFeedbackDismissed, showConfirm, showError, showPrompt, showSuccess } from '@/lib/feedback'
|
||||||
import {
|
import {
|
||||||
createAdminUser,
|
createAdminUser,
|
||||||
fetchAdminUsers,
|
fetchAdminUsers,
|
||||||
@@ -65,7 +65,7 @@ async function loadUsers(page = pagination.value.page) {
|
|||||||
|
|
||||||
async function submitCreate() {
|
async function submitCreate() {
|
||||||
if (!createUsername.value.trim() || !createPassword.value.trim()) {
|
if (!createUsername.value.trim() || !createPassword.value.trim()) {
|
||||||
ElMessage.error('请填写账号和密码')
|
showError('请填写账号和密码')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,13 +78,13 @@ async function submitCreate() {
|
|||||||
role: createRole.value,
|
role: createRole.value,
|
||||||
status: 'active',
|
status: 'active',
|
||||||
})
|
})
|
||||||
ElMessage.success('后台用户已创建')
|
showSuccess('后台用户已创建')
|
||||||
createUsername.value = ''
|
createUsername.value = ''
|
||||||
createPassword.value = ''
|
createPassword.value = ''
|
||||||
createRole.value = 'operator'
|
createRole.value = 'operator'
|
||||||
await loadUsers(1)
|
await loadUsers(1)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '创建后台用户失败')
|
showError(error instanceof Error ? error.message : '创建后台用户失败')
|
||||||
} finally {
|
} finally {
|
||||||
creating.value = false
|
creating.value = false
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ async function toggleRole(item: AdminUserListItem) {
|
|||||||
const nextRole: AdminRole = item.role === 'admin' ? 'operator' : 'admin'
|
const nextRole: AdminRole = item.role === 'admin' ? 'operator' : 'admin'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确认将 ${item.username} 调整为${nextRole === 'admin' ? '管理员' : '普通运营'}吗?`, '确认操作', {
|
await showConfirm(`确认将 ${item.username} 调整为${nextRole === 'admin' ? '管理员' : '普通运营'}吗?`, '确认操作', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: '继续执行',
|
confirmButtonText: '继续执行',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
@@ -107,10 +107,10 @@ async function toggleRole(item: AdminUserListItem) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await updateAdminUserRole(item.userId, { role: nextRole })
|
await updateAdminUserRole(item.userId, { role: nextRole })
|
||||||
ElMessage.success('用户角色已更新')
|
showSuccess('用户角色已更新')
|
||||||
await loadUsers()
|
await loadUsers()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '更新用户角色失败')
|
showError(error instanceof Error ? error.message : '更新用户角色失败')
|
||||||
} finally {
|
} finally {
|
||||||
actionLoadingId.value = null
|
actionLoadingId.value = null
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ async function toggleStatus(item: AdminUserListItem) {
|
|||||||
const nextStatus = item.status === 'active' ? 'disabled' : 'active'
|
const nextStatus = item.status === 'active' ? 'disabled' : 'active'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确认将 ${item.username}${nextStatus === 'active' ? '启用' : '停用'}吗?`, '确认操作', {
|
await showConfirm(`确认将 ${item.username}${nextStatus === 'active' ? '启用' : '停用'}吗?`, '确认操作', {
|
||||||
type: nextStatus === 'active' ? 'info' : 'warning',
|
type: nextStatus === 'active' ? 'info' : 'warning',
|
||||||
confirmButtonText: '继续执行',
|
confirmButtonText: '继续执行',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
@@ -133,10 +133,10 @@ async function toggleStatus(item: AdminUserListItem) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await updateAdminUserStatus(item.userId, { status: nextStatus })
|
await updateAdminUserStatus(item.userId, { status: nextStatus })
|
||||||
ElMessage.success('用户状态已更新')
|
showSuccess('用户状态已更新')
|
||||||
await loadUsers()
|
await loadUsers()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '更新用户状态失败')
|
showError(error instanceof Error ? error.message : '更新用户状态失败')
|
||||||
} finally {
|
} finally {
|
||||||
actionLoadingId.value = null
|
actionLoadingId.value = null
|
||||||
}
|
}
|
||||||
@@ -144,7 +144,7 @@ async function toggleStatus(item: AdminUserListItem) {
|
|||||||
|
|
||||||
async function promptResetPassword(item: AdminUserListItem) {
|
async function promptResetPassword(item: AdminUserListItem) {
|
||||||
try {
|
try {
|
||||||
const result = await ElMessageBox.prompt(`请输入 ${item.username} 的新密码`, '重置密码', {
|
const result = await showPrompt(`请输入 ${item.username} 的新密码`, '重置密码', {
|
||||||
inputType: 'password',
|
inputType: 'password',
|
||||||
inputPlaceholder: '至少 8 位',
|
inputPlaceholder: '至少 8 位',
|
||||||
confirmButtonText: '提交',
|
confirmButtonText: '提交',
|
||||||
@@ -154,13 +154,13 @@ async function promptResetPassword(item: AdminUserListItem) {
|
|||||||
|
|
||||||
actionLoadingId.value = item.userId
|
actionLoadingId.value = item.userId
|
||||||
await resetAdminUserPassword(item.userId, { password: result.value.trim() })
|
await resetAdminUserPassword(item.userId, { password: result.value.trim() })
|
||||||
ElMessage.success('用户密码已重置')
|
showSuccess('用户密码已重置')
|
||||||
await loadUsers()
|
await loadUsers()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error === 'cancel' || error === 'close') {
|
if (isFeedbackDismissed(error)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ElMessage.error(error instanceof Error ? error.message : '重置密码失败')
|
showError(error instanceof Error ? error.message : '重置密码失败')
|
||||||
} finally {
|
} finally {
|
||||||
actionLoadingId.value = null
|
actionLoadingId.value = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
|
import { showConfirm, showError, showSuccess } from '@/lib/feedback'
|
||||||
import { fetchAdminWebhookEventDetail, replayAdminWebhookEvent } from '@/services/admin'
|
import { fetchAdminWebhookEventDetail, replayAdminWebhookEvent } from '@/services/admin'
|
||||||
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
||||||
import type { AdminWebhookEventDetail, AdminWebhookReplayResponse } from '@/types/admin'
|
import type { AdminWebhookEventDetail, AdminWebhookReplayResponse } from '@/types/admin'
|
||||||
@@ -38,7 +38,7 @@ async function submitReplay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(
|
await showConfirm(
|
||||||
`确认重放 webhook #${detail.value.eventId} 吗?这会重新推进订单与任务处理。`,
|
`确认重放 webhook #${detail.value.eventId} 吗?这会重新推进订单与任务处理。`,
|
||||||
'确认重放',
|
'确认重放',
|
||||||
{
|
{
|
||||||
@@ -56,10 +56,10 @@ async function submitReplay() {
|
|||||||
try {
|
try {
|
||||||
const response = await replayAdminWebhookEvent(detail.value.eventId)
|
const response = await replayAdminWebhookEvent(detail.value.eventId)
|
||||||
lastReplayResult.value = response.data.result
|
lastReplayResult.value = response.data.result
|
||||||
ElMessage.success(`Webhook 已重放,生成/更新任务 ${response.data.result.taskCount} 条`)
|
showSuccess(`Webhook 已重放,生成/更新任务 ${response.data.result.taskCount} 条`)
|
||||||
await loadDetail()
|
await loadDetail()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '重放 webhook 失败')
|
showError(error instanceof Error ? error.message : '重放 webhook 失败')
|
||||||
} finally {
|
} finally {
|
||||||
replayLoading.value = false
|
replayLoading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
import AdminPaginationBar from '@/components/admin/AdminPaginationBar.vue'
|
import AdminPaginationBar from '@/components/admin/AdminPaginationBar.vue'
|
||||||
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
|
||||||
|
import { showConfirm, showError, showSuccess } from '@/lib/feedback'
|
||||||
import { fetchAdminWebhookEvents, replayAdminWebhookEvent } from '@/services/admin'
|
import { fetchAdminWebhookEvents, replayAdminWebhookEvent } from '@/services/admin'
|
||||||
import type { AdminPagination, AdminWebhookEventListItem } from '@/types/admin'
|
import type { AdminPagination, AdminWebhookEventListItem } from '@/types/admin'
|
||||||
import { hasAdminRole } from '@/utils/admin-auth'
|
import { hasAdminRole } from '@/utils/admin-auth'
|
||||||
@@ -53,7 +53,7 @@ async function loadEvents(page = pagination.value.page) {
|
|||||||
|
|
||||||
async function submitReplay(item: AdminWebhookEventListItem) {
|
async function submitReplay(item: AdminWebhookEventListItem) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(
|
await showConfirm(
|
||||||
`确认重放 webhook #${item.eventId} 吗?`,
|
`确认重放 webhook #${item.eventId} 吗?`,
|
||||||
'确认重放',
|
'确认重放',
|
||||||
{
|
{
|
||||||
@@ -70,10 +70,10 @@ async function submitReplay(item: AdminWebhookEventListItem) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await replayAdminWebhookEvent(item.eventId)
|
await replayAdminWebhookEvent(item.eventId)
|
||||||
ElMessage.success('Webhook 已重放')
|
showSuccess('Webhook 已重放')
|
||||||
await loadEvents()
|
await loadEvents()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(error instanceof Error ? error.message : '重放 webhook 失败')
|
showError(error instanceof Error ? error.message : '重放 webhook 失败')
|
||||||
} finally {
|
} finally {
|
||||||
actionLoadingId.value = null
|
actionLoadingId.value = null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user