feat(usage): 记录并展示失败请求(用户端+管理端)

- 记录失败请求并在用户端/管理端展示;分类下拉改用统一 Select 组件
- 模型过滤改后端 ILIKE 模糊匹配;新增「Key 名称」列(含已删除标记)与按 Key 过滤;时间列移至末列
- 用户可见「已删除 key 失败请求」:OpsErrorLogFilter 加 MatchDeletedKeyOwner,用户侧归属
  放宽为 (user_id OR deleted_key_owner_user_id),让 key 原所有者能看到删除 key 后继续请求
  导致的认证失败记录(他人仍 NotFound,不泄露存在性)
- 迁移 148:ops_error_logs 用户+时间索引

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DaydreamCoding
2026-06-05 14:00:57 +08:00
committed by QTom
co-authored by Claude Opus 4.8
parent ddf063352a
commit cfb195c7b2
37 changed files with 1746 additions and 84 deletions
+30
View File
@@ -22,6 +22,18 @@
{{ selectedLabel }}
</slot>
</span>
<span
v-if="clearable && hasValue && !disabled"
class="select-clear"
role="button"
tabindex="-1"
aria-label="Clear selection"
@click.stop="clearSelection"
@mousedown.stop
@keydown.enter.stop.prevent="clearSelection"
>
<Icon name="x" size="sm" />
</span>
<span class="select-icon">
<Icon
name="chevronDown"
@@ -135,6 +147,7 @@ interface Props {
labelKey?: string
creatable?: boolean
creatablePrefix?: string
clearable?: boolean
}
interface Emits {
@@ -148,6 +161,7 @@ const props = withDefaults(defineProps<Props>(), {
searchable: 'auto',
creatable: false,
creatablePrefix: '',
clearable: false,
valueKey: 'value',
labelKey: 'label'
})
@@ -239,6 +253,10 @@ const selectedLabel = computed(() => {
return placeholderText.value
})
const hasValue = computed(
() => props.modelValue !== null && props.modelValue !== undefined && props.modelValue !== ''
)
const filteredOptions = computed(() => {
let opts = props.options as any[]
if (isSearchable.value && searchQuery.value) {
@@ -355,6 +373,12 @@ const selectOption = (option: any) => {
triggerRef.value?.focus()
}
const clearSelection = () => {
if (props.disabled) return
emit('update:modelValue', null)
emit('change', null, null)
}
// Keyboards
const onTriggerKeyDown = () => {
if (!isOpen.value) {
@@ -461,6 +485,12 @@ onUnmounted(() => {
.select-icon {
@apply flex-shrink-0 text-gray-400 dark:text-dark-400;
}
.select-clear {
@apply flex flex-shrink-0 cursor-pointer items-center justify-center;
@apply rounded text-gray-400 transition-colors;
@apply hover:text-gray-600 dark:hover:text-gray-200;
}
</style>
<style>