feat(web-admin): link system user on personnel form

Allow binding a login account on driver/escort personnel so
miniprogram driver-plan visibility can resolve by sys_user_id.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-09-13 10:42:19 +08:00
1 parent 26b096ae38
commit 3dc71788d4
3 files changed
+149 -7

No files matched your search

@@ -30,6 +30,24 @@
<el-form-item label="手机号" prop="phoneNo">
<el-input v-model="formData.phoneNo" placeholder="请输入手机号(选填)" maxlength="32" />
</el-form-item>
<el-form-item label="关联用户账号" prop="sysUserId">
<el-select
v-model="formData.sysUserId"
placeholder="可选,绑定小程序登录账号"
filterable
clearable
:loading="userSelectLoading"
style="width: 100%"
@focus="ensureUserOptions"
>
<el-option
v-for="user in userOptions"
:key="user.userId"
:label="user.displayName"
:value="user.userId"
/>
</el-select>
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input-number
v-model="formData.age"
@@ -110,6 +128,7 @@
<script setup lang="ts">
import { Check, Close } from '@element-plus/icons-vue'
import { fetchUserOptions } from '@/api/admin'
import { personnelApi, type CertRow, type PersonnelDetail } from '@/api/pl'
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
import PlCertFormList from '@/components/business/pl-cert-form-list/index.vue'
@@ -185,9 +204,38 @@
personnelAffiliation: props.affiliation,
status: '1',
remark: '',
photoUrls: ''
photoUrls: '',
sysUserId: '' as string
})
const userSelectLoading = ref(false)
const userOptions = ref<Array<{ userId: string; nickName: string; displayName: string }>>([])
async function ensureUserOptions() {
if (userOptions.value.length || userSelectLoading.value) return
userSelectLoading.value = true
try {
const res = await fetchUserOptions()
const data = (res as any)?.data ?? res
userOptions.value = (Array.isArray(data) ? data : [])
.map((u: Record<string, unknown>) => {
const userId = String(u.value ?? u.id ?? u.userId ?? '')
const name = String(u.label ?? u.nickName ?? u.userName ?? userId)
const account = String(u.userName ?? u.account ?? '').trim()
return {
userId,
nickName: name,
displayName: account && account !== name ? `${name}(${account})` : name
}
})
.filter((u) => u.userId)
} catch {
userOptions.value = []
} finally {
userSelectLoading.value = false
}
}
const requiredPersonnelCertNames = computed(() =>
isCarrierPersonnel.value
? []
@@ -227,7 +275,8 @@
personnelAffiliation: props.affiliation,
status: '1',
remark: '',
photoUrls: ''
photoUrls: '',
sysUserId: ''
})
resetCertList()
}
@@ -286,7 +335,8 @@
personnelAffiliation: props.affiliation,
status: detail.status,
remark: detail.remark || '',
photoUrls: detail.photoUrls || ''
photoUrls: detail.photoUrls || '',
sysUserId: detail.sysUserId || ''
})
certList.value = mergePersonnelCertSlots(
(detail.certList || []).map((c) => ({
@@ -310,6 +360,7 @@
async function openDialog() {
resetForm()
void ensureUserOptions()
if (props.personnelId) {
await loadDetail(props.personnelId)
} else if (props.initialPersonnelType) {
@@ -341,6 +392,7 @@
status: formData.status,
remark: formData.remark || undefined,
photoUrls: formData.photoUrls ?? '',
sysUserId: (formData.sysUserId || '').trim() || null,
certList: certList.value.map((c) => ({
certId: c.certId,
certName: c.certName.trim(),
@@ -372,6 +424,11 @@
emit('saved', { mode: 'create', personnel: detail })
}
visible.value = false
} catch (e: any) {
const msg = e?.response?.data?.detail || e?.response?.data?.message || e?.message || ''
if (typeof msg === 'string' && msg) {
ElMessage.error(msg)
}
} finally {
submitLoading.value = false
}