批量更新功能
This commit is contained in:
1 parent
6f106e43af
commit
a26c98b92b
79 files changed
+4529
-724
No files matched your search
@@ -41,9 +41,11 @@
|
||||
<h4>联系方式</h4>
|
||||
<el-descriptions :column="1" border size="small">
|
||||
<el-descriptions-item label="手机号">
|
||||
<span v-if="contact.phone">
|
||||
<a :href="'tel:' + contact.phone" class="contact-link">{{ contact.phone }}</a>
|
||||
</span>
|
||||
<template v-if="contactMobiles.length">
|
||||
<div v-for="(m, i) in contactMobiles" :key="i">
|
||||
<a :href="'tel:' + m" class="contact-link">{{ m }}</a>
|
||||
</div>
|
||||
</template>
|
||||
<span v-else class="empty-text">-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办公电话">
|
||||
@@ -124,6 +126,28 @@ const genderLabel = (gender) => {
|
||||
return map[gender] || '未知'
|
||||
}
|
||||
|
||||
/** 解析多手机号:JSON 数组字符串优先,缺省回退单个 phone */
|
||||
const contactMobiles = computed(() => {
|
||||
const c = props.contact
|
||||
if (!c) return []
|
||||
let arr = []
|
||||
const raw = c.mobiles
|
||||
if (Array.isArray(raw)) {
|
||||
arr = raw
|
||||
} else if (raw !== undefined && raw !== null && String(raw).trim()) {
|
||||
const s = String(raw).trim()
|
||||
try {
|
||||
const parsed = JSON.parse(s)
|
||||
arr = Array.isArray(parsed) ? parsed : [s]
|
||||
} catch (error) {
|
||||
arr = s.split(/[,,、;;\s]+/)
|
||||
}
|
||||
}
|
||||
arr = arr.map((m) => String(m || '').trim()).filter(Boolean)
|
||||
if (arr.length === 0 && c.phone) arr = [String(c.phone).trim()]
|
||||
return arr
|
||||
})
|
||||
|
||||
const handleEdit = () => {
|
||||
emit('edit', props.contact)
|
||||
}
|
||||
|
||||
@@ -84,15 +84,23 @@
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input
|
||||
v-model="form.phone"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="20"
|
||||
/>
|
||||
<el-col :span="24">
|
||||
<!-- 手机号:支持登记多个(有的人有多个号码),默认一行,点加号增加,最多 5 个 -->
|
||||
<el-form-item label="手机号" prop="mobiles">
|
||||
<div class="mobile-list">
|
||||
<div v-for="(m, idx) in form.mobiles" :key="idx" class="mobile-item">
|
||||
<el-input v-model="form.mobiles[idx]" placeholder="请输入手机号" maxlength="20" clearable />
|
||||
<el-button :icon="Delete" :disabled="form.mobiles.length <= 1" @click="removeMobile(idx)" />
|
||||
</div>
|
||||
<el-button v-if="form.mobiles.length < 5" type="primary" plain :icon="Plus" @click="addMobile">
|
||||
添加手机号({{ form.mobiles.length }}/5)
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="办公电话" prop="work_phone">
|
||||
<el-input
|
||||
@@ -223,6 +231,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Plus, Delete } from '@element-plus/icons-vue'
|
||||
import { getOrganizationList, getPositionList, getPositionDetail } from '@/api/contactOA'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -248,6 +257,8 @@ const emptyForm = () => ({
|
||||
contact_type: 2,
|
||||
gender: 0,
|
||||
phone: '',
|
||||
// 手机号:动态列表,默认一行,点击加号增加(最多 5 个,与后端存储上限一致)
|
||||
mobiles: [''],
|
||||
work_phone: '',
|
||||
email: '',
|
||||
wechat: '',
|
||||
@@ -273,8 +284,17 @@ const rules = {
|
||||
contact_type: [
|
||||
{ required: true, message: '请选择联系人类型', trigger: 'change' }
|
||||
],
|
||||
phone: [
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||
mobiles: [
|
||||
{
|
||||
// 手机号非必填,填写时逐个校验格式
|
||||
validator: (rule, value, callback) => {
|
||||
const list = Array.isArray(value) ? value : [value]
|
||||
const bad = list.map((m) => String(m || '').trim()).find((m) => m && !/^1[3-9]\d{9}$/.test(m))
|
||||
if (bad) return callback(new Error(`手机号格式不正确:${bad}`))
|
||||
callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
email: [
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||
@@ -367,9 +387,41 @@ const handlePositionChange = (val) => {
|
||||
form.value.position_title = pos ? pos.position_name : ''
|
||||
}
|
||||
|
||||
/** 增加一行手机号输入 */
|
||||
const addMobile = () => {
|
||||
if (form.value.mobiles.length < 5) form.value.mobiles.push('')
|
||||
}
|
||||
|
||||
/** 删除一行手机号输入(至少保留一行) */
|
||||
const removeMobile = (idx) => {
|
||||
if (form.value.mobiles.length > 1) form.value.mobiles.splice(idx, 1)
|
||||
}
|
||||
|
||||
/** 解析手机号集合:兼容 JSON 数组字符串 / 分隔符字符串 / 旧数据单个号码(最多 5 个) */
|
||||
const parseMobilesArr = (row) => {
|
||||
let arr = []
|
||||
const raw = row?.mobiles
|
||||
if (Array.isArray(raw)) {
|
||||
arr = raw
|
||||
} else if (raw !== undefined && raw !== null && String(raw).trim()) {
|
||||
const s = String(raw).trim()
|
||||
try {
|
||||
const parsed = JSON.parse(s)
|
||||
arr = Array.isArray(parsed) ? parsed : [s]
|
||||
} catch (error) {
|
||||
arr = s.split(/[,,、;;\s]+/)
|
||||
}
|
||||
}
|
||||
arr = arr.map((m) => String(m || '').trim()).filter(Boolean).slice(0, 5)
|
||||
if (arr.length === 0 && row?.phone) arr = [String(row.phone).trim()]
|
||||
return arr.length ? arr : ['']
|
||||
}
|
||||
|
||||
const initForm = () => {
|
||||
if (props.isEdit && props.formData) {
|
||||
form.value = { ...props.formData }
|
||||
// 回显手机号:mobiles JSON 数组优先,缺省回退单个 phone
|
||||
form.value.mobiles = parseMobilesArr(props.formData)
|
||||
} else {
|
||||
form.value = emptyForm()
|
||||
}
|
||||
@@ -380,7 +432,14 @@ const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
submitting.value = true
|
||||
const payload = { ...form.value }
|
||||
// 手机号:动态列表去空后存 JSON 字符串(后端 TEXT 存 JSON 数组,最多 5 个),
|
||||
// phone 同步为第一个号码,保持新旧字段一致
|
||||
const valid = form.value.mobiles.map((m) => String(m || '').trim()).filter(Boolean)
|
||||
const payload = {
|
||||
...form.value,
|
||||
mobiles: valid.length ? JSON.stringify(valid) : '',
|
||||
phone: valid[0] || ''
|
||||
}
|
||||
emit('save', payload)
|
||||
} catch {
|
||||
// validation failed
|
||||
@@ -424,4 +483,16 @@ watch(() => form.value.contact_type, (type) => {
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 手机号:动态列表,每行输入框 + 删除按钮 */
|
||||
.mobile-list {
|
||||
width: 100%;
|
||||
|
||||
.mobile-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -116,7 +116,9 @@
|
||||
<span>{{ row.org_name || row.dept_name || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="phone" label="手机号" width="140" />
|
||||
<el-table-column label="手机号" width="150" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ formatMobiles(row.mobiles) || row.phone || '-' }}</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column prop="wechat" label="微信" width="180">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.wechat || '-' }}</span>
|
||||
@@ -319,6 +321,8 @@ const editFormData = reactive({
|
||||
contact_type: 2,
|
||||
gender: 0,
|
||||
phone: '',
|
||||
// 多手机号原始 JSON 数组字符串,编辑弹窗内解析回显
|
||||
mobiles: '',
|
||||
work_phone: '',
|
||||
email: '',
|
||||
wechat: '',
|
||||
@@ -342,6 +346,7 @@ const openCreateContact = () => {
|
||||
contact_type: 2,
|
||||
gender: 0,
|
||||
phone: '',
|
||||
mobiles: '',
|
||||
work_phone: '',
|
||||
email: '',
|
||||
wechat: '',
|
||||
@@ -366,6 +371,8 @@ const openEditContact = (row) => {
|
||||
contact_type: row.contact_type,
|
||||
gender: row.gender,
|
||||
phone: row.phone,
|
||||
// 多手机号原始 JSON 数组字符串,编辑弹窗内解析回显
|
||||
mobiles: row.mobiles || '',
|
||||
work_phone: row.work_phone,
|
||||
email: row.email,
|
||||
wechat: row.wechat,
|
||||
@@ -399,6 +406,25 @@ const handleSaveContact = async (payload) => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 多手机号格式化:JSON数组 / 分隔符字符串 / 单个号码 → "138…、139…"(无内容返回空串) */
|
||||
function formatMobiles(val) {
|
||||
if (!val) return ''
|
||||
let arr = []
|
||||
if (Array.isArray(val)) {
|
||||
arr = val
|
||||
} else {
|
||||
const s = String(val).trim()
|
||||
if (!s) return ''
|
||||
try {
|
||||
const parsed = JSON.parse(s)
|
||||
arr = Array.isArray(parsed) ? parsed : [s]
|
||||
} catch (e) {
|
||||
arr = s.split(/[,,、;;\s]+/)
|
||||
}
|
||||
}
|
||||
return arr.map((m) => String(m || '').trim()).filter(Boolean).join('、')
|
||||
}
|
||||
|
||||
// ---- 详情 ----
|
||||
const detailDialogVisible = ref(false)
|
||||
const detailContact = ref(null)
|
||||
|
||||
Reference in new issue
Block a user