|
|
@@ -224,6 +224,18 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ v-if="isColumnVisible('sysUserId')"
|
|
|
+ prop="sysUserId"
|
|
|
+ label="关联用户"
|
|
|
+ min-width="160"
|
|
|
+ align="center"
|
|
|
+ header-align="center"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ resolveUserDisplay(row.sysUserId) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column
|
|
|
v-if="isColumnVisible('age')"
|
|
|
prop="age"
|
|
|
@@ -338,6 +350,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"
|
|
|
@@ -466,6 +496,7 @@
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { Check, Close, Delete, DocumentCopy, Download, Edit, Filter, Plus, User } from '@element-plus/icons-vue'
|
|
|
+import { fetchUserOptions } from '@/api/admin'
|
|
|
import { personnelApi, type Personnel, type PersonnelDetail, type CertRow } from '@/api/pl'
|
|
|
import PlCertFormList from '@/components/business/pl-cert-form-list/index.vue'
|
|
|
import PlPasteRecognizeDialog from '@/components/business/pl-paste-recognize-dialog/index.vue'
|
|
|
@@ -535,6 +566,7 @@ const listColumnDefs = [
|
|
|
{ prop: 'personnelAffiliation', label: '人员归属' },
|
|
|
{ prop: 'personnelType', label: '人员类型' },
|
|
|
{ prop: 'phoneNo', label: '手机号' },
|
|
|
+ { prop: 'sysUserId', label: '关联用户' },
|
|
|
{ prop: 'age', label: '年龄' },
|
|
|
{ prop: 'idCardNo', label: '身份证号' },
|
|
|
{ prop: 'status', label: '状态' },
|
|
|
@@ -548,7 +580,40 @@ const {
|
|
|
isIndeterminate,
|
|
|
isColumnVisible,
|
|
|
resetToDefault
|
|
|
-} = usePersistedListColumns('pl-personnel-list-columns-v3', listColumnDefs)
|
|
|
+} = usePersistedListColumns('pl-personnel-list-columns-v4', listColumnDefs)
|
|
|
+
|
|
|
+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
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function resolveUserDisplay(sysUserId?: string | null) {
|
|
|
+ const uid = (sysUserId || '').trim()
|
|
|
+ if (!uid) return '—'
|
|
|
+ const hit = userOptions.value.find((u) => u.userId === uid)
|
|
|
+ return hit?.displayName || uid
|
|
|
+}
|
|
|
|
|
|
const {
|
|
|
filters,
|
|
|
@@ -653,7 +718,8 @@ const formData = reactive({
|
|
|
personnelAffiliation: 'self',
|
|
|
status: '1',
|
|
|
remark: '',
|
|
|
- photoUrls: ''
|
|
|
+ photoUrls: '',
|
|
|
+ sysUserId: '' as string
|
|
|
})
|
|
|
|
|
|
const requiredPersonnelCertNames = computed(() =>
|
|
|
@@ -720,6 +786,7 @@ const viewSections = computed<ViewSection[]>(() => {
|
|
|
fields: [
|
|
|
{ label: '姓名', value: d.personnelName },
|
|
|
{ label: '手机号', value: d.phoneNo || '—' },
|
|
|
+ { label: '关联用户', value: resolveUserDisplay(d.sysUserId) },
|
|
|
{
|
|
|
label: '人员归属',
|
|
|
value: getPersonnelAffiliationLabel(d.personnelAffiliation),
|
|
|
@@ -919,10 +986,12 @@ function handleAdd() {
|
|
|
personnelAffiliation: affiliationMode.value,
|
|
|
status: '1',
|
|
|
remark: '',
|
|
|
- photoUrls: ''
|
|
|
+ photoUrls: '',
|
|
|
+ sysUserId: ''
|
|
|
})
|
|
|
resetCertList()
|
|
|
dialogVisible.value = true
|
|
|
+ void ensureUserOptions()
|
|
|
}
|
|
|
|
|
|
async function handleView(row: Personnel) {
|
|
|
@@ -930,6 +999,7 @@ async function handleView(row: Personnel) {
|
|
|
viewLoading.value = true
|
|
|
viewDetail.value = null
|
|
|
try {
|
|
|
+ await ensureUserOptions()
|
|
|
const res = await personnelApi.get(row.id)
|
|
|
viewDetail.value = ((res.data ?? res) || null) as PersonnelDetail | null
|
|
|
} catch {
|
|
|
@@ -963,6 +1033,7 @@ async function handleEdit(row: Personnel) {
|
|
|
isEdit.value = true
|
|
|
dialogVisible.value = true
|
|
|
try {
|
|
|
+ await ensureUserOptions()
|
|
|
const res = await personnelApi.get(row.id)
|
|
|
const detail = ((res.data ?? res) || {}) as PersonnelDetail
|
|
|
Object.assign(formData, {
|
|
|
@@ -975,7 +1046,8 @@ async function handleEdit(row: Personnel) {
|
|
|
personnelAffiliation: affiliationMode.value,
|
|
|
status: detail.status,
|
|
|
remark: detail.remark || '',
|
|
|
- photoUrls: detail.photoUrls || ''
|
|
|
+ photoUrls: detail.photoUrls || '',
|
|
|
+ sysUserId: detail.sysUserId || ''
|
|
|
})
|
|
|
certList.value = mergePersonnelCertSlots(
|
|
|
|
|
|
@@ -1019,6 +1091,7 @@ async function handleSubmit() {
|
|
|
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(),
|
|
|
@@ -1037,6 +1110,13 @@ async function handleSubmit() {
|
|
|
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
|
|
|
dialogVisible.value = false
|
|
|
fetchData()
|
|
|
+ } catch (e: any) {
|
|
|
+ const msg = e?.response?.data?.detail || e?.response?.data?.message || e?.message || ''
|
|
|
+ if (typeof msg === 'string' && msg.includes('已关联')) {
|
|
|
+ ElMessage.error(msg)
|
|
|
+ } else if (msg) {
|
|
|
+ ElMessage.error(msg)
|
|
|
+ }
|
|
|
} finally {
|
|
|
submitLoading.value = false
|
|
|
}
|
|
|
@@ -1063,6 +1143,7 @@ async function handleDelete(row: Personnel) {
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
+ void ensureUserOptions()
|
|
|
fetchData()
|
|
|
|
|
|
nextTick(() => recalcTableHeight())
|