mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 07:16:51 +00:00
@@ -153,6 +153,8 @@ export interface ContactInfo {
|
|||||||
remark?: string
|
remark?: string
|
||||||
nickname?: string
|
nickname?: string
|
||||||
alias?: string
|
alias?: string
|
||||||
|
labels?: string[]
|
||||||
|
detailDescription?: string
|
||||||
avatarUrl?: string
|
avatarUrl?: string
|
||||||
type: 'friend' | 'group' | 'official' | 'former_friend' | 'other'
|
type: 'friend' | 'group' | 'official' | 'former_friend' | 'other'
|
||||||
}
|
}
|
||||||
@@ -1321,6 +1323,8 @@ class ChatService {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const labels = this.parseContactLabels(row)
|
||||||
|
const detailDescription = this.getContactDetailDescription(row)
|
||||||
const displayName = row.remark || row.nick_name || row.alias || username
|
const displayName = row.remark || row.nick_name || row.alias || username
|
||||||
|
|
||||||
contacts.push({
|
contacts.push({
|
||||||
@@ -1329,6 +1333,8 @@ class ChatService {
|
|||||||
remark: row.remark || undefined,
|
remark: row.remark || undefined,
|
||||||
nickname: row.nick_name || undefined,
|
nickname: row.nick_name || undefined,
|
||||||
alias: row.alias || undefined,
|
alias: row.alias || undefined,
|
||||||
|
labels: labels.length > 0 ? labels : undefined,
|
||||||
|
detailDescription: detailDescription || undefined,
|
||||||
avatarUrl: undefined,
|
avatarUrl: undefined,
|
||||||
type,
|
type,
|
||||||
lastContactTime: lastContactTimeMap.get(username) || 0
|
lastContactTime: lastContactTimeMap.get(username) || 0
|
||||||
@@ -1880,6 +1886,59 @@ class ChatService {
|
|||||||
return Number.isFinite(parsed) ? parsed : fallback
|
return Number.isFinite(parsed) ? parsed : fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseContactLabels(row: Record<string, any>): string[] {
|
||||||
|
const raw = this.getRowField(row, [
|
||||||
|
'label_list', 'labelList', 'labels', 'label_names', 'labelNames', 'tags', 'tag_list', 'tagList'
|
||||||
|
])
|
||||||
|
const normalizedFromValue = (value: unknown): string[] => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return Array.from(new Set(value.map((item) => String(item || '').trim()).filter(Boolean)))
|
||||||
|
}
|
||||||
|
const text = String(value || '').trim()
|
||||||
|
if (!text) return []
|
||||||
|
return Array.from(new Set(
|
||||||
|
text
|
||||||
|
.replace(/[;;、|]+/g, ',')
|
||||||
|
.split(',')
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
const direct = normalizedFromValue(raw)
|
||||||
|
if (direct.length > 0) return direct
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(row)) {
|
||||||
|
const normalizedKey = key.toLowerCase()
|
||||||
|
if (!normalizedKey.includes('label') && !normalizedKey.includes('tag')) continue
|
||||||
|
if (normalizedKey.includes('img') || normalizedKey.includes('head')) continue
|
||||||
|
const fallback = normalizedFromValue(value)
|
||||||
|
if (fallback.length > 0) return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
private getContactDetailDescription(row: Record<string, any>): string {
|
||||||
|
const value = this.getRowField(row, [
|
||||||
|
'detail_description', 'detailDescription', 'description', 'desc', 'contact_description', 'contactDescription',
|
||||||
|
'profile', 'introduction', 'phone', 'mobile', 'telephone', 'tel', 'vcard', 'card_info', 'cardInfo'
|
||||||
|
])
|
||||||
|
const direct = String(value || '').trim()
|
||||||
|
if (direct) return direct
|
||||||
|
|
||||||
|
for (const [key, rawValue] of Object.entries(row)) {
|
||||||
|
const normalizedKey = key.toLowerCase()
|
||||||
|
const isCandidate = normalizedKey.includes('detail') || normalizedKey.includes('desc') || normalizedKey.includes('description') || normalizedKey.includes('profile') || normalizedKey.includes('intro') || normalizedKey.includes('phone') || normalizedKey.includes('mobile') || normalizedKey.includes('tel') || normalizedKey.includes('vcard') || normalizedKey.includes('card')
|
||||||
|
if (!isCandidate) continue
|
||||||
|
if (normalizedKey.includes('avatar') || normalizedKey.includes('img') || normalizedKey.includes('head')) continue
|
||||||
|
const text = String(rawValue || '').trim()
|
||||||
|
if (text) return text
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
private normalizeUnsignedIntegerToken(raw: any): string | undefined {
|
private normalizeUnsignedIntegerToken(raw: any): string | undefined {
|
||||||
if (raw === undefined || raw === null || raw === '') return undefined
|
if (raw === undefined || raw === null || raw === '') return undefined
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ class ContactExportService {
|
|||||||
displayName: c.displayName,
|
displayName: c.displayName,
|
||||||
remark: c.remark,
|
remark: c.remark,
|
||||||
nickname: c.nickname,
|
nickname: c.nickname,
|
||||||
|
alias: c.alias,
|
||||||
|
labels: Array.isArray(c.labels) ? c.labels : [],
|
||||||
|
detailDescription: c.detailDescription,
|
||||||
type: c.type
|
type: c.type
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -103,12 +106,15 @@ class ContactExportService {
|
|||||||
* 导出为CSV格式
|
* 导出为CSV格式
|
||||||
*/
|
*/
|
||||||
private async exportToCSV(contacts: any[], outputPath: string): Promise<void> {
|
private async exportToCSV(contacts: any[], outputPath: string): Promise<void> {
|
||||||
const headers = ['用户名', '显示名称', '备注', '昵称', '类型']
|
const headers = ['用户名', '显示名称', '备注', '昵称', '微信号', '标签', '详细描述', '类型']
|
||||||
const rows = contacts.map(c => [
|
const rows = contacts.map(c => [
|
||||||
c.username || '',
|
c.username || '',
|
||||||
c.displayName || '',
|
c.displayName || '',
|
||||||
c.remark || '',
|
c.remark || '',
|
||||||
c.nickname || '',
|
c.nickname || '',
|
||||||
|
c.alias || '',
|
||||||
|
Array.isArray(c.labels) ? c.labels.join(' | ') : '',
|
||||||
|
c.detailDescription || '',
|
||||||
this.getTypeLabel(c.type)
|
this.getTypeLabel(c.type)
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -137,9 +143,13 @@ class ContactExportService {
|
|||||||
lines.push(`NICKNAME:${c.nickname}`)
|
lines.push(`NICKNAME:${c.nickname}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 备注
|
const noteParts = [
|
||||||
if (c.remark) {
|
c.remark ? String(c.remark) : '',
|
||||||
lines.push(`NOTE:${c.remark}`)
|
Array.isArray(c.labels) && c.labels.length > 0 ? `标签: ${c.labels.join(', ')}` : '',
|
||||||
|
c.detailDescription ? `详细描述: ${c.detailDescription}` : ''
|
||||||
|
].filter(Boolean)
|
||||||
|
if (noteParts.length > 0) {
|
||||||
|
lines.push(`NOTE:${noteParts.join('\\n')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 微信ID
|
// 微信ID
|
||||||
|
|||||||
@@ -397,6 +397,9 @@ function ContactsPage() {
|
|||||||
displayName: contact.displayName,
|
displayName: contact.displayName,
|
||||||
remark: contact.remark,
|
remark: contact.remark,
|
||||||
nickname: contact.nickname,
|
nickname: contact.nickname,
|
||||||
|
alias: contact.alias,
|
||||||
|
labels: contact.labels,
|
||||||
|
detailDescription: contact.detailDescription,
|
||||||
type: contact.type
|
type: contact.type
|
||||||
}))
|
}))
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
@@ -1110,6 +1113,9 @@ function ContactsPage() {
|
|||||||
<div className="detail-row"><span className="detail-label">用户名</span><span className="detail-value">{selectedContact.username}</span></div>
|
<div className="detail-row"><span className="detail-label">用户名</span><span className="detail-value">{selectedContact.username}</span></div>
|
||||||
<div className="detail-row"><span className="detail-label">昵称</span><span className="detail-value">{selectedContact.nickname || selectedContact.displayName}</span></div>
|
<div className="detail-row"><span className="detail-label">昵称</span><span className="detail-value">{selectedContact.nickname || selectedContact.displayName}</span></div>
|
||||||
{selectedContact.remark && <div className="detail-row"><span className="detail-label">备注</span><span className="detail-value">{selectedContact.remark}</span></div>}
|
{selectedContact.remark && <div className="detail-row"><span className="detail-label">备注</span><span className="detail-value">{selectedContact.remark}</span></div>}
|
||||||
|
{selectedContact.alias && <div className="detail-row"><span className="detail-label">微信号</span><span className="detail-value">{selectedContact.alias}</span></div>}
|
||||||
|
{selectedContact.labels && selectedContact.labels.length > 0 && <div className="detail-row"><span className="detail-label">标签</span><span className="detail-value">{selectedContact.labels.join('、')}</span></div>}
|
||||||
|
{selectedContact.detailDescription && <div className="detail-row"><span className="detail-label">详细描述</span><span className="detail-value">{selectedContact.detailDescription}</span></div>}
|
||||||
<div className="detail-row"><span className="detail-label">类型</span><span className="detail-value">{getContactTypeName(selectedContact.type)}</span></div>
|
<div className="detail-row"><span className="detail-label">类型</span><span className="detail-value">{getContactTypeName(selectedContact.type)}</span></div>
|
||||||
{selectedContactSupportsSns && (
|
{selectedContactSupportsSns && (
|
||||||
<div className="detail-row">
|
<div className="detail-row">
|
||||||
|
|||||||
@@ -663,6 +663,8 @@ export interface ContactsListCacheContact {
|
|||||||
remark?: string
|
remark?: string
|
||||||
nickname?: string
|
nickname?: string
|
||||||
alias?: string
|
alias?: string
|
||||||
|
labels?: string[]
|
||||||
|
detailDescription?: string
|
||||||
type: 'friend' | 'group' | 'official' | 'former_friend' | 'other'
|
type: 'friend' | 'group' | 'official' | 'former_friend' | 'other'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1176,6 +1178,10 @@ export async function getContactsListCache(scopeKey: string): Promise<ContactsLi
|
|||||||
remark: typeof item.remark === 'string' ? item.remark : undefined,
|
remark: typeof item.remark === 'string' ? item.remark : undefined,
|
||||||
nickname: typeof item.nickname === 'string' ? item.nickname : undefined,
|
nickname: typeof item.nickname === 'string' ? item.nickname : undefined,
|
||||||
alias: typeof item.alias === 'string' ? item.alias : undefined,
|
alias: typeof item.alias === 'string' ? item.alias : undefined,
|
||||||
|
labels: Array.isArray(item.labels)
|
||||||
|
? Array.from(new Set(item.labels.map((label) => String(label || '').trim()).filter(Boolean)))
|
||||||
|
: undefined,
|
||||||
|
detailDescription: typeof item.detailDescription === 'string' ? item.detailDescription : undefined,
|
||||||
type: (type === 'friend' || type === 'group' || type === 'official' || type === 'former_friend' || type === 'other')
|
type: (type === 'friend' || type === 'group' || type === 'official' || type === 'former_friend' || type === 'other')
|
||||||
? type
|
? type
|
||||||
: 'other'
|
: 'other'
|
||||||
@@ -1210,6 +1216,10 @@ export async function setContactsListCache(scopeKey: string, contacts: ContactsL
|
|||||||
remark: contact?.remark ? String(contact.remark) : undefined,
|
remark: contact?.remark ? String(contact.remark) : undefined,
|
||||||
nickname: contact?.nickname ? String(contact.nickname) : undefined,
|
nickname: contact?.nickname ? String(contact.nickname) : undefined,
|
||||||
alias: contact?.alias ? String(contact.alias) : undefined,
|
alias: contact?.alias ? String(contact.alias) : undefined,
|
||||||
|
labels: Array.isArray(contact?.labels)
|
||||||
|
? Array.from(new Set(contact.labels.map((label) => String(label || '').trim()).filter(Boolean)))
|
||||||
|
: undefined,
|
||||||
|
detailDescription: contact?.detailDescription ? String(contact.detailDescription) : undefined,
|
||||||
type
|
type
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ export interface ContactInfo {
|
|||||||
remark?: string
|
remark?: string
|
||||||
nickname?: string
|
nickname?: string
|
||||||
alias?: string
|
alias?: string
|
||||||
|
labels?: string[]
|
||||||
|
detailDescription?: string
|
||||||
avatarUrl?: string
|
avatarUrl?: string
|
||||||
type: 'friend' | 'group' | 'official' | 'former_friend' | 'other'
|
type: 'friend' | 'group' | 'official' | 'former_friend' | 'other'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user