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 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 {
|
||||
await adminAPI.users.updateBalance(props.user.id, form.amount, props.operation, form.notes)
|
||||
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>
|
||||
@@ -86,6 +86,14 @@ const copyPassword = async () => {
|
||||
}
|
||||
const handleUpdateUser = async () => {
|
||||
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
|
||||
try {
|
||||
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 () => {
|
||||
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
|
||||
try {
|
||||
const data = {
|
||||
|
||||
@@ -462,7 +462,8 @@ export default {
|
||||
days: ' days',
|
||||
codeRedeemSuccess: 'Code redeemed successfully!',
|
||||
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
|
||||
@@ -658,6 +659,10 @@ export default {
|
||||
failedToDelete: 'Failed to delete user',
|
||||
failedToToggle: 'Failed to update user status',
|
||||
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.",
|
||||
setAllowedGroups: 'Set Allowed Groups',
|
||||
allowedGroupsHint:
|
||||
@@ -689,7 +694,6 @@ export default {
|
||||
failedToDeposit: 'Failed to deposit',
|
||||
failedToWithdraw: 'Failed to withdraw',
|
||||
useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance',
|
||||
insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal',
|
||||
roles: {
|
||||
admin: 'Admin',
|
||||
user: 'User'
|
||||
@@ -749,6 +753,9 @@ export default {
|
||||
failedToLoad: 'Failed to load attributes',
|
||||
failedToCreate: 'Failed to create 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',
|
||||
failedToReorder: 'Failed to update order',
|
||||
keyExists: 'Attribute key already exists',
|
||||
@@ -816,6 +823,7 @@ export default {
|
||||
failedToCreate: 'Failed to create group',
|
||||
failedToUpdate: 'Failed to update group',
|
||||
failedToDelete: 'Failed to delete group',
|
||||
nameRequired: 'Please enter group name',
|
||||
platforms: {
|
||||
all: 'All Platforms',
|
||||
anthropic: 'Anthropic',
|
||||
@@ -1584,6 +1592,9 @@ export default {
|
||||
failedToUpdate: 'Failed to update proxy',
|
||||
failedToDelete: 'Failed to delete proxy',
|
||||
failedToTest: 'Failed to test proxy',
|
||||
nameRequired: 'Please enter proxy name',
|
||||
hostRequired: 'Please enter host address',
|
||||
portInvalid: 'Port must be between 1-65535',
|
||||
deleteConfirm:
|
||||
"Are you sure you want to delete '{name}'? Accounts using this proxy will have their proxy removed."
|
||||
},
|
||||
|
||||
@@ -459,7 +459,8 @@ export default {
|
||||
days: '天',
|
||||
codeRedeemSuccess: '兑换成功!',
|
||||
failedToRedeem: '兑换失败,请检查兑换码后重试。',
|
||||
subscriptionRefreshFailed: '兑换成功,但订阅状态刷新失败。'
|
||||
subscriptionRefreshFailed: '兑换成功,但订阅状态刷新失败。',
|
||||
pleaseEnterCode: '请输入兑换码'
|
||||
},
|
||||
|
||||
// Profile
|
||||
@@ -716,6 +717,10 @@ export default {
|
||||
concurrencyAdjustedSuccess: '并发数调整成功',
|
||||
failedToSave: '保存用户失败',
|
||||
failedToAdjust: '调整失败',
|
||||
emailRequired: '请输入邮箱',
|
||||
concurrencyMin: '并发数不能小于1',
|
||||
amountRequired: '请输入有效金额',
|
||||
insufficientBalance: '余额不足',
|
||||
setAllowedGroups: '设置允许分组',
|
||||
allowedGroupsHint: '选择此用户可以使用的标准分组。订阅类型分组请在订阅管理中配置。',
|
||||
noStandardGroups: '暂无标准分组',
|
||||
@@ -742,7 +747,6 @@ export default {
|
||||
failedToDeposit: '充值失败',
|
||||
failedToWithdraw: '退款失败',
|
||||
useDepositWithdrawButtons: '请使用充值/退款按钮调整余额',
|
||||
insufficientBalance: '余额不足,退款后余额不能为负数',
|
||||
// Settings Dropdowns
|
||||
filterSettings: '筛选设置',
|
||||
columnSettings: '列设置',
|
||||
@@ -798,6 +802,9 @@ export default {
|
||||
failedToLoad: '加载属性列表失败',
|
||||
failedToCreate: '创建属性失败',
|
||||
failedToUpdate: '更新属性失败',
|
||||
keyRequired: '请输入属性键',
|
||||
nameRequired: '请输入显示名称',
|
||||
optionsRequired: '请至少添加一个选项',
|
||||
failedToDelete: '删除属性失败',
|
||||
failedToReorder: '更新排序失败',
|
||||
keyExists: '属性键已存在',
|
||||
@@ -905,6 +912,7 @@ export default {
|
||||
groupDeleted: '分组删除成功',
|
||||
failedToCreate: '创建分组失败',
|
||||
failedToUpdate: '更新分组失败',
|
||||
nameRequired: '请输入分组名称',
|
||||
subscription: {
|
||||
title: '订阅设置',
|
||||
type: '计费类型',
|
||||
@@ -1694,6 +1702,9 @@ export default {
|
||||
failedToCreate: '创建代理失败',
|
||||
failedToUpdate: '更新代理失败',
|
||||
failedToTest: '测试代理失败',
|
||||
nameRequired: '请输入代理名称',
|
||||
hostRequired: '请输入主机地址',
|
||||
portInvalid: '端口必须在 1-65535 之间',
|
||||
deleteConfirm: "确定要删除代理 '{name}' 吗?使用此代理的账号将被移除代理设置。"
|
||||
},
|
||||
|
||||
|
||||
@@ -871,6 +871,10 @@ const closeCreateModal = () => {
|
||||
}
|
||||
|
||||
const handleCreateGroup = async () => {
|
||||
if (!createForm.name.trim()) {
|
||||
appStore.showError(t('admin.groups.nameRequired'))
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
await adminAPI.groups.create(createForm)
|
||||
@@ -912,6 +916,10 @@ const closeEditModal = () => {
|
||||
|
||||
const handleUpdateGroup = async () => {
|
||||
if (!editingGroup.value) return
|
||||
if (!editForm.name.trim()) {
|
||||
appStore.showError(t('admin.groups.nameRequired'))
|
||||
return
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
|
||||
@@ -887,6 +887,18 @@ const handleBatchCreate = 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
|
||||
try {
|
||||
await adminAPI.proxies.create({
|
||||
@@ -927,6 +939,18 @@ const closeEditModal = () => {
|
||||
|
||||
const handleUpdateProxy = async () => {
|
||||
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
|
||||
try {
|
||||
|
||||
@@ -531,6 +531,7 @@ const fetchHistory = async () => {
|
||||
|
||||
const handleRedeem = async () => {
|
||||
if (!redeemCode.value.trim()) {
|
||||
appStore.showError(t('redeem.pleaseEnterCode'))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user