更新
This commit is contained in:
@@ -198,9 +198,9 @@ export function wecomPromotionSaveWidget(params: Record<string, unknown>) {
|
||||
return request.post({ url: '/firstvisit.wecomPromotion/saveWidget', params })
|
||||
}
|
||||
|
||||
export function wecomPromotionDeletePool(params: { id: number }) {
|
||||
return request.post({ url: '/firstvisit.wecomPromotion/deletePool', params })
|
||||
}
|
||||
export function wecomPromotionDeletePool(params: { id: number }) {
|
||||
return request.post({ url: '/firstvisit.wecomPromotion/deletePool', params, timeout: 120000 })
|
||||
}
|
||||
|
||||
export function wecomPromotionSaveLink(params: Record<string, unknown>) {
|
||||
return request.post({ url: '/firstvisit.wecomPromotion/saveLink', params })
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<span class="heading-icon"><el-icon><Promotion /></el-icon></span>
|
||||
<div>
|
||||
<h1>企业微信获客助手</h1>
|
||||
<p>一个方案保留一条企业微信官方直链,按回调结果自动调度医助权重与数量</p>
|
||||
<p>一个方案保留一条企业微信官方直链,由企业微信在多个可用医助之间原生路由</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="heading-actions">
|
||||
@@ -26,7 +26,7 @@
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<span class="metric-icon is-green"><el-icon><Link /></el-icon></span>
|
||||
<div><small>官方链接</small><strong>{{ overview.summary.online_links }}</strong><p>链接不变,成员范围自动切换</p></div>
|
||||
<div><small>官方链接</small><strong>{{ overview.summary.online_links }}</strong><p>链接不变,成员范围自动更新</p></div>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<span class="metric-icon is-orange"><el-icon><Mouse /></el-icon></span>
|
||||
@@ -91,7 +91,7 @@
|
||||
<el-button :icon="DocumentCopy" :disabled="!selectedPool.main_url" @click="copyText(selectedPool.main_url, '官方获客链接')">复制链接</el-button>
|
||||
<el-button :icon="DocumentCopy" @click="copyText(selectedPool.install_code, 'JS 安装代码')">复制 JS</el-button>
|
||||
<el-button :icon="Edit" @click="openPoolDialog(selectedPool)">编辑方案</el-button>
|
||||
<el-button type="danger" plain :icon="Delete" @click="removePool(selectedPool)">删除</el-button>
|
||||
<el-button type="danger" plain :icon="Delete" :loading="deletingPoolId === Number(selectedPool.id)" :disabled="deletingPoolId > 0" @click="removePool(selectedPool)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -110,8 +110,8 @@
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
title="回调驱动分流"
|
||||
description="企业微信确认实际承接成员后,系统累计今日数量,并在符合启用状态、有效期和上限的成员中按权重随机抽取下一位;随机结果可能连续命中同一人,并发访问及企微同步存在短暂延迟。"
|
||||
title="企业微信原生多人路由"
|
||||
description="当前全部可用医助会同时写入官方链接的成员范围,由企业微信在打开和添加阶段直接进行多人路由。回调只用于统计实际承接结果,并在禁用、过期或达到上限后更新成员范围。"
|
||||
/>
|
||||
|
||||
<el-table :data="selectedMemberRules" class="link-table" stripe>
|
||||
@@ -125,12 +125,12 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="调度" width="105" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.is_current" class="status-tag is-online">{{ Number(row.sync_status) === 0 ? '当前命中' : '待切换' }}</span>
|
||||
<span v-else-if="row.is_applied" class="status-tag is-offline">切换中</span>
|
||||
<span v-else class="muted">候选</span>
|
||||
<span v-if="row.is_in_remote_range && eligibility(row).className === 'is-ok'" class="status-tag is-online">企微路由中</span>
|
||||
<span v-else-if="row.is_in_remote_range" class="status-tag is-offline">待移出</span>
|
||||
<span v-else-if="eligibility(row).className === 'is-ok'" class="status-tag is-offline">待同步</span>
|
||||
<span v-else class="muted">不参与</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="权重" prop="weight" width="85" align="center" />
|
||||
<el-table-column label="今日 / 上限" width="125" align="center">
|
||||
<template #default="{ row }">{{ todayCount(row) }} / {{ Number(row.daily_limit) ? row.daily_limit : '不限' }}</template>
|
||||
</el-table-column>
|
||||
@@ -197,7 +197,7 @@
|
||||
show-icon
|
||||
:closable="false"
|
||||
title="消息次数回调尚未配置完成"
|
||||
description="复用企业微信自建应用现有的“API 接收消息”地址即可,无需新增第二个回调;请确保该地址为 /api/qywx/external-contact/notify,且服务器 Token、AESKey 与企微后台一致。未完成前无法自动累计成员获客数量和切换下一位。"
|
||||
description="复用企业微信自建应用现有的“API 接收消息”地址即可,无需新增第二个回调;请确保该地址为 /api/qywx/external-contact/notify,且服务器 Token、AESKey 与企微后台一致。未完成前无法自动累计成员获客数量,也无法在成员达到上限后自动更新官方范围。"
|
||||
/>
|
||||
|
||||
<el-alert
|
||||
@@ -382,16 +382,10 @@
|
||||
<el-form-item label="获客医助">
|
||||
<el-input :model-value="memberForm.name ? `${memberForm.name} · ${memberForm.userid}` : memberForm.userid" readonly />
|
||||
</el-form-item>
|
||||
<div class="rule-form-grid">
|
||||
<el-form-item label="权重" required>
|
||||
<el-input-number v-model="memberForm.weight" :min="1" :max="100" :disabled="savingMember" style="width: 100%" />
|
||||
<span class="form-tip">权重越高,在后续回调调度中被选中的频率越高。</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="每日获客上限">
|
||||
<el-input-number v-model="memberForm.daily_limit" :min="0" :max="1000000" :disabled="savingMember" style="width: 100%" />
|
||||
<span class="form-tip">0 表示不限;数量按实际获客回调累计。</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="每日获客上限">
|
||||
<el-input-number v-model="memberForm.daily_limit" :min="0" :max="1000000" :disabled="savingMember" style="width: 100%" />
|
||||
<span class="form-tip">0 表示不限;数量按实际获客回调累计,达到上限后会从企微官方成员范围移除。</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="有效时间">
|
||||
<el-date-picker v-model="memberForm.active_range" type="datetimerange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" :disabled="savingMember" style="width: 100%" />
|
||||
</el-form-item>
|
||||
@@ -446,8 +440,9 @@ const checkingApi = ref(false)
|
||||
const memberDialogVisible = ref(false)
|
||||
const savingMember = ref(false)
|
||||
const togglingMemberId = ref(0)
|
||||
const deletingPoolId = ref(0)
|
||||
const poolForm = reactive({ id: 0, name: '', fallback_url: '', status: 1, member_admin_ids: [] as number[], skip_verify: 0, main_url: '' })
|
||||
const memberForm = reactive({ id: 0, name: '', userid: '', weight: 1, daily_limit: 0, status: 1, active_range: [] as string[], remark: '' })
|
||||
const memberForm = reactive({ id: 0, name: '', userid: '', daily_limit: 0, status: 1, active_range: [] as string[], remark: '' })
|
||||
const customerStatsLoading = ref(false)
|
||||
const customerStatsLoaded = ref(false)
|
||||
const syncingCustomers = ref(false)
|
||||
@@ -534,7 +529,9 @@ async function savePool() {
|
||||
poolDialogVisible.value = false
|
||||
await loadOverview()
|
||||
if (result?.id) selectedPoolId.value = Number(result.id)
|
||||
ElMessage.success(poolForm.id ? '分流方案已保存' : '分流方案和官方获客链接已创建')
|
||||
result?.sync_error
|
||||
? ElMessage.warning('方案已保存,企业微信多人范围将在后台自动重试同步')
|
||||
: ElMessage.success(poolForm.id ? '分流方案已保存' : '分流方案和官方获客链接已创建')
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '分流方案保存失败')
|
||||
} finally {
|
||||
@@ -543,14 +540,25 @@ async function savePool() {
|
||||
}
|
||||
|
||||
async function removePool(pool: any) {
|
||||
await ElMessageBox.confirm(`删除“${pool.name}”后,本站将停止使用该链接,但不会永久删除企业微信后台中的官方链接。确认继续?`, '删除分流方案', { type: 'warning' })
|
||||
try {
|
||||
await wecomPromotionDeletePool({ id: Number(pool.id) })
|
||||
ElMessage.success('分流方案已删除')
|
||||
await loadOverview()
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '删除失败')
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`删除“${pool.name}”将同时永久删除企业微信后台中的官方获客链接,已投放的链接会失效且无法恢复。确认继续?`,
|
||||
'永久删除分流方案',
|
||||
{ type: 'warning', confirmButtonText: '永久删除', cancelButtonText: '取消' }
|
||||
)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
deletingPoolId.value = Number(pool.id)
|
||||
try {
|
||||
await wecomPromotionDeletePool({ id: Number(pool.id) })
|
||||
ElMessage.success('分流方案及企业微信官方获客链接已永久删除')
|
||||
await loadOverview()
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '删除失败')
|
||||
} finally {
|
||||
deletingPoolId.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
function openMemberDialog(row: any) {
|
||||
@@ -561,7 +569,6 @@ function openMemberDialog(row: any) {
|
||||
id: Number(row.id),
|
||||
name: row.name || '',
|
||||
userid: row.userid || '',
|
||||
weight: Number(row.weight) || 1,
|
||||
daily_limit: Number(row.daily_limit) || 0,
|
||||
status: Number(row.enabled) === 1 ? 1 : 0,
|
||||
active_range: range,
|
||||
@@ -597,7 +604,7 @@ async function handleMemberToggle(row: any, value: unknown) {
|
||||
await loadOverview()
|
||||
result?.sync_error
|
||||
? ElMessage.warning('成员状态已保存,企业微信成员范围将在后台自动重试同步')
|
||||
: ElMessage.success(value ? '医助已启用' : '医助已禁用并退出后续调度')
|
||||
: ElMessage.success('成员规则已保存,企微多人路由范围已重新计算')
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '成员状态更新失败')
|
||||
await loadOverview()
|
||||
@@ -727,9 +734,8 @@ function eligibility(row: any) {
|
||||
if (Number(row.active_start) > 0 && Number(row.active_start) > now) return { label: '尚未生效', className: 'is-waiting' }
|
||||
if (Number(row.active_end) > 0 && Number(row.active_end) < now) return { label: '已过期', className: 'is-error' }
|
||||
if (Number(row.daily_limit) > 0 && todayCount(row) >= Number(row.daily_limit)) return { label: '今日已达上限', className: 'is-waiting' }
|
||||
const isSyncTarget = Boolean(row.is_current || row.is_applied)
|
||||
if (isSyncTarget && Number(row.sync_status) === 3) return { label: '企微同步重试中', className: 'is-waiting' }
|
||||
if (isSyncTarget && Number(row.sync_status) === 4) return { label: '暂无可用调度', className: 'is-error' }
|
||||
if (Number(row.sync_status) === 3) return { label: '企微同步重试中', className: 'is-waiting' }
|
||||
if (Number(row.sync_status) === 4) return { label: '暂无可用成员', className: 'is-error' }
|
||||
return { label: '可参与分流', className: 'is-ok' }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user