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
+57 -79
View File
@@ -37,7 +37,7 @@ const agents = ref<Agent[]>([])
const applications = ref<Application[]>([]) const applications = ref<Application[]>([])
const cardTypes = ref<CardType[]>([]) const cardTypes = ref<CardType[]>([])
const form = ref({ const formData = ref({
agent_id: '', agent_id: '',
application_id: '', application_id: '',
card_type_id: '', card_type_id: '',
@@ -46,22 +46,22 @@ const form = ref({
}) })
const selectedAgent = computed(() => { const selectedAgent = computed(() => {
if (form.value.agent_id) { if (formData.value.agent_id) {
return agents.value.find(a => String(a.id) === form.value.agent_id) return agents.value.find(a => String(a.id) === formData.value.agent_id)
} }
return null return null
}) })
const selectedApplication = computed(() => { const selectedApplication = computed(() => {
if (form.value.application_id) { if (formData.value.application_id) {
return applications.value.find(app => String(app.id) === form.value.application_id) return applications.value.find(app => String(app.id) === formData.value.application_id)
} }
return null return null
}) })
const selectedCardType = computed(() => { const selectedCardType = computed(() => {
if (form.value.card_type_id) { if (formData.value.card_type_id) {
return cardTypes.value.find(ct => String(ct.id) === form.value.card_type_id) return cardTypes.value.find(ct => String(ct.id) === formData.value.card_type_id)
} }
return null return null
}) })
@@ -107,17 +107,16 @@ async function fetchAgentApp() {
const agentApp = data?.agent_app || data const agentApp = data?.agent_app || data
if (agentApp) { if (agentApp) {
form.value.agent_id = String(agentApp.agent_id) formData.value.agent_id = String(agentApp.agent_id)
form.value.application_id = String(agentApp.application_id) formData.value.application_id = String(agentApp.application_id)
form.value.remark = agentApp.remark || ''
await fetchCardTypes(String(agentApp.application_id)) await fetchCardTypes(String(agentApp.application_id))
// 取第一个卡类权限 // 取第一个卡类权限
if (agentApp.card_types && agentApp.card_types.length > 0) { if (agentApp.card_types && agentApp.card_types.length > 0) {
const firstCardType = agentApp.card_types[0] const firstCardType = agentApp.card_types[0]
form.value.card_type_id = String(firstCardType.card_type_id) formData.value.card_type_id = String(firstCardType.card_type_id)
form.value.price = firstCardType.price || 0 formData.value.price = firstCardType.price || 0
} }
} }
} }
@@ -132,38 +131,34 @@ async function fetchAgentApp() {
watch(selectedCardType, (ct) => { watch(selectedCardType, (ct) => {
if (ct && !loading.value) { if (ct && !loading.value) {
form.value.price = ct.price formData.value.price = ct.price
} }
}) })
async function handleSave() { async function handleSubmit() {
if (!form.value.agent_id) { if (!formData.value.agent_id) {
toast.error(t('admin.agentApps.createForm.selectAgentRequired')) toast.error(t('admin.agentApps.createForm.selectAgentRequired'))
return return
} }
if (!form.value.application_id) { if (!formData.value.application_id) {
toast.error(t('admin.agentApps.createForm.selectAppRequired')) toast.error(t('admin.agentApps.createForm.selectAppRequired'))
return return
} }
if (!form.value.card_type_id) { if (!formData.value.card_type_id) {
toast.error(t('admin.agentApps.createForm.selectCardRequired')) toast.error(t('admin.agentApps.createForm.selectCardRequired'))
return return
} }
saving.value = true saving.value = true
try { try {
await api.put(`/dev/agent-apps/${agentAppId.value}`, { await api.put(`/dev/agent-apps/${agentAppId.value}/card-types`, {
agent_id: Number(form.value.agent_id),
application_id: Number(form.value.application_id),
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, 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')) toast.success(t('admin.agentApps.createForm.updateSuccess'))
router.push('/admin/agent-apps') router.push('/admin/agent-apps')
@@ -209,48 +204,46 @@ onMounted(() => {
<UiCardDescription>{{ t('admin.agentApps.createForm.authConfigDesc') }}</UiCardDescription> <UiCardDescription>{{ t('admin.agentApps.createForm.authConfigDesc') }}</UiCardDescription>
</UiCardHeader> </UiCardHeader>
<UiCardContent class="space-y-6"> <UiCardContent class="space-y-6">
<div class="grid gap-4 sm:grid-cols-2"> <div class="space-y-2">
<div class="space-y-2"> <UiLabel>{{ t('admin.agentApps.createForm.selectAgent') }}</UiLabel>
<UiLabel>{{ t('admin.agentApps.createForm.selectAgent') }} <span class="text-destructive">*</span></UiLabel> <UiSelect v-model="formData.agent_id" disabled>
<UiSelect v-model="form.agent_id" :disabled="saving"> <UiSelectTrigger>
<UiSelectTrigger> <UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAgentPlaceholder')" />
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAgentPlaceholder')" /> </UiSelectTrigger>
</UiSelectTrigger> <UiSelectContent>
<UiSelectContent> <UiSelectItem
<UiSelectItem v-for="agent in agents"
v-for="agent in agents" :key="agent.id"
:key="agent.id" :value="String(agent.id)"
:value="String(agent.id)" >
> {{ agent.username }}
{{ agent.username }} </UiSelectItem>
</UiSelectItem> </UiSelectContent>
</UiSelectContent> </UiSelect>
</UiSelect> </div>
</div>
<div class="space-y-2"> <div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectApp') }} <span class="text-destructive">*</span></UiLabel> <UiLabel>{{ t('admin.agentApps.createForm.selectApp') }}</UiLabel>
<UiSelect v-model="form.application_id" :disabled="saving" @update:model-value="fetchCardTypes($event)"> <UiSelect v-model="formData.application_id" disabled>
<UiSelectTrigger> <UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAppPlaceholder')" /> <UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAppPlaceholder')" />
</UiSelectTrigger> </UiSelectTrigger>
<UiSelectContent> <UiSelectContent>
<UiSelectItem <UiSelectItem
v-for="app in applications" v-for="app in applications"
:key="app.id" :key="app.id"
:value="String(app.id)" :value="String(app.id)"
> >
{{ app.name }} {{ app.name }}
</UiSelectItem> </UiSelectItem>
</UiSelectContent> </UiSelectContent>
</UiSelect> </UiSelect>
</div>
</div> </div>
<div class="grid gap-4 sm:grid-cols-2"> <div class="grid gap-4 sm:grid-cols-2">
<div class="space-y-2"> <div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectCardType') }} <span class="text-destructive">*</span></UiLabel> <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> <UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectCardTypePlaceholder')" /> <UiSelectValue :placeholder="t('admin.agentApps.createForm.selectCardTypePlaceholder')" />
</UiSelectTrigger> </UiSelectTrigger>
@@ -268,7 +261,7 @@ onMounted(() => {
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">¥</span> <span class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">¥</span>
<UiInput <UiInput
id="price" id="price"
v-model.number="form.price" v-model.number="formData.price"
type="number" type="number"
class="pl-7" class="pl-7"
:placeholder="t('admin.agentApps.createForm.pricePlaceholder')" :placeholder="t('admin.agentApps.createForm.pricePlaceholder')"
@@ -277,17 +270,6 @@ onMounted(() => {
</div> </div>
</div> </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> </UiCardContent>
</UiCard> </UiCard>
</div> </div>
@@ -316,11 +298,7 @@ onMounted(() => {
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('admin.agentApps.createForm.previewPrice') }}</span> <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>
</div> </div>
</div> </div>
</UiCardContent> </UiCardContent>
@@ -332,8 +310,8 @@ onMounted(() => {
<UiButton <UiButton
class="w-full" class="w-full"
size="lg" size="lg"
:disabled="saving || !form.agent_id || !form.application_id || !form.card_type_id" :disabled="saving || !formData.card_type_id"
@click="handleSave" @click="handleSubmit"
> >
<Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" /> <Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" />
<Check v-else class="mr-2 h-4 w-4" /> <Check v-else class="mr-2 h-4 w-4" />
+66 -68
View File
@@ -34,7 +34,7 @@ const agents = ref<Agent[]>([])
const applications = ref<Application[]>([]) const applications = ref<Application[]>([])
const cardTypes = ref<CardType[]>([]) const cardTypes = ref<CardType[]>([])
const form = ref({ const formData = ref({
agent_id: '', agent_id: '',
application_id: '', application_id: '',
card_type_id: '', card_type_id: '',
@@ -43,22 +43,22 @@ const form = ref({
}) })
const selectedAgent = computed(() => { const selectedAgent = computed(() => {
if (form.value.agent_id) { if (formData.value.agent_id) {
return agents.value.find(a => String(a.id) === form.value.agent_id) return agents.value.find(a => String(a.id) === formData.value.agent_id)
} }
return null return null
}) })
const selectedApplication = computed(() => { const selectedApplication = computed(() => {
if (form.value.application_id) { if (formData.value.application_id) {
return applications.value.find(app => String(app.id) === form.value.application_id) return applications.value.find(app => String(app.id) === formData.value.application_id)
} }
return null return null
}) })
const selectedCardType = computed(() => { const selectedCardType = computed(() => {
if (form.value.card_type_id) { if (formData.value.card_type_id) {
return cardTypes.value.find(ct => String(ct.id) === form.value.card_type_id) return cardTypes.value.find(ct => String(ct.id) === formData.value.card_type_id)
} }
return null return null
}) })
@@ -90,57 +90,56 @@ async function fetchApplications() {
async function fetchCardTypes(appId: string) { async function fetchCardTypes(appId: string) {
if (!appId) { if (!appId) {
cardTypes.value = [] cardTypes.value = []
form.value.card_type_id = '' formData.value.card_type_id = ''
form.value.price = 0 formData.value.price = 0
return return
} }
try { try {
const data = await api.get<{ card_types: CardType[] }>(`/dev/card-types?application_id=${appId}`) const data = await api.get<{ card_types: CardType[] }>(`/dev/card-types?application_id=${appId}`)
cardTypes.value = data?.card_types || [] cardTypes.value = data?.card_types || []
form.value.card_type_id = '' formData.value.card_type_id = ''
form.value.price = 0 formData.value.price = 0
} }
catch { catch {
cardTypes.value = [] cardTypes.value = []
form.value.card_type_id = '' formData.value.card_type_id = ''
form.value.price = 0 formData.value.price = 0
} }
} }
watch(selectedCardType, (ct) => { watch(selectedCardType, (ct) => {
if (ct) { if (ct) {
form.value.price = ct.price formData.value.price = ct.price
} }
}) })
async function handleSave() { async function handleSubmit() {
if (!form.value.agent_id) { if (!formData.value.agent_id) {
toast.error(t('admin.agentApps.createForm.selectAgentRequired')) toast.error(t('admin.agentApps.createForm.selectAgentRequired'))
return return
} }
if (!form.value.application_id) { if (!formData.value.application_id) {
toast.error(t('admin.agentApps.createForm.selectAppRequired')) toast.error(t('admin.agentApps.createForm.selectAppRequired'))
return return
} }
if (!form.value.card_type_id) { if (!formData.value.card_type_id) {
toast.error(t('admin.agentApps.createForm.selectCardRequired')) toast.error(t('admin.agentApps.createForm.selectCardRequired'))
return return
} }
saving.value = true saving.value = true
try { try {
await api.post('/dev/agent-apps', { await api.post('/dev/agent-apps/authorize', {
agent_id: Number(form.value.agent_id), agent_id: Number(formData.value.agent_id),
application_id: Number(form.value.application_id), application_id: Number(formData.value.application_id),
discount: 1.0,
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, 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')) toast.success(t('admin.agentApps.createForm.createSuccess'))
router.push('/admin/agent-apps') router.push('/admin/agent-apps')
@@ -181,48 +180,46 @@ onMounted(() => {
<UiCardDescription>{{ t('admin.agentApps.createForm.authConfigDesc') }}</UiCardDescription> <UiCardDescription>{{ t('admin.agentApps.createForm.authConfigDesc') }}</UiCardDescription>
</UiCardHeader> </UiCardHeader>
<UiCardContent class="space-y-6"> <UiCardContent class="space-y-6">
<div class="grid gap-4 sm:grid-cols-2"> <div class="space-y-2">
<div class="space-y-2"> <UiLabel>{{ t('admin.agentApps.createForm.selectAgent') }} <span class="text-destructive">*</span></UiLabel>
<UiLabel>{{ t('admin.agentApps.createForm.selectAgent') }} <span class="text-destructive">*</span></UiLabel> <UiSelect v-model="formData.agent_id" :disabled="saving || loading">
<UiSelect v-model="form.agent_id" :disabled="saving || loading"> <UiSelectTrigger>
<UiSelectTrigger> <UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAgentPlaceholder')" />
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAgentPlaceholder')" /> </UiSelectTrigger>
</UiSelectTrigger> <UiSelectContent>
<UiSelectContent> <UiSelectItem
<UiSelectItem v-for="agent in agents"
v-for="agent in agents" :key="agent.id"
:key="agent.id" :value="String(agent.id)"
:value="String(agent.id)" >
> {{ agent.username }}
{{ agent.username }} </UiSelectItem>
</UiSelectItem> </UiSelectContent>
</UiSelectContent> </UiSelect>
</UiSelect> </div>
</div>
<div class="space-y-2"> <div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectApp') }} <span class="text-destructive">*</span></UiLabel> <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)"> <UiSelect v-model="formData.application_id" :disabled="saving || loading" @update:model-value="fetchCardTypes($event)">
<UiSelectTrigger> <UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAppPlaceholder')" /> <UiSelectValue :placeholder="t('admin.agentApps.createForm.selectAppPlaceholder')" />
</UiSelectTrigger> </UiSelectTrigger>
<UiSelectContent> <UiSelectContent>
<UiSelectItem <UiSelectItem
v-for="app in applications" v-for="app in applications"
:key="app.id" :key="app.id"
:value="String(app.id)" :value="String(app.id)"
> >
{{ app.name }} {{ app.name }}
</UiSelectItem> </UiSelectItem>
</UiSelectContent> </UiSelectContent>
</UiSelect> </UiSelect>
</div>
</div> </div>
<div class="grid gap-4 sm:grid-cols-2"> <div class="grid gap-4 sm:grid-cols-2">
<div class="space-y-2"> <div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.selectCardType') }} <span class="text-destructive">*</span></UiLabel> <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> <UiSelectTrigger>
<UiSelectValue :placeholder="t('admin.agentApps.createForm.selectCardTypePlaceholder')" /> <UiSelectValue :placeholder="t('admin.agentApps.createForm.selectCardTypePlaceholder')" />
</UiSelectTrigger> </UiSelectTrigger>
@@ -240,7 +237,7 @@ onMounted(() => {
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">¥</span> <span class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">¥</span>
<UiInput <UiInput
id="price" id="price"
v-model.number="form.price" v-model.number="formData.price"
type="number" type="number"
class="pl-7" class="pl-7"
:placeholder="t('admin.agentApps.createForm.pricePlaceholder')" :placeholder="t('admin.agentApps.createForm.pricePlaceholder')"
@@ -251,9 +248,10 @@ onMounted(() => {
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<UiLabel>{{ t('admin.agentApps.createForm.remark') }}</UiLabel> <UiLabel for="remark">{{ t('admin.agentApps.createForm.remark') }}</UiLabel>
<textarea <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" 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')" :placeholder="t('admin.agentApps.createForm.remarkPlaceholder')"
:disabled="saving" :disabled="saving"
@@ -288,11 +286,11 @@ onMounted(() => {
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('admin.agentApps.createForm.previewPrice') }}</span> <span class="text-muted-foreground">{{ t('admin.agentApps.createForm.previewPrice') }}</span>
<span>¥{{ form.price || 0 }}</span> <span>¥{{ formData.price || 0 }}</span>
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('admin.agentApps.createForm.previewRemark') }}</span> <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>
</div> </div>
</UiCardContent> </UiCardContent>
@@ -304,8 +302,8 @@ onMounted(() => {
<UiButton <UiButton
class="w-full" class="w-full"
size="lg" size="lg"
:disabled="saving || !form.agent_id || !form.application_id || !form.card_type_id" :disabled="saving || !formData.agent_id || !formData.application_id || !formData.card_type_id"
@click="handleSave" @click="handleSubmit"
> >
<Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" /> <Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" />
<Rocket v-else class="mr-2 h-4 w-4" /> <Rocket v-else class="mr-2 h-4 w-4" />