fix: 修复授权管理页面价格显示和编辑问题
- 列表页面添加价格列显示 - 添加/编辑页面价格字段单独一行 - 使用shadcn官方Input组件 - 编辑页面价格修改后正确保存 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
import { BasicPage } from '@/components/global-layout'
|
import { BasicPage } from '@/components/global-layout'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
import api from '@/services/api'
|
import api from '@/services/api'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -42,7 +43,6 @@ const formData = ref({
|
|||||||
application_id: '',
|
application_id: '',
|
||||||
card_type_id: '',
|
card_type_id: '',
|
||||||
price: 0,
|
price: 0,
|
||||||
remark: '',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedAgent = computed(() => {
|
const selectedAgent = computed(() => {
|
||||||
@@ -129,23 +129,14 @@ async function fetchAgentApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 不自动更新价格,让用户手动输入
|
||||||
watch(selectedCardType, (ct) => {
|
watch(selectedCardType, (ct) => {
|
||||||
if (ct && !loading.value) {
|
if (ct && loading.value) {
|
||||||
formData.value.price = ct.price
|
formData.value.price = ct.price
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (!formData.value.agent_id) {
|
|
||||||
toast.error(t('admin.agentApps.createForm.selectAgentRequired'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!formData.value.application_id) {
|
|
||||||
toast.error(t('admin.agentApps.createForm.selectAppRequired'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!formData.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
|
||||||
@@ -240,34 +231,32 @@ onMounted(() => {
|
|||||||
</UiSelect>
|
</UiSelect>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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="formData.card_type_id" :disabled="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>
|
<UiSelectContent>
|
||||||
<UiSelectContent>
|
<UiSelectItem v-for="ct in cardTypes" :key="ct.id" :value="String(ct.id)">
|
||||||
<UiSelectItem v-for="ct in cardTypes" :key="ct.id" :value="String(ct.id)">
|
{{ ct.name }}<template v-if="ct.billing_type"> ({{ ct.billing_type }})</template>
|
||||||
{{ ct.name }}<template v-if="ct.billing_type"> ({{ ct.billing_type }})</template>
|
</UiSelectItem>
|
||||||
</UiSelectItem>
|
</UiSelectContent>
|
||||||
</UiSelectContent>
|
</UiSelect>
|
||||||
</UiSelect>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel for="price">{{ t('admin.agentApps.createForm.price') }} <span class="text-destructive">*</span></UiLabel>
|
<UiLabel for="price">{{ t('admin.agentApps.createForm.price') }} <span class="text-destructive">*</span></UiLabel>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<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
|
<Input
|
||||||
id="price"
|
id="price"
|
||||||
v-model.number="formData.price"
|
v-model="formData.price"
|
||||||
type="number"
|
type="number"
|
||||||
class="pl-7"
|
class="pl-7"
|
||||||
:placeholder="t('admin.agentApps.createForm.pricePlaceholder')"
|
:placeholder="t('admin.agentApps.createForm.pricePlaceholder')"
|
||||||
:disabled="saving"
|
:disabled="saving"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</UiCardContent>
|
</UiCardContent>
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ export function getColumns(actions: {
|
|||||||
if (!cardTypes || cardTypes.length === 0) {
|
if (!cardTypes || cardTypes.length === 0) {
|
||||||
return h('span', { class: 'text-muted-foreground' }, '-')
|
return h('span', { class: 'text-muted-foreground' }, '-')
|
||||||
}
|
}
|
||||||
|
|
||||||
return h('div', { class: 'flex flex-wrap gap-1' },
|
return h('div', { class: 'flex flex-wrap gap-1' },
|
||||||
cardTypes.map(ct =>
|
cardTypes.map(ct =>
|
||||||
h('span', {
|
h('span', {
|
||||||
class: `inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
|
class: `inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
|
||||||
ct.can_generate
|
ct.can_generate
|
||||||
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
|
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
|
||||||
: 'bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400'
|
: 'bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400'
|
||||||
}`
|
}`
|
||||||
}, [
|
}, [
|
||||||
@@ -66,13 +66,26 @@ export function getColumns(actions: {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'card_types',
|
||||||
|
id: 'price',
|
||||||
|
header: () => h('span', {}, '价格'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const cardTypes = row.getValue('card_types') as AgentApp['card_types']
|
||||||
|
if (!cardTypes || cardTypes.length === 0) {
|
||||||
|
return h('span', { class: 'text-muted-foreground' }, '-')
|
||||||
|
}
|
||||||
|
const price = cardTypes[0]?.price || 0
|
||||||
|
return h('span', { class: 'font-medium' }, `¥${price}`)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'status',
|
accessorKey: 'status',
|
||||||
header: () => h('span', {}, '状态'),
|
header: () => h('span', {}, '状态'),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const status = row.getValue('status') as string
|
const status = row.getValue('status') as string
|
||||||
const isActive = status === 'active'
|
const isActive = status === 'active'
|
||||||
return h(Badge, { variant: isActive ? 'default' : 'secondary' }, () =>
|
return h(Badge, { variant: isActive ? 'default' : 'secondary' }, () =>
|
||||||
isActive ? '已启用' : '已禁用'
|
isActive ? '已启用' : '已禁用'
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -137,4 +150,4 @@ export function getColumns(actions: {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
import { BasicPage } from '@/components/global-layout'
|
import { BasicPage } from '@/components/global-layout'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
import api from '@/services/api'
|
import api from '@/services/api'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -216,34 +217,32 @@ onMounted(() => {
|
|||||||
</UiSelect>
|
</UiSelect>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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="formData.card_type_id" :disabled="!formData.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>
|
<UiSelectContent>
|
||||||
<UiSelectContent>
|
<UiSelectItem v-for="ct in cardTypes" :key="ct.id" :value="String(ct.id)">
|
||||||
<UiSelectItem v-for="ct in cardTypes" :key="ct.id" :value="String(ct.id)">
|
{{ ct.name }}<template v-if="ct.billing_type"> ({{ ct.billing_type }})</template>
|
||||||
{{ ct.name }}<template v-if="ct.billing_type"> ({{ ct.billing_type }})</template>
|
</UiSelectItem>
|
||||||
</UiSelectItem>
|
</UiSelectContent>
|
||||||
</UiSelectContent>
|
</UiSelect>
|
||||||
</UiSelect>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel for="price">{{ t('admin.agentApps.createForm.price') }} <span class="text-destructive">*</span></UiLabel>
|
<UiLabel for="price">{{ t('admin.agentApps.createForm.price') }} <span class="text-destructive">*</span></UiLabel>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<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
|
<Input
|
||||||
id="price"
|
id="price"
|
||||||
v-model.number="formData.price"
|
v-model="formData.price"
|
||||||
type="number"
|
type="number"
|
||||||
class="pl-7"
|
class="pl-7"
|
||||||
:placeholder="t('admin.agentApps.createForm.pricePlaceholder')"
|
:placeholder="t('admin.agentApps.createForm.pricePlaceholder')"
|
||||||
:disabled="saving"
|
:disabled="saving"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -323,4 +322,4 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BasicPage>
|
</BasicPage>
|
||||||
</template>
|
</template>
|
||||||
Reference in New Issue
Block a user