fix: 修复授权管理API路径和页面布局

- POST /dev/agent-apps → POST /dev/agent-apps/authorize
- PUT /dev/agent-apps/:id → PUT /dev/agent-apps/:id/card-types
- 参考版本管理页面优化布局样式
- 编辑页面禁用代理和应用选择(不可修改)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-09 19:36:42 +08:00
parent aa5d3d126b
commit 87ba499f14
2 changed files with 124 additions and 148 deletions
+58 -80
View File
@@ -37,7 +37,7 @@ const agents = ref<Agent[]>([])
const applications = ref<Application[]>([])
const cardTypes = ref<CardType[]>([])
const form = ref({
const formData = ref({
agent_id: '',
application_id: '',
card_type_id: '',
@@ -46,22 +46,22 @@ const form = ref({
})
const selectedAgent = computed(() => {
if (form.value.agent_id) {
return agents.value.find(a => String(a.id) === form.value.agent_id)
if (formData.value.agent_id) {
return agents.value.find(a => String(a.id) === formData.value.agent_id)
}
return null
})
const selectedApplication = computed(() => {
if (form.value.application_id) {
return applications.value.find(app => String(app.id) === form.value.application_id)
if (formData.value.application_id) {
return applications.value.find(app => String(app.id) === formData.value.application_id)
}
return null
})
const selectedCardType = computed(() => {
if (form.value.card_type_id) {
return cardTypes.value.find(ct => String(ct.id) === form.value.card_type_id)
if (formData.value.card_type_id) {
return cardTypes.value.find(ct => String(ct.id) === formData.value.card_type_id)
}
return null
})
@@ -107,17 +107,16 @@ async function fetchAgentApp() {
const agentApp = data?.agent_app || data
if (agentApp) {
form.value.agent_id = String(agentApp.agent_id)
form.value.application_id = String(agentApp.application_id)
form.value.remark = agentApp.remark || ''
formData.value.agent_id = String(agentApp.agent_id)
formData.value.application_id = String(agentApp.application_id)
await fetchCardTypes(String(agentApp.application_id))
// 取第一个卡类权限
if (agentApp.card_types && agentApp.card_types.length > 0) {
const firstCardType = agentApp.card_types[0]
form.value.card_type_id = String(firstCardType.card_type_id)
form.value.price = firstCardType.price || 0
formData.value.card_type_id = String(firstCardType.card_type_id)
formData.value.price = firstCardType.price || 0
}
}
}
@@ -132,38 +131,34 @@ async function fetchAgentApp() {
watch(selectedCardType, (ct) => {
if (ct && !loading.value) {
form.value.price = ct.price
formData.value.price = ct.price
}
})
async function handleSave() {
if (!form.value.agent_id) {
async function handleSubmit() {
if (!formData.value.agent_id) {
toast.error(t('admin.agentApps.createForm.selectAgentRequired'))
return
}
if (!form.value.application_id) {
if (!formData.value.application_id) {
toast.error(t('admin.agentApps.createForm.selectAppRequired'))
return
}
if (!form.value.card_type_id) {
if (!formData.value.card_type_id) {
toast.error(t('admin.agentApps.createForm.selectCardRequired'))
return
}
saving.value = true
try {
await api.put(`/dev/agent-apps/${agentAppId.value}`, {
agent_id: Number(form.value.agent_id),
application_id: Number(form.value.application_id),
await api.put(`/dev/agent-apps/${agentAppId.value}/card-types`, {
card_types: [{
card_type_id: Number(form.value.card_type_id),
card_type_id: Number(formData.value.card_type_id),
can_generate: true,
price: Number(form.value.price),
price: Number(formData.value.price),
}],
discount: 0,
remark: form.value.remark,
})
toast.success(t('admin.agentApps.createForm.updateSuccess'))
router.push('/admin/agent-apps')
@@ -209,48 +204,46 @@ onMounted(() => {
<UiCardDescription>{{ t('admin.agentApps.createForm.authConfigDesc') }}</UiCardDescription>
</UiCardHeader>
<UiCardContent class="space-y-6">
<div class="grid gap-4 sm:grid-cols-2">
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectAgent') }} <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="form.agent_id" :disabled="saving">
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAgentPlaceholder')" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="agent in agents"
:key="agent.id"
:value="String(agent.id)"
>
{{ agent.username }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectAgent') }}</UiLabel>
<UiSelect v-model="formData.agent_id" disabled>
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAgentPlaceholder')" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="agent in agents"
:key="agent.id"
:value="String(agent.id)"
>
{{ agent.username }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectApp') }} <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="form.application_id" :disabled="saving" @update:model-value="fetchCardTypes($event)">
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAppPlaceholder')" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="app in applications"
:key="app.id"
:value="String(app.id)"
>
{{ app.name }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectApp') }}</UiLabel>
<UiSelect v-model="formData.application_id" disabled>
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAppPlaceholder')" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="app in applications"
:key="app.id"
:value="String(app.id)"
>
{{ app.name }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="grid gap-4 sm:grid-cols-2">
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectCardType') }} <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="form.card_type_id" :disabled="!form.application_id || saving">
<UiSelect v-model="formData.card_type_id" :disabled="saving">
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectCardTypePlaceholder')" />
</UiSelectTrigger>
@@ -268,7 +261,7 @@ onMounted(() => {
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">¥</span>
<UiInput
id="price"
v-model.number="form.price"
v-model.number="formData.price"
type="number"
class="pl-7"
:placeholder="t('admin.agentApps.createForm.pricePlaceholder')"
@@ -277,17 +270,6 @@ onMounted(() => {
</div>
</div>
</div>
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.remark') }}</UiLabel>
<textarea
v-model="form.remark"
class="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
:placeholder="t('admin.agentApps.createForm.remarkPlaceholder')"
:disabled="saving"
rows="2"
/>
</div>
</UiCardContent>
</UiCard>
</div>
@@ -316,11 +298,7 @@ onMounted(() => {
</div>
<div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('admin.agentApps.createForm.previewPrice') }}</span>
<span>¥{{ form.price || 0 }}</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('admin.agentApps.createForm.previewRemark') }}</span>
<span class="truncate max-w-[120px]">{{ form.remark || '-' }}</span>
<span>¥{{ formData.price || 0 }}</span>
</div>
</div>
</UiCardContent>
@@ -332,8 +310,8 @@ onMounted(() => {
<UiButton
class="w-full"
size="lg"
:disabled="saving || !form.agent_id || !form.application_id || !form.card_type_id"
@click="handleSave"
:disabled="saving || !formData.card_type_id"
@click="handleSubmit"
>
<Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" />
<Check v-else class="mr-2 h-4 w-4" />
@@ -353,4 +331,4 @@ onMounted(() => {
</div>
</div>
</BasicPage>
</template>
</template>
+66 -68
View File
@@ -34,7 +34,7 @@ const agents = ref<Agent[]>([])
const applications = ref<Application[]>([])
const cardTypes = ref<CardType[]>([])
const form = ref({
const formData = ref({
agent_id: '',
application_id: '',
card_type_id: '',
@@ -43,22 +43,22 @@ const form = ref({
})
const selectedAgent = computed(() => {
if (form.value.agent_id) {
return agents.value.find(a => String(a.id) === form.value.agent_id)
if (formData.value.agent_id) {
return agents.value.find(a => String(a.id) === formData.value.agent_id)
}
return null
})
const selectedApplication = computed(() => {
if (form.value.application_id) {
return applications.value.find(app => String(app.id) === form.value.application_id)
if (formData.value.application_id) {
return applications.value.find(app => String(app.id) === formData.value.application_id)
}
return null
})
const selectedCardType = computed(() => {
if (form.value.card_type_id) {
return cardTypes.value.find(ct => String(ct.id) === form.value.card_type_id)
if (formData.value.card_type_id) {
return cardTypes.value.find(ct => String(ct.id) === formData.value.card_type_id)
}
return null
})
@@ -90,57 +90,56 @@ async function fetchApplications() {
async function fetchCardTypes(appId: string) {
if (!appId) {
cardTypes.value = []
form.value.card_type_id = ''
form.value.price = 0
formData.value.card_type_id = ''
formData.value.price = 0
return
}
try {
const data = await api.get<{ card_types: CardType[] }>(`/dev/card-types?application_id=${appId}`)
cardTypes.value = data?.card_types || []
form.value.card_type_id = ''
form.value.price = 0
formData.value.card_type_id = ''
formData.value.price = 0
}
catch {
cardTypes.value = []
form.value.card_type_id = ''
form.value.price = 0
formData.value.card_type_id = ''
formData.value.price = 0
}
}
watch(selectedCardType, (ct) => {
if (ct) {
form.value.price = ct.price
formData.value.price = ct.price
}
})
async function handleSave() {
if (!form.value.agent_id) {
async function handleSubmit() {
if (!formData.value.agent_id) {
toast.error(t('admin.agentApps.createForm.selectAgentRequired'))
return
}
if (!form.value.application_id) {
if (!formData.value.application_id) {
toast.error(t('admin.agentApps.createForm.selectAppRequired'))
return
}
if (!form.value.card_type_id) {
if (!formData.value.card_type_id) {
toast.error(t('admin.agentApps.createForm.selectCardRequired'))
return
}
saving.value = true
try {
await api.post('/dev/agent-apps', {
agent_id: Number(form.value.agent_id),
application_id: Number(form.value.application_id),
await api.post('/dev/agent-apps/authorize', {
agent_id: Number(formData.value.agent_id),
application_id: Number(formData.value.application_id),
discount: 1.0,
card_types: [{
card_type_id: Number(form.value.card_type_id),
card_type_id: Number(formData.value.card_type_id),
can_generate: true,
price: Number(form.value.price),
price: Number(formData.value.price),
}],
discount: 0,
remark: form.value.remark,
})
toast.success(t('admin.agentApps.createForm.createSuccess'))
router.push('/admin/agent-apps')
@@ -181,48 +180,46 @@ onMounted(() => {
<UiCardDescription>{{ t('admin.agentApps.createForm.authConfigDesc') }}</UiCardDescription>
</UiCardHeader>
<UiCardContent class="space-y-6">
<div class="grid gap-4 sm:grid-cols-2">
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectAgent') }} <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="form.agent_id" :disabled="saving || loading">
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAgentPlaceholder')" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="agent in agents"
:key="agent.id"
:value="String(agent.id)"
>
{{ agent.username }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectAgent') }} <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="formData.agent_id" :disabled="saving || loading">
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAgentPlaceholder')" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="agent in agents"
:key="agent.id"
:value="String(agent.id)"
>
{{ agent.username }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectApp') }} <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="form.application_id" :disabled="saving || loading" @update:model-value="fetchCardTypes($event)">
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAppPlaceholder')" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="app in applications"
:key="app.id"
:value="String(app.id)"
>
{{ app.name }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectApp') }} <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="formData.application_id" :disabled="saving || loading" @update:model-value="fetchCardTypes($event)">
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAppPlaceholder')" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="app in applications"
:key="app.id"
:value="String(app.id)"
>
{{ app.name }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="grid gap-4 sm:grid-cols-2">
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectCardType') }} <span class="text-destructive">*</span></UiLabel>
<UiSelect v-model="form.card_type_id" :disabled="!form.application_id || saving">
<UiSelect v-model="formData.card_type_id" :disabled="!formData.application_id || saving">
<UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectCardTypePlaceholder')" />
</UiSelectTrigger>
@@ -240,7 +237,7 @@ onMounted(() => {
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">¥</span>
<UiInput
id="price"
v-model.number="form.price"
v-model.number="formData.price"
type="number"
class="pl-7"
:placeholder="t('admin.agentApps.createForm.pricePlaceholder')"
@@ -251,9 +248,10 @@ onMounted(() => {
</div>
<div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.remark') }}</UiLabel>
<UiLabel for="remark">{{ t('admin.agentApps.createForm.remark') }}</UiLabel>
<textarea
v-model="form.remark"
id="remark"
v-model="formData.remark"
class="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
:placeholder="t('admin.agentApps.createForm.remarkPlaceholder')"
:disabled="saving"
@@ -288,11 +286,11 @@ onMounted(() => {
</div>
<div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('admin.agentApps.createForm.previewPrice') }}</span>
<span>¥{{ form.price || 0 }}</span>
<span>¥{{ formData.price || 0 }}</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('admin.agentApps.createForm.previewRemark') }}</span>
<span class="truncate max-w-[120px]">{{ form.remark || '-' }}</span>
<span class="truncate max-w-[120px]">{{ formData.remark || '-' }}</span>
</div>
</div>
</UiCardContent>
@@ -304,8 +302,8 @@ onMounted(() => {
<UiButton
class="w-full"
size="lg"
:disabled="saving || !form.agent_id || !form.application_id || !form.card_type_id"
@click="handleSave"
:disabled="saving || !formData.agent_id || !formData.application_id || !formData.card_type_id"
@click="handleSubmit"
>
<Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" />
<Rocket v-else class="mr-2 h-4 w-4" />