fix(frontend): 完善表单校验并添加错误提示
- UserEditModal: 添加 email 必填和 concurrency 最小值校验 - UserAttributesConfigModal: 添加 key/name 必填和 options 非空校验 - GroupsView: 添加 name 必填校验 - ProxiesView: 添加 name/host 必填和 port 范围校验 - UserBalanceModal: 添加 amount 有效性和余额充足性校验 - RedeemView: 添加空兑换码错误提示 - i18n: 添加所有新增校验的中英文翻译
This commit is contained in:
@@ -37,10 +37,21 @@ watch(() => props.show, (v) => { if(v) { form.amount = 0; form.notes = '' } })
|
|||||||
|
|
||||||
const calculateNewBalance = () => (props.user ? (props.operation === 'add' ? props.user.balance + form.amount : props.user.balance - form.amount) : 0)
|
const calculateNewBalance = () => (props.user ? (props.operation === 'add' ? props.user.balance + form.amount : props.user.balance - form.amount) : 0)
|
||||||
const handleBalanceSubmit = async () => {
|
const handleBalanceSubmit = async () => {
|
||||||
if (!props.user) return; submitting.value = true
|
if (!props.user) return
|
||||||
|
if (!form.amount || form.amount <= 0) {
|
||||||
|
appStore.showError(t('admin.users.amountRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (props.operation === 'subtract' && form.amount > props.user.balance) {
|
||||||
|
appStore.showError(t('admin.users.insufficientBalance'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
await adminAPI.users.updateBalance(props.user.id, form.amount, props.operation, form.notes)
|
await adminAPI.users.updateBalance(props.user.id, form.amount, props.operation, form.notes)
|
||||||
appStore.showSuccess(t('common.success')); emit('success'); emit('close')
|
appStore.showSuccess(t('common.success')); emit('success'); emit('close')
|
||||||
} catch {} finally { submitting.value = false }
|
} catch (e: any) {
|
||||||
|
appStore.showError(e.response?.data?.detail || t('common.error'))
|
||||||
|
} finally { submitting.value = false }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -86,6 +86,14 @@ const copyPassword = async () => {
|
|||||||
}
|
}
|
||||||
const handleUpdateUser = async () => {
|
const handleUpdateUser = async () => {
|
||||||
if (!props.user) return
|
if (!props.user) return
|
||||||
|
if (!form.email.trim()) {
|
||||||
|
appStore.showError(t('admin.users.emailRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (form.concurrency < 1) {
|
||||||
|
appStore.showError(t('admin.users.concurrencyMin'))
|
||||||
|
return
|
||||||
|
}
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
const data: any = { email: form.email, username: form.username, notes: form.notes, concurrency: form.concurrency }
|
const data: any = { email: form.email, username: form.username, notes: form.notes, concurrency: form.concurrency }
|
||||||
|
|||||||
@@ -344,6 +344,18 @@ const removeOption = (index: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
|
if (!form.key.trim()) {
|
||||||
|
appStore.showError(t('admin.users.attributes.keyRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!form.name.trim()) {
|
||||||
|
appStore.showError(t('admin.users.attributes.nameRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((form.type === 'select' || form.type === 'multi_select') && form.options.length === 0) {
|
||||||
|
appStore.showError(t('admin.users.attributes.optionsRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
saving.value = true
|
saving.value = true
|
||||||
try {
|
try {
|
||||||
const data = {
|
const data = {
|
||||||
|
|||||||
@@ -462,7 +462,8 @@ export default {
|
|||||||
days: ' days',
|
days: ' days',
|
||||||
codeRedeemSuccess: 'Code redeemed successfully!',
|
codeRedeemSuccess: 'Code redeemed successfully!',
|
||||||
failedToRedeem: 'Failed to redeem code. Please check the code and try again.',
|
failedToRedeem: 'Failed to redeem code. Please check the code and try again.',
|
||||||
subscriptionRefreshFailed: 'Redeemed successfully, but failed to refresh subscription status.'
|
subscriptionRefreshFailed: 'Redeemed successfully, but failed to refresh subscription status.',
|
||||||
|
pleaseEnterCode: 'Please enter a redeem code'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Profile
|
// Profile
|
||||||
@@ -658,6 +659,10 @@ export default {
|
|||||||
failedToDelete: 'Failed to delete user',
|
failedToDelete: 'Failed to delete user',
|
||||||
failedToToggle: 'Failed to update user status',
|
failedToToggle: 'Failed to update user status',
|
||||||
failedToLoadApiKeys: 'Failed to load user API keys',
|
failedToLoadApiKeys: 'Failed to load user API keys',
|
||||||
|
emailRequired: 'Please enter email',
|
||||||
|
concurrencyMin: 'Concurrency must be at least 1',
|
||||||
|
amountRequired: 'Please enter a valid amount',
|
||||||
|
insufficientBalance: 'Insufficient balance',
|
||||||
deleteConfirm: "Are you sure you want to delete '{email}'? This action cannot be undone.",
|
deleteConfirm: "Are you sure you want to delete '{email}'? This action cannot be undone.",
|
||||||
setAllowedGroups: 'Set Allowed Groups',
|
setAllowedGroups: 'Set Allowed Groups',
|
||||||
allowedGroupsHint:
|
allowedGroupsHint:
|
||||||
@@ -689,7 +694,6 @@ export default {
|
|||||||
failedToDeposit: 'Failed to deposit',
|
failedToDeposit: 'Failed to deposit',
|
||||||
failedToWithdraw: 'Failed to withdraw',
|
failedToWithdraw: 'Failed to withdraw',
|
||||||
useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance',
|
useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance',
|
||||||
insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal',
|
|
||||||
roles: {
|
roles: {
|
||||||
admin: 'Admin',
|
admin: 'Admin',
|
||||||
user: 'User'
|
user: 'User'
|
||||||
@@ -749,6 +753,9 @@ export default {
|
|||||||
failedToLoad: 'Failed to load attributes',
|
failedToLoad: 'Failed to load attributes',
|
||||||
failedToCreate: 'Failed to create attribute',
|
failedToCreate: 'Failed to create attribute',
|
||||||
failedToUpdate: 'Failed to update attribute',
|
failedToUpdate: 'Failed to update attribute',
|
||||||
|
keyRequired: 'Please enter attribute key',
|
||||||
|
nameRequired: 'Please enter display name',
|
||||||
|
optionsRequired: 'Please add at least one option',
|
||||||
failedToDelete: 'Failed to delete attribute',
|
failedToDelete: 'Failed to delete attribute',
|
||||||
failedToReorder: 'Failed to update order',
|
failedToReorder: 'Failed to update order',
|
||||||
keyExists: 'Attribute key already exists',
|
keyExists: 'Attribute key already exists',
|
||||||
@@ -816,6 +823,7 @@ export default {
|
|||||||
failedToCreate: 'Failed to create group',
|
failedToCreate: 'Failed to create group',
|
||||||
failedToUpdate: 'Failed to update group',
|
failedToUpdate: 'Failed to update group',
|
||||||
failedToDelete: 'Failed to delete group',
|
failedToDelete: 'Failed to delete group',
|
||||||
|
nameRequired: 'Please enter group name',
|
||||||
platforms: {
|
platforms: {
|
||||||
all: 'All Platforms',
|
all: 'All Platforms',
|
||||||
anthropic: 'Anthropic',
|
anthropic: 'Anthropic',
|
||||||
@@ -1584,6 +1592,9 @@ export default {
|
|||||||
failedToUpdate: 'Failed to update proxy',
|
failedToUpdate: 'Failed to update proxy',
|
||||||
failedToDelete: 'Failed to delete proxy',
|
failedToDelete: 'Failed to delete proxy',
|
||||||
failedToTest: 'Failed to test proxy',
|
failedToTest: 'Failed to test proxy',
|
||||||
|
nameRequired: 'Please enter proxy name',
|
||||||
|
hostRequired: 'Please enter host address',
|
||||||
|
portInvalid: 'Port must be between 1-65535',
|
||||||
deleteConfirm:
|
deleteConfirm:
|
||||||
"Are you sure you want to delete '{name}'? Accounts using this proxy will have their proxy removed."
|
"Are you sure you want to delete '{name}'? Accounts using this proxy will have their proxy removed."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -459,7 +459,8 @@ export default {
|
|||||||
days: '天',
|
days: '天',
|
||||||
codeRedeemSuccess: '兑换成功!',
|
codeRedeemSuccess: '兑换成功!',
|
||||||
failedToRedeem: '兑换失败,请检查兑换码后重试。',
|
failedToRedeem: '兑换失败,请检查兑换码后重试。',
|
||||||
subscriptionRefreshFailed: '兑换成功,但订阅状态刷新失败。'
|
subscriptionRefreshFailed: '兑换成功,但订阅状态刷新失败。',
|
||||||
|
pleaseEnterCode: '请输入兑换码'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Profile
|
// Profile
|
||||||
@@ -716,6 +717,10 @@ export default {
|
|||||||
concurrencyAdjustedSuccess: '并发数调整成功',
|
concurrencyAdjustedSuccess: '并发数调整成功',
|
||||||
failedToSave: '保存用户失败',
|
failedToSave: '保存用户失败',
|
||||||
failedToAdjust: '调整失败',
|
failedToAdjust: '调整失败',
|
||||||
|
emailRequired: '请输入邮箱',
|
||||||
|
concurrencyMin: '并发数不能小于1',
|
||||||
|
amountRequired: '请输入有效金额',
|
||||||
|
insufficientBalance: '余额不足',
|
||||||
setAllowedGroups: '设置允许分组',
|
setAllowedGroups: '设置允许分组',
|
||||||
allowedGroupsHint: '选择此用户可以使用的标准分组。订阅类型分组请在订阅管理中配置。',
|
allowedGroupsHint: '选择此用户可以使用的标准分组。订阅类型分组请在订阅管理中配置。',
|
||||||
noStandardGroups: '暂无标准分组',
|
noStandardGroups: '暂无标准分组',
|
||||||
@@ -742,7 +747,6 @@ export default {
|
|||||||
failedToDeposit: '充值失败',
|
failedToDeposit: '充值失败',
|
||||||
failedToWithdraw: '退款失败',
|
failedToWithdraw: '退款失败',
|
||||||
useDepositWithdrawButtons: '请使用充值/退款按钮调整余额',
|
useDepositWithdrawButtons: '请使用充值/退款按钮调整余额',
|
||||||
insufficientBalance: '余额不足,退款后余额不能为负数',
|
|
||||||
// Settings Dropdowns
|
// Settings Dropdowns
|
||||||
filterSettings: '筛选设置',
|
filterSettings: '筛选设置',
|
||||||
columnSettings: '列设置',
|
columnSettings: '列设置',
|
||||||
@@ -798,6 +802,9 @@ export default {
|
|||||||
failedToLoad: '加载属性列表失败',
|
failedToLoad: '加载属性列表失败',
|
||||||
failedToCreate: '创建属性失败',
|
failedToCreate: '创建属性失败',
|
||||||
failedToUpdate: '更新属性失败',
|
failedToUpdate: '更新属性失败',
|
||||||
|
keyRequired: '请输入属性键',
|
||||||
|
nameRequired: '请输入显示名称',
|
||||||
|
optionsRequired: '请至少添加一个选项',
|
||||||
failedToDelete: '删除属性失败',
|
failedToDelete: '删除属性失败',
|
||||||
failedToReorder: '更新排序失败',
|
failedToReorder: '更新排序失败',
|
||||||
keyExists: '属性键已存在',
|
keyExists: '属性键已存在',
|
||||||
@@ -905,6 +912,7 @@ export default {
|
|||||||
groupDeleted: '分组删除成功',
|
groupDeleted: '分组删除成功',
|
||||||
failedToCreate: '创建分组失败',
|
failedToCreate: '创建分组失败',
|
||||||
failedToUpdate: '更新分组失败',
|
failedToUpdate: '更新分组失败',
|
||||||
|
nameRequired: '请输入分组名称',
|
||||||
subscription: {
|
subscription: {
|
||||||
title: '订阅设置',
|
title: '订阅设置',
|
||||||
type: '计费类型',
|
type: '计费类型',
|
||||||
@@ -1694,6 +1702,9 @@ export default {
|
|||||||
failedToCreate: '创建代理失败',
|
failedToCreate: '创建代理失败',
|
||||||
failedToUpdate: '更新代理失败',
|
failedToUpdate: '更新代理失败',
|
||||||
failedToTest: '测试代理失败',
|
failedToTest: '测试代理失败',
|
||||||
|
nameRequired: '请输入代理名称',
|
||||||
|
hostRequired: '请输入主机地址',
|
||||||
|
portInvalid: '端口必须在 1-65535 之间',
|
||||||
deleteConfirm: "确定要删除代理 '{name}' 吗?使用此代理的账号将被移除代理设置。"
|
deleteConfirm: "确定要删除代理 '{name}' 吗?使用此代理的账号将被移除代理设置。"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -871,6 +871,10 @@ const closeCreateModal = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleCreateGroup = async () => {
|
const handleCreateGroup = async () => {
|
||||||
|
if (!createForm.name.trim()) {
|
||||||
|
appStore.showError(t('admin.groups.nameRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
await adminAPI.groups.create(createForm)
|
await adminAPI.groups.create(createForm)
|
||||||
@@ -912,6 +916,10 @@ const closeEditModal = () => {
|
|||||||
|
|
||||||
const handleUpdateGroup = async () => {
|
const handleUpdateGroup = async () => {
|
||||||
if (!editingGroup.value) return
|
if (!editingGroup.value) return
|
||||||
|
if (!editForm.name.trim()) {
|
||||||
|
appStore.showError(t('admin.groups.nameRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -887,6 +887,18 @@ const handleBatchCreate = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleCreateProxy = async () => {
|
const handleCreateProxy = async () => {
|
||||||
|
if (!createForm.name.trim()) {
|
||||||
|
appStore.showError(t('admin.proxies.nameRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!createForm.host.trim()) {
|
||||||
|
appStore.showError(t('admin.proxies.hostRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (createForm.port < 1 || createForm.port > 65535) {
|
||||||
|
appStore.showError(t('admin.proxies.portInvalid'))
|
||||||
|
return
|
||||||
|
}
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
await adminAPI.proxies.create({
|
await adminAPI.proxies.create({
|
||||||
@@ -927,6 +939,18 @@ const closeEditModal = () => {
|
|||||||
|
|
||||||
const handleUpdateProxy = async () => {
|
const handleUpdateProxy = async () => {
|
||||||
if (!editingProxy.value) return
|
if (!editingProxy.value) return
|
||||||
|
if (!editForm.name.trim()) {
|
||||||
|
appStore.showError(t('admin.proxies.nameRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!editForm.host.trim()) {
|
||||||
|
appStore.showError(t('admin.proxies.hostRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (editForm.port < 1 || editForm.port > 65535) {
|
||||||
|
appStore.showError(t('admin.proxies.portInvalid'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -531,6 +531,7 @@ const fetchHistory = async () => {
|
|||||||
|
|
||||||
const handleRedeem = async () => {
|
const handleRedeem = async () => {
|
||||||
if (!redeemCode.value.trim()) {
|
if (!redeemCode.value.trim()) {
|
||||||
|
appStore.showError(t('redeem.pleaseEnterCode'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user