add
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="isEdit ? '编辑查询白名单' : '新增查询白名单'"
|
||||
width="640px"
|
||||
destroy-on-close
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-form-item label="生效范围" prop="scope">
|
||||
<el-radio-group v-model="form.scope" @change="handleScopeChange">
|
||||
<el-radio value="global">全局(所有用户)</el-radio>
|
||||
<el-radio value="user">指定用户</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.scope === 'user'" label="用户" prop="user_id">
|
||||
<el-select
|
||||
v-model="form.user_id"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="搜索手机号或企业名"
|
||||
:remote-method="searchUsers"
|
||||
:loading="userLoading"
|
||||
class="w-full"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in userOptions"
|
||||
:key="user.id"
|
||||
:label="formatUserLabel(user)"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="form.name" placeholder="输入姓名;填 * 表示仅匹配身份证" />
|
||||
<div class="form-tip">填 <code>*</code> 时只校验身份证,不校验姓名(兼容历史硬编码逻辑)</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="身份证号" prop="id_card">
|
||||
<el-input
|
||||
v-model="form.id_card"
|
||||
placeholder="18位身份证号"
|
||||
maxlength="18"
|
||||
:disabled="isEdit"
|
||||
/>
|
||||
<div v-if="isEdit" class="form-tip">编辑时不修改身份证号;如需变更请删除后重新添加</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="生效接口" prop="api_codes">
|
||||
<el-select
|
||||
v-model="form.api_codes"
|
||||
multiple
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="选择或输入 API 编码"
|
||||
class="w-full"
|
||||
@change="handleApiCodesChange"
|
||||
>
|
||||
<el-option label="全部接口 (*)" value="*" />
|
||||
<el-option
|
||||
v-for="product in productOptions"
|
||||
:key="product.code"
|
||||
:label="`${product.code} - ${product.name}`"
|
||||
:value="product.code"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="form-tip">选择「全部接口」时仅对入参<strong>必填身份证</strong>的 API 生效,企业类等无身份证入参的接口不受影响</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="可选备注" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { productAdminApi, queryWhitelistApi, userApi } from '@/api'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
entry: { type: Object, default: null }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
const isEdit = computed(() => !!props.entry?.id)
|
||||
const formRef = ref(null)
|
||||
const submitting = ref(false)
|
||||
const userLoading = ref(false)
|
||||
const userOptions = ref([])
|
||||
const productOptions = ref([])
|
||||
|
||||
const defaultForm = () => ({
|
||||
scope: 'global',
|
||||
user_id: '*',
|
||||
name: '*',
|
||||
id_card: '',
|
||||
api_codes: ['*'],
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const form = reactive(defaultForm())
|
||||
|
||||
const rules = {
|
||||
scope: [{ required: true, message: '请选择生效范围', trigger: 'change' }],
|
||||
user_id: [{
|
||||
validator: (_rule, value, callback) => {
|
||||
if (form.scope === 'user' && !value) {
|
||||
callback(new Error('请选择用户'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}],
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
id_card: [{
|
||||
validator: (_rule, value, callback) => {
|
||||
if (isEdit.value) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
if (!value || value.length !== 18) {
|
||||
callback(new Error('请输入18位身份证号'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}],
|
||||
api_codes: [{ required: true, type: 'array', min: 1, message: '请选择至少一个接口', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const formatUserLabel = (user) => {
|
||||
const phone = user.phone || '-'
|
||||
const company = user.enterprise_info?.company_name
|
||||
return company ? `${phone}(${company})` : phone
|
||||
}
|
||||
|
||||
const handleScopeChange = (scope) => {
|
||||
form.user_id = scope === 'global' ? '*' : ''
|
||||
}
|
||||
|
||||
const handleApiCodesChange = (codes) => {
|
||||
if (codes.includes('*') && codes.length > 1) {
|
||||
form.api_codes = ['*']
|
||||
}
|
||||
}
|
||||
|
||||
const searchUsers = async (query) => {
|
||||
if (!query) return
|
||||
userLoading.value = true
|
||||
try {
|
||||
const res = await userApi.getUserList({ phone: query, page: 1, page_size: 20 })
|
||||
userOptions.value = res.data?.items || []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
userLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const loadProducts = async () => {
|
||||
try {
|
||||
const res = await productAdminApi.getAvailableProducts({ page: 1, page_size: 500 })
|
||||
productOptions.value = res.data?.items || res.data || []
|
||||
} catch (e) {
|
||||
console.error('加载产品列表失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
const fillForm = (entry) => {
|
||||
Object.assign(form, defaultForm())
|
||||
if (!entry) return
|
||||
form.scope = entry.is_global ? 'global' : 'user'
|
||||
form.user_id = entry.user_id
|
||||
form.name = entry.name
|
||||
form.api_codes = [...(entry.api_codes || ['*'])]
|
||||
form.remark = entry.remark || ''
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, (open) => {
|
||||
if (open) {
|
||||
fillForm(props.entry)
|
||||
loadProducts()
|
||||
if (props.entry && !props.entry.is_global) {
|
||||
userOptions.value = [{ id: props.entry.user_id, phone: props.entry.user_id }]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate()
|
||||
submitting.value = true
|
||||
try {
|
||||
const payload = {
|
||||
user_id: form.scope === 'global' ? '*' : form.user_id,
|
||||
name: form.name.trim(),
|
||||
api_codes: form.api_codes,
|
||||
remark: form.remark
|
||||
}
|
||||
if (!isEdit.value) {
|
||||
payload.id_card = form.id_card.trim()
|
||||
await queryWhitelistApi.createEntry(payload)
|
||||
ElMessage.success('创建成功')
|
||||
} else {
|
||||
await queryWhitelistApi.updateEntry(props.entry.id, {
|
||||
name: payload.name,
|
||||
api_codes: payload.api_codes,
|
||||
remark: payload.remark
|
||||
})
|
||||
ElMessage.success('更新成功')
|
||||
}
|
||||
visible.value = false
|
||||
emit('success')
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || '操作失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleClosed = () => {
|
||||
formRef.value?.resetFields()
|
||||
Object.assign(form, defaultForm())
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-tip {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.form-tip code {
|
||||
background: #f3f4f6;
|
||||
padding: 0 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user