Compare commits

...
4 Commits
Author SHA1 Message Date
Your Name af1db59c07 更新 2026-08-25 09:41:26 +08:00
Your Name 47094cc617 更新 2026-08-25 09:36:25 +08:00
Your Name 01c38d8c5b 更新 2026-08-25 09:35:47 +08:00
Your Name 1f3e580cf8 更新 2026-08-25 09:34:39 +08:00
363 changed files with 2298 additions and 770 deletions
+14 -6
View File
@@ -190,9 +190,9 @@ export function wecomPromotionOverview() {
return request.get({ url: '/firstvisit.wecomPromotion/overview' }) return request.get({ url: '/firstvisit.wecomPromotion/overview' })
} }
export function wecomPromotionSavePool(params: Record<string, unknown>) { export function wecomPromotionSavePool(params: Record<string, unknown>) {
return request.post({ url: '/firstvisit.wecomPromotion/savePool', params }) return request.post({ url: '/firstvisit.wecomPromotion/savePool', params, timeout: 120000 })
} }
export function wecomPromotionSaveWidget(params: Record<string, unknown>) { export function wecomPromotionSaveWidget(params: Record<string, unknown>) {
return request.post({ url: '/firstvisit.wecomPromotion/saveWidget', params }) return request.post({ url: '/firstvisit.wecomPromotion/saveWidget', params })
@@ -202,9 +202,17 @@ export function wecomPromotionDeletePool(params: { id: number }) {
return request.post({ url: '/firstvisit.wecomPromotion/deletePool', params }) return request.post({ url: '/firstvisit.wecomPromotion/deletePool', params })
} }
export function wecomPromotionSaveLink(params: Record<string, unknown>) { export function wecomPromotionSaveLink(params: Record<string, unknown>) {
return request.post({ url: '/firstvisit.wecomPromotion/saveLink', params }) return request.post({ url: '/firstvisit.wecomPromotion/saveLink', params })
} }
export function wecomPromotionSaveMember(params: Record<string, unknown>) {
return request.post({ url: '/firstvisit.wecomPromotion/saveMember', params, timeout: 120000 })
}
export function wecomPromotionToggleMember(params: { id: number; status: number }) {
return request.post({ url: '/firstvisit.wecomPromotion/toggleMember', params, timeout: 120000 })
}
export function wecomPromotionCheckApiPermission() { export function wecomPromotionCheckApiPermission() {
return request.post({ url: '/firstvisit.wecomPromotion/checkApiPermission' }) return request.post({ url: '/firstvisit.wecomPromotion/checkApiPermission' })
@@ -239,7 +239,7 @@
<div class="install-tip"> <div class="install-tip">
<el-icon><CircleCheck /></el-icon> <el-icon><CircleCheck /></el-icon>
<span><strong>无需暴露链接</strong>浮窗点击后仍由服务端执行可用性筛选权重随机和访问统计</span> <span><strong>一个方案一个官方链接</strong>浮窗会直接打开企业微信链接多名医助由企业微信执行均衡分流</span>
</div> </div>
</section> </section>
</section> </section>
@@ -274,6 +274,8 @@ interface PromotionPool {
name?: string name?: string
public_key?: string public_key?: string
script_url?: string script_url?: string
main_url?: string
compat_go_url?: string
go_url?: string go_url?: string
install_code?: string install_code?: string
trigger_code?: string trigger_code?: string
@@ -437,11 +439,12 @@ async function copySnippet(value: string | undefined, label: string) {
} }
function openTestLink() { function openTestLink() {
if (!props.pool.go_url) { const targetUrl = props.pool.main_url || props.pool.go_url || props.pool.compat_go_url
if (!targetUrl) {
ElMessage.warning('当前方案暂无测试链接') ElMessage.warning('当前方案暂无测试链接')
return return
} }
window.open(props.pool.go_url, '_blank', 'noopener,noreferrer') window.open(targetUrl, '_blank', 'noopener,noreferrer')
} }
</script> </script>
@@ -5,7 +5,7 @@
<span class="heading-icon"><el-icon><Promotion /></el-icon></span> <span class="heading-icon"><el-icon><Promotion /></el-icon></span>
<div> <div>
<h1>企业微信获客助手</h1> <h1>企业微信获客助手</h1>
<p>对接企业微信获客助手官方 API按角色与部门管理获客成员并将链接安全分流到推广页面</p> <p>一个方案保留一条企业微信官方直链按回调结果自动调度医助权重与数量</p>
</div> </div>
</div> </div>
<div class="heading-actions"> <div class="heading-actions">
@@ -22,15 +22,15 @@
</article> </article>
<article class="metric-card"> <article class="metric-card">
<span class="metric-icon is-blue"><el-icon><SetUp /></el-icon></span> <span class="metric-icon is-blue"><el-icon><SetUp /></el-icon></span>
<div><small>分流方案</small><strong>{{ overview.summary.pool_count }}</strong><p>每个方案生成独立 JS</p></div> <div><small>分流方案</small><strong>{{ overview.summary.pool_count }}</strong><p>每个方案对应一个官方链接</p></div>
</article> </article>
<article class="metric-card"> <article class="metric-card">
<span class="metric-icon is-green"><el-icon><Link /></el-icon></span> <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>
<article class="metric-card"> <article class="metric-card">
<span class="metric-icon is-orange"><el-icon><Mouse /></el-icon></span> <span class="metric-icon is-orange"><el-icon><Mouse /></el-icon></span>
<div><small>今日分流点击</small><strong>{{ overview.summary.today_clicks }}</strong><p>只记录脱敏访问数据</p></div> <div><small>今日实际获客</small><strong>{{ overview.summary.today_clicks }}</strong><p>按企业微信回调确认承接成员</p></div>
</article> </article>
</section> </section>
@@ -52,9 +52,9 @@
<div v-if="activeTab === 'links'" class="tab-content links-tab"> <div v-if="activeTab === 'links'" class="tab-content links-tab">
<div class="section-heading"> <div class="section-heading">
<div> <div>
<h2>获客助手链接分流</h2> <h2>获客助手分流方案</h2>
<p>访客点击时由服务端从当前可用的获客助手链接中按权重随机选择页面端不会暴露完整链接</p> <p>选择几名医助就生成几条成员规则官方链接只在方案顶部复制成员行不重复展示链接</p>
</div> </div>
<el-button type="primary" :icon="Plus" @click="openPoolDialog()">新建分流方案</el-button> <el-button type="primary" :icon="Plus" @click="openPoolDialog()">新建分流方案</el-button>
</div> </div>
@@ -69,7 +69,7 @@
@click="selectedPoolId = Number(pool.id)" @click="selectedPoolId = Number(pool.id)"
> >
<span class="pool-status" :class="Number(pool.status) === 1 ? 'online' : 'offline'" /> <span class="pool-status" :class="Number(pool.status) === 1 ? 'online' : 'offline'" />
<span><strong>{{ pool.name }}</strong><small>{{ linkCount(pool.id) }} 条链接 · {{ formatNumber(pool.click_count) }} 次点击</small></span> <span><strong>{{ pool.name }}</strong><small>{{ memberSummary(pool) }} · {{ pool.main_url ? '官方链接已生成' : '待生成链接' }}</small></span>
<el-icon><ArrowRight /></el-icon> <el-icon><ArrowRight /></el-icon>
</button> </button>
</aside> </aside>
@@ -84,100 +84,77 @@
</span> </span>
<span class="owner-tag">{{ selectedPool.dept_name || '未分部门' }} · {{ selectedPool.owner_name || '系统' }}</span> <span class="owner-tag">{{ selectedPool.dept_name || '未分部门' }} · {{ selectedPool.owner_name || '系统' }}</span>
</div> </div>
<p>公开键{{ selectedPool.public_key }}</p> <p>公开键{{ selectedPool.public_key }}</p>
</div> </div>
<div class="toolbar-actions"> <div class="toolbar-actions">
<el-button :icon="CircleCheck" :loading="checkingApi" @click="checkApiPermission">验证 API</el-button> <el-button :icon="CircleCheck" :loading="checkingApi" @click="checkApiPermission">验证 API</el-button>
<el-button :icon="Refresh" :loading="syncingRemote" @click="syncRemoteLinks">同步企业微信</el-button> <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="DocumentCopy" @click="copyText(selectedPool.install_code, 'JS 安装代码')">复制 JS</el-button>
<el-button :icon="Edit" @click="openPoolDialog(selectedPool)">编辑方案</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" @click="removePool(selectedPool)">删除</el-button>
<el-button type="primary" :icon="Plus" @click="openLinkDialog()">创建官方获客链接</el-button> </div>
</div> </div>
</div>
<el-alert
<el-alert v-if="selectedPool.migration_state === 'needs_resolution'"
v-if="selectedLegacyCount > 0 && selectedOfficialCount === 0" class="legacy-sync-alert"
class="legacy-sync-alert" type="warning"
type="warning" show-icon
show-icon :closable="false"
:closable="false" title="检测到旧版方案存在多个官方链接"
:title="`当前方案的 ${selectedLegacyCount} 条链接都是历史手工链接,企业微信同步不会返回它们`" description="当前显示最新链接;保存方案后,其余旧链接只会在本地下线,企业微信端与历史客户归因仍会保留。"
description="官方接口只返回当前应用通过 API 创建的获客链接。现有链接仍可参与本地分流;如需同步 link_id、客户和官方统计,请点击“创建官方获客链接”。" />
/>
<el-alert
<el-table :data="selectedLinks" class="link-table" stripe> class="legacy-sync-alert"
<el-table-column label="推广成员 / 分组" min-width="180" fixed="left"> type="info"
<template #default="{ row }"> show-icon
<div class="member-cell"><strong>{{ row.name }}</strong><small>{{ row.group_name || '默认分组' }}</small></div> :closable="false"
</template> title="回调驱动分流"
</el-table-column> description="企业微信确认实际承接成员后,系统累计今日数量,并在符合启用状态、有效期和上限的成员中按权重随机抽取下一位;随机结果可能连续命中同一人,并发访问及企微同步存在短暂延迟。"
<el-table-column label="企业微信链接 ID" min-width="280"> />
<template #default="{ row }">
<div class="link-id-cell"> <el-table :data="selectedMemberRules" class="link-table" stripe>
<span v-if="row.remote_link_id" class="remote-id" :title="row.remote_link_id">{{ row.remote_link_id }}</span> <el-table-column label="推广成员" min-width="210" fixed="left">
<span v-else class="legacy-tag">历史手工链接</span> <template #default="{ row }">
<a <div class="member-cell">
v-if="row.wecom_url" <strong>{{ row.name || row.userid }}</strong>
class="wecom-url" <small>{{ memberRuleDetail(row) }}</small>
:href="row.wecom_url" </div>
:title="row.wecom_url" </template>
target="_blank" </el-table-column>
rel="noopener noreferrer" <el-table-column label="调度" width="105" align="center">
@click.stop <template #default="{ row }">
>{{ row.wecom_url }}</a> <span v-if="row.is_current" class="status-tag is-online">{{ Number(row.sync_status) === 0 ? '当前命中' : '待切换' }}</span>
<el-button <span v-else-if="row.is_applied" class="status-tag is-offline">切换中</span>
v-if="row.wecom_url" <span v-else class="muted">候选</span>
type="primary" </template>
link </el-table-column>
class="copy-url-btn" <el-table-column label="权重" prop="weight" width="85" align="center" />
@click.stop="copyText(row.wecom_url, '企业微信链接')" <el-table-column label="今日 / 上限" width="125" align="center">
>复制链接</el-button> <template #default="{ row }">{{ todayCount(row) }} / {{ Number(row.daily_limit) ? row.daily_limit : '不限' }}</template>
<span v-else class="muted">暂无链接地址</span> </el-table-column>
</div> <el-table-column label="累计获客" prop="total_count" width="105" align="center" />
</template> <el-table-column label="当前可用性" min-width="130">
</el-table-column> <template #default="{ row }">
<el-table-column label="可用成员" min-width="150"> <span class="availability" :class="eligibility(row).className">{{ eligibility(row).label }}</span>
<template #default="{ row }"> </template>
<span>{{ remoteMemberLabel(row) }}</span> </el-table-column>
</template> <el-table-column label="上线" width="90" align="center">
</el-table-column> <template #default="{ row }">
<el-table-column label="权重" prop="weight" width="84" align="center" /> <el-switch :model-value="Number(row.enabled) === 1" :loading="togglingMemberId === Number(row.id)" @change="(value) => handleMemberToggle(row, value)" />
<el-table-column label="今日 / 上限" min-width="120" align="center"> </template>
<template #default="{ row }">{{ todayCount(row) }} / {{ Number(row.daily_limit) ? row.daily_limit : '不限' }}</template> </el-table-column>
</el-table-column> <el-table-column label="操作" min-width="100" fixed="right">
<el-table-column label="累计点击" prop="click_count" min-width="105" align="center" /> <template #default="{ row }">
<el-table-column label="当前可用性" min-width="130"> <el-button type="primary" link @click="openMemberDialog(row)">编辑规则</el-button>
<template #default="{ row }"> </template>
<span class="availability" :class="eligibility(row).className">{{ eligibility(row).label }}</span> </el-table-column>
</template> <template #empty><el-empty :image-size="72" description="编辑方案并选择获客医助" /></template>
</el-table-column> </el-table>
<el-table-column label="上线" width="90" align="center">
<template #default="{ row }">
<el-switch :model-value="Number(row.status) === 1" :disabled="Number(row.remote_status) === 2" @change="(value) => handleToggle(row, value)" />
</template>
</el-table-column>
<el-table-column label="操作" min-width="245" fixed="right">
<template #default="{ row }">
<el-button v-if="row.is_official && Number(row.remote_status) !== 2" type="primary" link :loading="refreshingLinkId === Number(row.id)" @click="refreshRemoteDetail(row)">刷新详情</el-button>
<el-button v-if="!row.is_official || Number(row.remote_status) !== 2" type="primary" link @click="openLinkDialog(row)">编辑</el-button>
<el-dropdown v-if="row.is_official" trigger="click" @command="(command) => handleLinkCommand(command, row)">
<el-button type="primary" link>更多<el-icon class="el-icon--right"><ArrowDown /></el-icon></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="local">从本地移除</el-dropdown-item>
<el-dropdown-item v-if="Number(row.remote_status) !== 2" command="remote" divided class="danger-command">永久删除企业微信链接</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button v-else type="danger" link @click="removeLink(row)">从本地移除</el-button>
</template>
</el-table-column>
<template #empty><el-empty :image-size="72" description="当前方案还没有获客助手链接" /></template>
</el-table>
</div> </div>
</div> </div>
<el-empty v-else description="创建一个分流方案,再添加企业微信获客助手链接"> <el-empty v-else description="创建方案并选择多个医助,保存后自动生成一个企业微信官方获客链接">
<el-button type="primary" :icon="Plus" @click="openPoolDialog()">创建第一个方案</el-button> <el-button type="primary" :icon="Plus" @click="openPoolDialog()">创建第一个方案</el-button>
</el-empty> </el-empty>
</div> </div>
@@ -220,7 +197,7 @@
show-icon show-icon
:closable="false" :closable="false"
title="消息次数回调尚未配置完成" title="消息次数回调尚未配置完成"
description="复用企业微信自建应用现有的“API 接收消息”地址即可,无需新增第二个回调;请确保该地址为 QywxExternalContactCallback/notify,且服务器 Token、AESKey 与企微后台一致。未完成前无法得到精确接收消息次数。" description="复用企业微信自建应用现有的“API 接收消息”地址即可,无需新增第二个回调;请确保该地址为 /api/qywx/external-contact/notify,且服务器 Token、AESKey 与企微后台一致。未完成前无法自动累计成员获客数量和切换下一位。"
/> />
<el-alert <el-alert
@@ -363,75 +340,89 @@
</div> </div>
</section> </section>
<el-dialog v-model="poolDialogVisible" :title="poolForm.id ? '编辑分流方案' : '新建分流方案'" width="560px" destroy-on-close> <el-dialog
<el-form label-position="top"> v-model="poolDialogVisible"
<el-form-item label="方案名称" required><el-input v-model="poolForm.name" maxlength="60" show-word-limit placeholder="例如:官网咨询分流" /></el-form-item> :title="poolForm.id ? '编辑分流方案' : '新建分流方案'"
<el-form-item label="兜底获客助手链接"> width="680px"
<el-input v-model="poolForm.fallback_url" placeholder="可选;没有可用成员时跳转" /> destroy-on-close
<span class="form-tip">仅接受企业微信获客助手生成的 https://work.weixin.qq.com/ca/... 链接。</span> :close-on-click-modal="false"
</el-form-item> :close-on-press-escape="!savingPool"
<el-form-item label="运行状态"><el-switch v-model="poolForm.status" :active-value="1" :inactive-value="0" active-text="运行" inactive-text="停用" /></el-form-item> >
</el-form> <el-form label-position="top">
<template #footer><el-button @click="poolDialogVisible = false">取消</el-button><el-button type="primary" :loading="savingPool" @click="savePool">保存方案</el-button></template> <el-form-item label="方案名称" required><el-input v-model="poolForm.name" :disabled="savingPool" maxlength="60" show-word-limit placeholder="例如:官网咨询分流" /></el-form-item>
</el-dialog> <el-form-item label="获客医助" required>
<el-select
<el-dialog v-model="linkDialogVisible" :title="linkForm.id ? '编辑获客助手链接' : '创建企业微信官方获客链接'" width="720px" destroy-on-close> v-model="poolForm.member_admin_ids"
<el-form label-position="top" class="link-form"> :disabled="savingPool"
<div class="form-grid"> multiple
<el-form-item label="获客链接名称" required><el-input v-model="linkForm.name" maxlength="80" placeholder="用于企业微信和后台识别" /></el-form-item> filterable
<el-form-item label="分组"><el-input v-model="linkForm.group_name" maxlength="60" placeholder="默认分组" /></el-form-item> collapse-tags
</div> collapse-tags-tooltip
<el-form-item v-if="!linkForm.legacy" label="获客成员" required> placeholder="选择后由企业微信生成一个多人分流链接"
<el-select v-model="linkForm.member_admin_ids" multiple filterable collapse-tags collapse-tags-tooltip placeholder="仅显示当前角色和部门可管理、且已绑定企业微信的成员" style="width: 100%"> style="width: 100%"
<el-option v-for="member in overview.member_options" :key="member.id" :label="memberOptionLabel(member)" :value="Number(member.id)" /> >
</el-select> <el-option v-for="member in overview.member_options" :key="member.id" :label="memberOptionLabel(member)" :value="Number(member.id)" />
<span class="form-tip">官方链接按这里的企业微信 userid 建立可见范围越权成员不会出现在列表中</span> </el-select>
</el-form-item> <span class="form-tip">保存后会为每名医助生成一条成员规则官方链接地址始终只有一个</span>
<el-form-item v-else label="企业微信获客助手链接" required> </el-form-item>
<el-input v-model="linkForm.wecom_url" type="textarea" :rows="2" :placeholder="overview.customer_acquisition_link_example" /> <el-form-item label="添加客户时跳过验证">
<span class="form-tip">这是历史手工链接只更新本地分流规则不会调用企业微信创建或修改接口</span> <el-switch v-model="poolForm.skip_verify" :disabled="savingPool" :active-value="1" :inactive-value="0" active-text="跳过验证" inactive-text="需要验证" />
</el-form-item> </el-form-item>
<div class="form-grid"> <el-form-item label="兜底获客助手链接">
<el-form-item label="分流权重" required><el-input-number v-model="linkForm.weight" :min="1" :max="100" controls-position="right" /></el-form-item> <el-input v-model="poolForm.fallback_url" :disabled="savingPool" placeholder="可选;仅供旧版兼容跳转使用" />
<el-form-item label="每日点击上限"><el-input-number v-model="linkForm.daily_limit" :min="0" :max="1000000" controls-position="right" /><span class="form-tip">0 表示不限</span></el-form-item> <span class="form-tip">新生成的官方直链不经过本站跳转此项仅兼容旧安装代码</span>
</div> </el-form-item>
<el-form-item v-if="!linkForm.legacy" label="添加客户时跳过验证"> <el-form-item label="运行状态"><el-switch v-model="poolForm.status" :disabled="savingPool" :active-value="1" :inactive-value="0" active-text="运行" inactive-text="停用" /></el-form-item>
<el-switch v-model="linkForm.skip_verify" :active-value="1" :inactive-value="0" active-text="跳过验证" inactive-text="需要验证" /> </el-form>
</el-form-item> <template #footer><el-button :disabled="savingPool" @click="poolDialogVisible = false">取消</el-button><el-button type="primary" :loading="savingPool" @click="savePool">{{ poolForm.id ? '保存方案' : '保存并生成链接' }}</el-button></template>
<el-form-item label="上线状态"><el-switch v-model="linkForm.status" :active-value="1" :inactive-value="0" active-text="上线" inactive-text="下线" /></el-form-item> </el-dialog>
<el-form-item label="生效时间段">
<el-date-picker v-model="linkForm.active_range" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" start-placeholder="开始时间" end-placeholder="结束时间" range-separator="至" /> <el-dialog v-model="memberDialogVisible" title="编辑医助分流规则" width="560px" destroy-on-close :close-on-click-modal="false">
<span class="form-tip">不选择表示长期有效</span> <el-form label-position="top">
</el-form-item> <el-form-item label="获客医助">
<el-form-item label="备注"><el-input v-model="linkForm.remark" maxlength="255" show-word-limit /></el-form-item> <el-input :model-value="memberForm.name ? `${memberForm.name} · ${memberForm.userid}` : memberForm.userid" readonly />
</el-form> </el-form-item>
<template #footer><el-button @click="linkDialogVisible = false">取消</el-button><el-button type="primary" :loading="savingLink" @click="saveLink">保存链接</el-button></template> <div class="rule-form-grid">
</el-dialog> <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-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>
<el-form-item label="上线状态">
<el-switch v-model="memberForm.status" :active-value="1" :inactive-value="0" active-text="启用" inactive-text="禁用" :disabled="savingMember" />
</el-form-item>
<el-form-item label="备注"><el-input v-model="memberForm.remark" type="textarea" :rows="2" maxlength="255" show-word-limit :disabled="savingMember" /></el-form-item>
</el-form>
<template #footer><el-button :disabled="savingMember" @click="memberDialogVisible = false">取消</el-button><el-button type="primary" :loading="savingMember" @click="saveMemberRule">保存规则</el-button></template>
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts" name="firstVisitWecomPromotionPage"> <script setup lang="ts" name="firstVisitWecomPromotionPage">
import { computed, onMounted, reactive, ref, watch } from 'vue' import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { import {
ArrowDown, ArrowRight, ChatDotRound, CircleCheck, Connection, DataAnalysis, Delete, ArrowRight, ChatDotRound, CircleCheck, Connection, DataAnalysis, Delete,
DocumentCopy, Edit, Key, Link, Lock, Mouse, OfficeBuilding, Plus, DocumentCopy, Edit, Key, Link, Lock, Mouse, OfficeBuilding, Plus,
Promotion, Refresh, Search, SetUp, User, Warning Promotion, Refresh, Search, SetUp, User, Warning
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import { import {
wecomPromotionCheckApiPermission, wecomPromotionCheckApiPermission,
wecomPromotionCustomerStats, wecomPromotionCustomerStats,
wecomPromotionDeleteLink, wecomPromotionDeletePool,
wecomPromotionDeleteRemoteLink, wecomPromotionOverview,
wecomPromotionDeletePool, wecomPromotionSaveMember,
wecomPromotionOverview, wecomPromotionSavePool,
wecomPromotionRemoteLinkDetail, wecomPromotionSyncCustomers,
wecomPromotionSaveLink, wecomPromotionToggleMember
wecomPromotionSavePool, } from '@/api/first_visit'
wecomPromotionSyncCustomers,
wecomPromotionSyncRemoteLinks,
wecomPromotionToggleLink
} from '@/api/first_visit'
import type { WecomPromotionCustomerChatStatus } from '@/api/first_visit' import type { WecomPromotionCustomerChatStatus } from '@/api/first_visit'
import WecomFloatingWidgetBuilder from './components/WecomFloatingWidgetBuilder.vue' import WecomFloatingWidgetBuilder from './components/WecomFloatingWidgetBuilder.vue'
@@ -447,17 +438,16 @@ const emptyOverview = () => ({
const overview = reactive(emptyOverview()) const overview = reactive(emptyOverview())
const loading = ref(false) const loading = ref(false)
const activeTab = ref<TabName>('links') const activeTab = ref<TabName>('links')
const selectedPoolId = ref<number>() const selectedPoolId = ref<number>()
const selectedInstallPoolId = ref<number>() const selectedInstallPoolId = ref<number>()
const poolDialogVisible = ref(false) const poolDialogVisible = ref(false)
const linkDialogVisible = ref(false) const savingPool = ref(false)
const savingPool = ref(false) const checkingApi = ref(false)
const savingLink = ref(false) const memberDialogVisible = ref(false)
const checkingApi = ref(false) const savingMember = ref(false)
const syncingRemote = ref(false) const togglingMemberId = ref(0)
const refreshingLinkId = ref(0) const poolForm = reactive({ id: 0, name: '', fallback_url: '', status: 1, member_admin_ids: [] as number[], skip_verify: 0, main_url: '' })
const poolForm = reactive({ id: 0, name: '', fallback_url: '', status: 1 }) const memberForm = reactive({ id: 0, name: '', userid: '', weight: 1, daily_limit: 0, status: 1, active_range: [] as string[], remark: '' })
const linkForm = reactive({ id: 0, pool_id: 0, name: '', group_name: '默认分组', wecom_url: '', member_admin_ids: [] as number[], skip_verify: 0, legacy: false, weight: 1, status: 1, daily_limit: 0, active_range: [] as string[], remark: '' })
const customerStatsLoading = ref(false) const customerStatsLoading = ref(false)
const customerStatsLoaded = ref(false) const customerStatsLoaded = ref(false)
const syncingCustomers = ref(false) const syncingCustomers = ref(false)
@@ -475,10 +465,8 @@ const customerStats = reactive({
meta: { last_synced_at: '' } meta: { last_synced_at: '' }
}) })
const selectedPool = computed(() => overview.pools.find((item: any) => Number(item.id) === selectedPoolId.value)) const selectedPool = computed(() => overview.pools.find((item: any) => Number(item.id) === selectedPoolId.value))
const selectedLinks = computed(() => overview.links.filter((item: any) => Number(item.pool_id) === selectedPoolId.value)) const selectedMemberRules = computed(() => Array.isArray(selectedPool.value?.member_rules) ? selectedPool.value.member_rules : [])
const selectedOfficialCount = computed(() => selectedLinks.value.filter((item: any) => Boolean(item.remote_link_id)).length)
const selectedLegacyCount = computed(() => selectedLinks.value.length - selectedOfficialCount.value)
const selectedInstallPool = computed(() => overview.pools.find((item: any) => Number(item.id) === selectedInstallPoolId.value)) const selectedInstallPool = computed(() => overview.pools.find((item: any) => Number(item.id) === selectedInstallPoolId.value))
const customerLinkOptions = computed(() => customerStats.link_options.length const customerLinkOptions = computed(() => customerStats.link_options.length
? customerStats.link_options ? customerStats.link_options
@@ -516,24 +504,37 @@ async function loadOverview() {
} }
} }
function linkCount(poolId: number) { function openPoolDialog(pool?: any) {
return overview.links.filter((item: any) => Number(item.pool_id) === Number(poolId)).length Object.assign(poolForm, pool ? {
} id: Number(pool.id),
name: pool.name,
function openPoolDialog(pool?: any) { fallback_url: pool.fallback_url || '',
Object.assign(poolForm, pool ? { id: Number(pool.id), name: pool.name, fallback_url: pool.fallback_url || '', status: Number(pool.status) } : { id: 0, name: '', fallback_url: '', status: 1 }) status: Number(pool.status),
poolDialogVisible.value = true member_admin_ids: Array.isArray(pool.member_admin_ids) ? pool.member_admin_ids.map(Number) : [],
} skip_verify: Number(pool.skip_verify) === 1 ? 1 : 0,
main_url: pool.main_url || ''
async function savePool() { } : {
if (!poolForm.name.trim()) return ElMessage.warning('请输入分流方案名称') id: 0,
savingPool.value = true name: '',
fallback_url: '',
status: 1,
member_admin_ids: [],
skip_verify: 0,
main_url: ''
})
poolDialogVisible.value = true
}
async function savePool() {
if (!poolForm.name.trim()) return ElMessage.warning('请输入分流方案名称')
if (!poolForm.member_admin_ids.length) return ElMessage.warning('请至少选择一名获客医助')
savingPool.value = true
try { try {
const result: any = await wecomPromotionSavePool({ ...poolForm }) const result: any = await wecomPromotionSavePool({ ...poolForm })
poolDialogVisible.value = false poolDialogVisible.value = false
await loadOverview() await loadOverview()
if (result?.id) selectedPoolId.value = Number(result.id) if (result?.id) selectedPoolId.value = Number(result.id)
ElMessage.success('分流方案已保存') ElMessage.success(poolForm.id ? '分流方案已保存' : '分流方案和官方获客链接已创建')
} catch (error: any) { } catch (error: any) {
ElMessage.error(error?.message || '分流方案保存失败') ElMessage.error(error?.message || '分流方案保存失败')
} finally { } finally {
@@ -541,8 +542,8 @@ async function savePool() {
} }
} }
async function removePool(pool: any) { async function removePool(pool: any) {
await ElMessageBox.confirm(`删除“${pool.name}”后,该方案下的获客助手链接也会停用。确认继续?`, '删除分流方案', { type: 'warning' }) await ElMessageBox.confirm(`删除“${pool.name}”后,本站将停止使用该链接,但不会永久删除企业微信后台中的官方链接。确认继续?`, '删除分流方案', { type: 'warning' })
try { try {
await wecomPromotionDeletePool({ id: Number(pool.id) }) await wecomPromotionDeletePool({ id: Number(pool.id) })
ElMessage.success('分流方案已删除') ElMessage.success('分流方案已删除')
@@ -550,50 +551,60 @@ async function removePool(pool: any) {
} catch (error: any) { } catch (error: any) {
ElMessage.error(error?.message || '删除失败') ElMessage.error(error?.message || '删除失败')
} }
} }
function openLinkDialog(row?: any) { function openMemberDialog(row: any) {
if (!selectedPool.value) return const range = Number(row.active_start) > 0 && Number(row.active_end) > 0
const range = row && Number(row.active_start) > 0 && Number(row.active_end) > 0 ? [formatPickerTime(row.active_start), formatPickerTime(row.active_end)]
? [formatPickerTime(row.active_start), formatPickerTime(row.active_end)] : [] : []
const selectedUserIds = new Set<string>((row?.range_userids || []).map((item: unknown) => String(item))) Object.assign(memberForm, {
const selectedMemberIds = overview.member_options id: Number(row.id),
.filter((member: any) => selectedUserIds.has(String(member.userid))) name: row.name || '',
.map((member: any) => Number(member.id)) userid: row.userid || '',
Object.assign(linkForm, row ? { weight: Number(row.weight) || 1,
id: Number(row.id), pool_id: Number(row.pool_id), daily_limit: Number(row.daily_limit) || 0,
name: row.name, group_name: row.group_name || '默认分组', wecom_url: row.wecom_url, status: Number(row.enabled) === 1 ? 1 : 0,
member_admin_ids: selectedMemberIds, skip_verify: Number(row.skip_verify) === 1 ? 1 : 0, legacy: !row.is_official, active_range: range,
weight: Number(row.weight) || 1, status: Number(row.status), daily_limit: Number(row.daily_limit) || 0, remark: row.remark || ''
active_range: range, remark: row.remark || '' })
} : { memberDialogVisible.value = true
id: 0, pool_id: Number(selectedPool.value.id), name: '', group_name: '默认分组', }
wecom_url: '', member_admin_ids: [], skip_verify: 0, legacy: false,
weight: 1, status: 1, daily_limit: 0, active_range: [], remark: '' async function saveMemberRule() {
}) savingMember.value = true
linkDialogVisible.value = true try {
} const result: any = await wecomPromotionSaveMember({
...memberForm,
async function saveLink() { active_start: memberForm.active_range?.[0] || '',
if (!linkForm.name.trim()) return ElMessage.warning('请输入获客链接名称') active_end: memberForm.active_range?.[1] || ''
if (linkForm.legacy && !linkForm.wecom_url.trim()) return ElMessage.warning('请填写企业微信获客助手链接') })
if (!linkForm.legacy && !linkForm.member_admin_ids.length) return ElMessage.warning('请至少选择一名获客成员') memberDialogVisible.value = false
savingLink.value = true await loadOverview()
try { result?.sync_error
await wecomPromotionSaveLink({ ? ElMessage.warning('规则已保存,企业微信成员范围将在后台自动重试同步')
...linkForm, : ElMessage.success('医助分流规则已保存')
active_start: linkForm.active_range?.[0] || '', } catch (error: any) {
active_end: linkForm.active_range?.[1] || '' ElMessage.error(error?.message || '医助分流规则保存失败')
}) } finally {
linkDialogVisible.value = false savingMember.value = false
await loadOverview() }
ElMessage.success(linkForm.legacy ? '本地分流规则已保存' : '企业微信官方获客链接已保存') }
} catch (error: any) {
ElMessage.error(error?.message || '获客助手链接保存失败') async function handleMemberToggle(row: any, value: unknown) {
} finally { togglingMemberId.value = Number(row.id)
savingLink.value = false try {
} const result: any = await wecomPromotionToggleMember({ id: Number(row.id), status: value ? 1 : 0 })
} await loadOverview()
result?.sync_error
? ElMessage.warning('成员状态已保存,企业微信成员范围将在后台自动重试同步')
: ElMessage.success(value ? '医助已启用' : '医助已禁用并退出后续调度')
} catch (error: any) {
ElMessage.error(error?.message || '成员状态更新失败')
await loadOverview()
} finally {
togglingMemberId.value = 0
}
}
async function checkApiPermission() { async function checkApiPermission() {
checkingApi.value = true checkingApi.value = true
@@ -710,119 +721,39 @@ async function syncCustomers() {
} }
} }
async function syncRemoteLinks() { function eligibility(row: any) {
if (!selectedPool.value) return ElMessage.warning('请先选择分流方案') const now = Math.floor(Date.now() / 1000)
syncingRemote.value = true if (Number(row.enabled) !== 1) return { label: '已禁用', className: 'is-muted' }
try { if (Number(row.active_start) > 0 && Number(row.active_start) > now) return { label: '尚未生效', className: 'is-waiting' }
const result: any = await wecomPromotionSyncRemoteLinks({ pool_id: Number(selectedPool.value.id) })
const message = `扫描 ${Number(result?.scanned || 0)} 条,新建 ${Number(result?.created || 0)} 条,更新 ${Number(result?.updated || 0)} 条,按权限跳过 ${Number(result?.skipped || 0)} 条,失败 ${Number(result?.failed || 0)}`
if (Number(result?.scanned || 0) === 0) {
await ElMessageBox.alert(
`${result?.empty_reason || '当前应用没有可同步的官方获客链接'}\n\n${result?.suggestion || ''}`,
'没有可同步的官方链接',
{ type: 'warning', confirmButtonText: '知道了' }
)
} else {
result?.failed ? ElMessage.warning(message) : ElMessage.success(message)
}
await loadOverview()
} catch (error: any) {
ElMessage.error(error?.message || '同步企业微信获客链接失败')
} finally {
syncingRemote.value = false
}
}
async function refreshRemoteDetail(row: any) {
refreshingLinkId.value = Number(row.id)
try {
await wecomPromotionRemoteLinkDetail({ id: Number(row.id) })
ElMessage.success('企业微信官方详情已刷新')
await loadOverview()
} catch (error: any) {
ElMessage.error(error?.message || '刷新官方详情失败')
} finally {
refreshingLinkId.value = 0
}
}
async function handleLinkCommand(command: string, row: any) {
if (command === 'local') {
await removeLink(row)
return
}
if (command !== 'remote') return
await ElMessageBox.confirm(
`将永久删除企业微信后台中的“${row.name}”,现有推广页面会立即失效,且无法恢复。确认继续?`,
'永久删除企业微信获客链接',
{ type: 'error', confirmButtonText: '永久删除', cancelButtonText: '取消' }
)
try {
await wecomPromotionDeleteRemoteLink({ id: Number(row.id) })
ElMessage.success('企业微信获客链接已永久删除,本地记录已保留并停止分流')
await loadOverview()
} catch (error: any) {
ElMessage.error(error?.message || '永久删除失败')
}
}
async function handleToggle(row: any, value: unknown) {
try {
await wecomPromotionToggleLink({ id: Number(row.id), status: value ? 1 : 0 })
row.status = value ? 1 : 0
ElMessage.success(value ? '链接已上线' : '链接已下线')
await loadOverview()
} catch (error: any) {
ElMessage.error(error?.message || '状态更新失败')
await loadOverview()
}
}
async function removeLink(row: any) {
await ElMessageBox.confirm(`仅从本地分流池移除“${row.name}”,不会删除企业微信后台的官方链接。确认继续?`, '从本地移除', { type: 'warning' })
try {
await wecomPromotionDeleteLink({ id: Number(row.id) })
ElMessage.success('已从本地分流池移除')
await loadOverview()
} catch (error: any) {
ElMessage.error(error?.message || '删除失败')
}
}
function eligibility(row: any) {
const now = Math.floor(Date.now() / 1000)
if (Number(row.remote_status) === 2) return { label: '企业微信已删除', className: 'is-error' }
if (row.valid_customer_acquisition_link === false) return { label: '非获客助手链接', className: 'is-error' }
if (Number(row.status) !== 1) return { label: '已下线', className: 'is-muted' }
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.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' } if (Number(row.daily_limit) > 0 && todayCount(row) >= Number(row.daily_limit)) return { label: '今日已达上限', className: 'is-waiting' }
return { label: '可参与分流', className: 'is-ok' } 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' }
return { label: '可参与分流', className: 'is-ok' }
}
function todayCount(row: any) { function todayCount(row: any) {
const today = new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date()) const today = new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date())
return row.today_date === today ? Number(row.today_count || 0) : 0 return row.today_date === today ? Number(row.today_count || 0) : 0
} }
function memberOptionLabel(member: any) { function memberOptionLabel(member: any) {
const departments = Array.isArray(member.dept_names) && member.dept_names.length const departments = Array.isArray(member.dept_names) && member.dept_names.length
? member.dept_names.join(' / ') ? member.dept_names.join(' / ')
: '未分部门' : '未分部门'
return `${member.name} · ${departments} · ${member.userid}` return `${member.name} · ${departments} · ${member.userid}`
} }
function memberSummary(pool: any) {
const ids = Array.isArray(pool?.member_admin_ids) ? pool.member_admin_ids : []
return ids.length ? `${ids.length} 名医助` : '未选择医助'
}
function remoteMemberLabel(row: any) { function memberRuleDetail(row: any) {
const userIds = Array.isArray(row.range_userids) ? row.range_userids : [] const departments = Array.isArray(row.dept_names) && row.dept_names.length ? row.dept_names.join(' / ') : '未分部门'
const names = userIds.map((userid: unknown) => { return `${departments} · ${row.userid || '未绑定 userid'}`
const member = overview.member_options.find((item: any) => String(item.userid) === String(userid)) }
return member?.name || String(userid)
})
if (names.length) return names.length > 2 ? `${names.slice(0, 2).join('、')}${names.length}` : names.join('、')
const departments = Array.isArray(row.range_department_ids) ? row.range_department_ids : []
if (departments.length) return `企微部门 ${departments.join('、')}`
return row.is_official ? '未返回成员范围' : '手工配置'
}
function memberFilterLabel(member: any) { function memberFilterLabel(member: any) {
const department = member.department_name || member.dept_name || '' const department = member.department_name || member.dept_name || ''
@@ -909,17 +840,17 @@ function formatCustomerRate(value: unknown) {
return `${Number(value || 0).toFixed(1)}%` return `${Number(value || 0).toFixed(1)}%`
} }
function formatNumber(value: unknown) { function formatNumber(value: unknown) {
return Number(value || 0).toLocaleString('zh-CN') return Number(value || 0).toLocaleString('zh-CN')
} }
function formatPickerTime(value: unknown) {
const date = new Date(Number(value || 0) * 1000)
const pad = (number: number) => String(number).padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
function formatPickerTime(value: unknown) { async function copyText(value: string, label: string) {
const date = new Date(Number(value || 0) * 1000)
const pad = (number: number) => String(number).padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
async function copyText(value: string, label: string) {
if (!value) return ElMessage.warning(`${label}暂无内容`) if (!value) return ElMessage.warning(`${label}暂无内容`)
try { try {
await navigator.clipboard.writeText(value) await navigator.clipboard.writeText(value)
@@ -930,8 +861,9 @@ async function copyText(value: string, label: string) {
textarea.style.opacity = '0' textarea.style.opacity = '0'
document.body.appendChild(textarea) document.body.appendChild(textarea)
textarea.select() textarea.select()
document.execCommand('copy') const copied = document.execCommand('copy')
textarea.remove() textarea.remove()
if (!copied) throw new Error('copy command failed')
} }
ElMessage.success(`${label}已复制`) ElMessage.success(`${label}已复制`)
} }
@@ -1010,7 +942,7 @@ h1, h2, h3, p { margin: 0; }
.callback-list { margin: 14px 0 0; border-top: 1px solid rgba(124,150,157,.16); }.callback-list > div { display: grid; grid-template-columns: 120px 1fr 50px; align-items: center; min-height: 40px; border-bottom: 1px solid rgba(124,150,157,.12); font-size: 11px; }.callback-list dt { color: #657488; }.callback-list dd { overflow: hidden; margin: 0; color: #27374c; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; text-overflow: ellipsis; white-space: nowrap; }.callback-list button { border: 0; color: #148f83; background: transparent; cursor: pointer; } .callback-list { margin: 14px 0 0; border-top: 1px solid rgba(124,150,157,.16); }.callback-list > div { display: grid; grid-template-columns: 120px 1fr 50px; align-items: center; min-height: 40px; border-bottom: 1px solid rgba(124,150,157,.12); font-size: 11px; }.callback-list dt { color: #657488; }.callback-list dd { overflow: hidden; margin: 0; color: #27374c; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; text-overflow: ellipsis; white-space: nowrap; }.callback-list button { border: 0; color: #148f83; background: transparent; cursor: pointer; }
.configuration-actions { display: flex; align-items: center; gap: 14px; margin-top: 16px; } .configuration-actions { display: flex; align-items: center; gap: 14px; margin-top: 16px; }
.install-pool-select { width: 220px; } .install-pool-select { width: 220px; }
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 14px; }.form-tip { display: block; margin-top: 5px; color: #8b97a6; font-size: 10px; }.link-form .el-select, .link-form .el-input-number { width: 100%; } .form-grid, .rule-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 14px; }.form-tip { display: block; margin-top: 5px; color: #8b97a6; font-size: 10px; }.link-form .el-select, .link-form .el-input-number { width: 100%; }
@media (max-width: 1100px) { .heading-actions { flex-wrap: wrap; justify-content: flex-end; }.metric-grid, .customer-metric-grid { grid-template-columns: repeat(2, minmax(0,1fr)); }.pool-layout { grid-template-columns: 210px minmax(0,1fr); }.pool-toolbar { align-items: flex-start; flex-direction: column; } } @media (max-width: 1100px) { .heading-actions { flex-wrap: wrap; justify-content: flex-end; }.metric-grid, .customer-metric-grid { grid-template-columns: repeat(2, minmax(0,1fr)); }.pool-layout { grid-template-columns: 210px minmax(0,1fr); }.pool-toolbar { align-items: flex-start; flex-direction: column; } }
@media (max-width: 760px) { .promotion-page { padding: 10px; }.page-header, .section-heading { align-items: flex-start; flex-direction: column; }.update-time { display: none; }.metric-grid, .customer-metric-grid, .form-grid { grid-template-columns: 1fr; }.tab-nav { overflow-x: auto; }.tab-nav button { min-width: 112px; }.tab-content { padding: 14px; }.pool-layout { grid-template-columns: 1fr; }.pool-sidebar { display: flex; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--line); }.pool-item { min-width: 190px; }.callback-list > div { grid-template-columns: 1fr 48px; padding: 8px 0; }.callback-list dt { grid-column: 1 / -1; }.install-pool-select { width: 100%; }.customer-heading-actions { width: 100%; justify-content: flex-end; }.customer-filter-bar :deep(.el-form-item) { width: 100%; margin-right: 0; }.customer-filter-bar :deep(.el-form-item__content), .customer-filter-bar .el-select { width: 100%; }.customer-filter-bar .filter-actions :deep(.el-form-item__content) { justify-content: flex-end; }.customer-pagination { align-items: flex-start; flex-direction: column; }.customer-pagination :deep(.el-pagination) { max-width: 100%; flex-wrap: wrap; justify-content: flex-start; } } @media (max-width: 760px) { .promotion-page { padding: 10px; }.page-header, .section-heading { align-items: flex-start; flex-direction: column; }.update-time { display: none; }.metric-grid, .customer-metric-grid, .form-grid, .rule-form-grid { grid-template-columns: 1fr; }.tab-nav { overflow-x: auto; }.tab-nav button { min-width: 112px; }.tab-content { padding: 14px; }.pool-layout { grid-template-columns: 1fr; }.pool-sidebar { display: flex; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--line); }.pool-item { min-width: 190px; }.callback-list > div { grid-template-columns: 1fr 48px; padding: 8px 0; }.callback-list dt { grid-column: 1 / -1; }.install-pool-select { width: 100%; }.customer-heading-actions { width: 100%; justify-content: flex-end; }.customer-filter-bar :deep(.el-form-item) { width: 100%; margin-right: 0; }.customer-filter-bar :deep(.el-form-item__content), .customer-filter-bar .el-select { width: 100%; }.customer-filter-bar .filter-actions :deep(.el-form-item__content) { justify-content: flex-end; }.customer-pagination { align-items: flex-start; flex-direction: column; }.customer-pagination :deep(.el-pagination) { max-width: 100%; flex-wrap: wrap; justify-content: flex-start; } }
</style> </style>
File diff suppressed because one or more lines are too long
+4 -3
View File
@@ -45,9 +45,10 @@ export default defineConfig(({ mode }) => {
: 'http://127.0.0.1:8080' : 'http://127.0.0.1:8080'
return { return {
base: '/admin/', base: '/admin/',
server: { server: {
host: '0.0.0.0', port: 5555,
host: '0.0.0.0',
hmr: true, hmr: true,
open: true, open: true,
proxy: { proxy: {
@@ -79,6 +79,33 @@ class WecomPromotionController extends BaseAdminController
))); )));
} }
public function saveMember()
{
if (!$this->hasPagePermission()) {
return $this->fail('权限不足');
}
return $this->run(fn () => $this->success('成员分流规则已保存', WecomPromotionLogic::saveMember(
$this->request->post(),
$this->adminId,
$this->adminInfo
)));
}
public function toggleMember()
{
if (!$this->hasPagePermission()) {
return $this->fail('权限不足');
}
$id = (int) $this->request->post('id', 0);
$status = (int) $this->request->post('status', 0);
return $this->run(fn () => $this->success(
'成员状态已更新',
WecomPromotionLogic::toggleMember($id, $status, $this->adminId, $this->adminInfo)
));
}
public function checkApiPermission() public function checkApiPermission()
{ {
if (!$this->hasPagePermission()) { if (!$this->hasPagePermission()) {
@@ -7,6 +7,9 @@ namespace app\adminapi\logic\firstvisit;
use app\common\service\DataScope\DataScopeService; use app\common\service\DataScope\DataScopeService;
use app\common\service\qywx\QywxCustomerAcquisitionApiService; use app\common\service\qywx\QywxCustomerAcquisitionApiService;
use app\common\service\qywx\QywxCustomerAcquisitionLinkService; use app\common\service\qywx\QywxCustomerAcquisitionLinkService;
use app\common\service\qywx\QywxPromotionMemberSchedulerService;
use app\common\service\qywx\QywxPromotionRangeSyncService;
use app\common\service\qywx\QywxPromotionWeightedRandom;
use app\common\service\qywx\QywxPromotionWidgetService; use app\common\service\qywx\QywxPromotionWidgetService;
use RuntimeException; use RuntimeException;
use think\facade\Db; use think\facade\Db;
@@ -16,6 +19,7 @@ class WecomPromotionLogic
{ {
public static function overview(int $adminId, array $adminInfo, string $domain): array public static function overview(int $adminId, array $adminInfo, string $domain): array
{ {
self::assertMemberDispatchSchema();
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo); $visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
$poolsQuery = Db::name('qywx_promotion_pool')->alias('p') $poolsQuery = Db::name('qywx_promotion_pool')->alias('p')
->leftJoin('admin u', 'u.id = p.owner_admin_id') ->leftJoin('admin u', 'u.id = p.owner_admin_id')
@@ -46,15 +50,12 @@ class WecomPromotionLogic
unset($pool['widget_config_json']); unset($pool['widget_config_json']);
$key = (string) $pool['public_key']; $key = (string) $pool['public_key'];
$scriptUrl = $domain . '/api/qywx-promotion/js/' . $key; $scriptUrl = $domain . '/api/qywx-promotion/js/' . $key;
$goUrl = $domain . '/api/qywx-promotion/go/' . $key; $compatGoUrl = $domain . '/api/qywx-promotion/go/' . $key;
$pool['script_url'] = $scriptUrl; $pool['script_url'] = $scriptUrl;
$pool['go_url'] = $goUrl; $pool['compat_go_url'] = $compatGoUrl;
$pool['install_code'] = '<script src="' $pool['install_code'] = '<script src="'
. htmlspecialchars($scriptUrl, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . htmlspecialchars($scriptUrl, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
. '" defer></script>'; . '" defer></script>';
$pool['trigger_code'] = '<a href="'
. htmlspecialchars($goUrl, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
. '" data-wecom-promotion="' . $key . '">添加企业微信</a>';
} }
unset($pool); unset($pool);
@@ -70,12 +71,101 @@ class WecomPromotionLogic
if ((int) ($link['status'] ?? 0) === 1 && $link['valid_customer_acquisition_link']) { if ((int) ($link['status'] ?? 0) === 1 && $link['valid_customer_acquisition_link']) {
$onlineLinks++; $onlineLinks++;
} }
if ((string) ($link['today_date'] ?? '') === $today) {
$todayClicks += (int) ($link['today_count'] ?? 0);
}
} }
unset($link); unset($link);
$memberOptions = self::memberOptions($adminId, $adminInfo);
$adminIdByUserId = [];
$memberOptionByAdminId = [];
foreach ($memberOptions as $member) {
$adminIdByUserId[(string) $member['userid']] = (int) $member['id'];
$memberOptionByAdminId[(int) $member['id']] = $member;
}
$linksByPool = [];
foreach ($links as $link) {
$linksByPool[(int) $link['pool_id']][] = $link;
}
self::backfillPoolMembers($pools, $linksByPool, $adminIdByUserId);
$memberRulesByPool = [];
$syncByPool = [];
if ($poolIds !== []) {
$memberRows = Db::name('qywx_promotion_pool_member')
->whereIn('pool_id', $poolIds)
->whereNull('delete_time')
->order('id', 'asc')
->select()->toArray();
foreach ($memberRows as $memberRow) {
$memberAdminId = (int) ($memberRow['admin_id'] ?? 0);
$option = $memberOptionByAdminId[$memberAdminId] ?? [];
$memberRow['name'] = (string) ($option['name'] ?? $memberRow['userid'] ?? '未知成员');
$memberRow['dept_names'] = array_values((array) ($option['dept_names'] ?? []));
$memberRow['status'] = (int) ($memberRow['enabled'] ?? 0);
$memberRow['today_count'] = (string) ($memberRow['today_date'] ?? '') === $today
? (int) ($memberRow['today_count'] ?? 0)
: 0;
$memberRulesByPool[(int) $memberRow['pool_id']][] = $memberRow;
$todayClicks += (int) $memberRow['today_count'];
}
foreach (Db::name('qywx_promotion_range_sync')->whereIn('pool_id', $poolIds)->select()->toArray() as $syncRow) {
$syncByPool[(int) $syncRow['pool_id']] = $syncRow;
}
}
foreach ($pools as &$pool) {
$poolLinks = $linksByPool[(int) $pool['id']] ?? [];
$officialLinks = array_values(array_filter(
$poolLinks,
static fn (array $link): bool => !empty($link['is_official'])
&& (int) ($link['remote_status'] ?? 0) !== 2
));
$legacyCount = count(array_filter(
$poolLinks,
static fn (array $link): bool => empty($link['is_official'])
));
// links 已按 id 倒序返回;旧多链接方案暂以最新官方链接作为主链接,
// 保存方案时会把其余链接仅在本地下线,保留远端链接与历史客户归因。
$officialLink = $officialLinks[0] ?? null;
$memberRules = $memberRulesByPool[(int) $pool['id']] ?? [];
$sync = $syncByPool[(int) $pool['id']] ?? [];
foreach ($memberRules as &$memberRule) {
$memberRule['is_current'] = (int) ($memberRule['id'] ?? 0)
=== (int) ($sync['desired_member_id'] ?? 0);
$memberRule['is_applied'] = (int) ($memberRule['id'] ?? 0)
=== (int) ($sync['applied_member_id'] ?? 0);
$memberRule['sync_status'] = (int) ($sync['status'] ?? 0);
$memberRule['sync_error'] = (string) ($sync['last_error'] ?? '');
}
unset($memberRule);
$memberAdminIds = [];
foreach ($memberRules as $memberRule) {
if ((int) ($memberRule['admin_id'] ?? 0) > 0) {
$memberAdminIds[] = (int) $memberRule['admin_id'];
}
}
$mainUrl = $officialLink === null
? ''
: QywxCustomerAcquisitionLinkService::withCustomerChannel(
(string) ($officialLink['wecom_url'] ?? ''),
'zyt_pool:' . (int) $pool['id']
);
$pool['official_link'] = $officialLink;
$pool['official_link_count'] = count($officialLinks);
$pool['legacy_link_count'] = $legacyCount;
$pool['member_admin_ids'] = array_values(array_unique($memberAdminIds));
$pool['member_rules'] = $memberRules;
$pool['dispatch_sync'] = $sync;
$pool['skip_verify'] = (int) ($officialLink['skip_verify'] ?? 0);
$pool['migration_state'] = count($officialLinks) > 1
? 'needs_resolution'
: ($officialLink === null ? ($legacyCount > 0 ? 'legacy_only' : 'missing') : 'ready');
$pool['main_url'] = $mainUrl;
// go_url 现在代表可直接分享的企业微信官方链接;compat_go_url 保留旧 302 入口。
$pool['go_url'] = $mainUrl;
$pool['trigger_code'] = $mainUrl === '' ? '' : '<a href="'
. htmlspecialchars($mainUrl, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
. '" data-wecom-promotion="' . (string) $pool['public_key'] . '">添加企业微信</a>';
}
unset($pool);
$config = self::internalApplicationStatus($domain); $config = self::internalApplicationStatus($domain);
return [ return [
@@ -92,14 +182,18 @@ class WecomPromotionLogic
], ],
'pools' => $pools, 'pools' => $pools,
'links' => $links, 'links' => $links,
'member_options' => self::memberOptions($adminId, $adminInfo), 'member_options' => $memberOptions,
'customer_acquisition_link_example' => QywxCustomerAcquisitionLinkService::example(), 'customer_acquisition_link_example' => QywxCustomerAcquisitionLinkService::example(),
]; ];
} }
public static function savePool(array $params, int $adminId, array $adminInfo): array public static function savePool(array $params, int $adminId, array $adminInfo): array
{ {
self::assertMemberDispatchSchema();
$id = max(0, (int) ($params['id'] ?? 0)); $id = max(0, (int) ($params['id'] ?? 0));
$existingPool = $id > 0
? self::assertScopedRow('qywx_promotion_pool', $id, $adminId, $adminInfo)
: null;
$name = trim((string) ($params['name'] ?? '')); $name = trim((string) ($params['name'] ?? ''));
if ($name === '' || mb_strlen($name) > 60) { if ($name === '' || mb_strlen($name) > 60) {
throw new RuntimeException('请输入 1-60 个字符的分流方案名称'); throw new RuntimeException('请输入 1-60 个字符的分流方案名称');
@@ -108,28 +202,147 @@ class WecomPromotionLogic
if (!QywxCustomerAcquisitionLinkService::isAllowed($fallback, true)) { if (!QywxCustomerAcquisitionLinkService::isAllowed($fallback, true)) {
throw new RuntimeException('兜底链接必须是企业微信获客助手生成的 HTTPS 链接'); throw new RuntimeException('兜底链接必须是企业微信获客助手生成的 HTTPS 链接');
} }
$members = self::resolveMembers((array) ($params['member_admin_ids'] ?? []), $adminId, $adminInfo);
$userIds = array_values(array_column($members, 'userid'));
$selectedUserId = self::preferredDispatchUserId($id, $members);
$skipVerify = (int) ($params['skip_verify'] ?? 0) === 1 ? 1 : 0;
$status = (int) ($params['status'] ?? 1) === 1 ? 1 : 0;
$now = time(); $now = time();
$data = [ $poolData = [
'name' => $name, 'name' => $name,
'status' => (int) ($params['status'] ?? 1) === 1 ? 1 : 0, 'status' => $status,
'fallback_url' => $fallback, 'fallback_url' => $fallback,
'update_time' => $now, 'update_time' => $now,
]; ];
if ($id > 0) {
self::assertScopedRow('qywx_promotion_pool', $id, $adminId, $adminInfo); $existingLink = null;
Db::name('qywx_promotion_pool')->where('id', $id)->update($data); if ($existingPool !== null) {
} else { $existingLink = Db::name('qywx_promotion_link')
$data += [ ->where('pool_id', $id)
'public_key' => bin2hex(random_bytes(16)), ->whereNull('delete_time')
'owner_admin_id' => $adminId, ->where('remote_link_id', '<>', '')
'dept_id' => self::primaryDeptId($adminId), ->where('remote_status', '<>', 2)
'click_count' => 0, ->order('id', 'desc')
'create_time' => $now, ->find();
];
$id = (int) Db::name('qywx_promotion_pool')->insertGetId($data);
} }
return ['id' => $id]; $api = new QywxCustomerAcquisitionApiService();
$payload = [
'link_name' => mb_substr($name, 0, 30),
// 对外始终是同一个官方直链;当前只激活调度器选中的成员,回调后再切换下一位。
'range' => ['user_list' => [$selectedUserId]],
'skip_verify' => $skipVerify === 1,
];
$createdRemote = false;
if ($existingLink !== null) {
$remoteLinkId = trim((string) ($existingLink['remote_link_id'] ?? ''));
$payload['link_id'] = $remoteLinkId;
$api->updateLink($payload);
} else {
$created = $api->createLink($payload);
$remoteLinkId = self::extractRemoteLinkId($created);
if ($remoteLinkId === '') {
throw new RuntimeException('企业微信已创建链接,但接口未返回 link_id,请执行“同步企业微信”确认结果');
}
$createdRemote = true;
}
try {
$remote = self::normaliseRemoteLink($api->getLink($remoteLinkId), $remoteLinkId);
} catch (\Throwable $e) {
if ($createdRemote) {
try {
$api->deleteLink($remoteLinkId);
} catch (\Throwable) {
// 保留详情读取的原始异常;孤立链接仍可通过企业微信同步找回。
}
}
throw $e;
}
$remoteData = self::remoteColumns($remote, $now);
$linkId = (int) ($existingLink['id'] ?? 0);
try {
Db::transaction(function () use (
&$id,
&$linkId,
$poolData,
$remoteData,
$status,
$existingPool,
$members,
$selectedUserId,
$now,
$adminId
): void {
if ($existingPool === null) {
$id = (int) Db::name('qywx_promotion_pool')->insertGetId($poolData + [
'public_key' => bin2hex(random_bytes(16)),
'owner_admin_id' => $adminId,
'dept_id' => self::primaryDeptId($adminId),
'click_count' => 0,
'create_time' => $now,
]);
} else {
Db::name('qywx_promotion_pool')->where('id', $id)->update($poolData);
}
$linkCommon = [
'pool_id' => $id,
'group_name' => '方案官方链接',
'status' => $status,
'update_time' => $now,
];
if ($linkId > 0) {
Db::name('qywx_promotion_link')->where('id', $linkId)->update($remoteData + $linkCommon);
} else {
$linkId = (int) Db::name('qywx_promotion_link')->insertGetId($remoteData + $linkCommon + [
'account_id' => 0,
'weight' => 1,
'daily_limit' => 0,
'today_count' => 0,
'today_date' => null,
'active_start' => 0,
'active_end' => 0,
'click_count' => 0,
'last_click_time' => 0,
'owner_admin_id' => (int) ($existingPool['owner_admin_id'] ?? 0) ?: $adminId,
'dept_id' => (int) ($existingPool['dept_id'] ?? 0) ?: self::primaryDeptId($adminId),
'remark' => '',
'create_time' => $now,
]);
}
self::persistPoolMembers($id, $members, $now);
QywxPromotionMemberSchedulerService::initialisePool($id, $linkId, $selectedUserId);
// 旧多链接只在本地下线,企业微信远端与历史客户归因继续保留。
Db::name('qywx_promotion_link')
->where('pool_id', $id)
->where('id', '<>', $linkId)
->whereNull('delete_time')
->update(['status' => 0, 'update_time' => $now]);
});
} catch (\Throwable $e) {
if ($createdRemote) {
try {
$api->deleteLink($remoteLinkId);
} catch (\Throwable) {
// 企业微信补偿失败时保留原始异常;可通过官方列表找回孤立链接。
}
}
throw $e;
}
return [
'id' => $id,
'remote_link_id' => (string) ($remote['link_id'] ?? ''),
'wecom_url' => (string) ($remote['url'] ?? ''),
'main_url' => QywxCustomerAcquisitionLinkService::withCustomerChannel(
(string) ($remote['url'] ?? ''),
'zyt_pool:' . $id
),
'member_userids' => $userIds,
'current_userid' => $selectedUserId,
];
} }
public static function saveWidget(array $params, int $adminId, array $adminInfo): array public static function saveWidget(array $params, int $adminId, array $adminInfo): array
@@ -154,15 +367,109 @@ class WecomPromotionLogic
Db::transaction(function () use ($id, $now): void { Db::transaction(function () use ($id, $now): void {
Db::name('qywx_promotion_pool')->where('id', $id)->update(['delete_time' => $now, 'update_time' => $now]); Db::name('qywx_promotion_pool')->where('id', $id)->update(['delete_time' => $now, 'update_time' => $now]);
Db::name('qywx_promotion_link')->where('pool_id', $id)->whereNull('delete_time')->update(['delete_time' => $now, 'update_time' => $now]); Db::name('qywx_promotion_link')->where('pool_id', $id)->whereNull('delete_time')->update(['delete_time' => $now, 'update_time' => $now]);
Db::name('qywx_promotion_pool_member')->where('pool_id', $id)->whereNull('delete_time')->update([
'enabled' => 0,
'delete_time' => $now,
'update_time' => $now,
]);
Db::name('qywx_promotion_range_sync')->where('pool_id', $id)->update([
'status' => 4,
'next_retry' => 0,
'last_error' => '分流方案已删除',
'update_time' => $now,
]);
}); });
} }
public static function saveMember(array $params, int $adminId, array $adminInfo): array
{
self::assertMemberDispatchSchema();
$id = max(0, (int) ($params['id'] ?? 0));
$member = Db::name('qywx_promotion_pool_member')->where('id', $id)->whereNull('delete_time')->find();
if (!$member) {
throw new RuntimeException('分流成员不存在或已移除');
}
$poolId = (int) $member['pool_id'];
self::assertScopedRow('qywx_promotion_pool', $poolId, $adminId, $adminInfo);
$enabled = (int) ($params['status'] ?? $params['enabled'] ?? 1) === 1 ? 1 : 0;
if ($enabled === 0) {
$otherEnabled = (int) Db::name('qywx_promotion_pool_member')
->where('pool_id', $poolId)
->where('id', '<>', $id)
->where('enabled', 1)
->whereNull('delete_time')
->count();
if ($otherEnabled <= 0) {
throw new RuntimeException('至少需要保留一名启用的获客医助');
}
}
$startAt = self::parseTime($params['active_start'] ?? null);
$endAt = self::parseTime($params['active_end'] ?? null);
if ($startAt > 0 && $endAt > 0 && $endAt <= $startAt) {
throw new RuntimeException('生效结束时间必须晚于开始时间');
}
$now = time();
Db::transaction(function () use ($id, $enabled, $params, $startAt, $endAt, $now): void {
Db::name('qywx_promotion_pool_member')->where('id', $id)->update([
'enabled' => $enabled,
'weight' => min(100, max(1, (int) ($params['weight'] ?? 1))),
'daily_limit' => min(1000000, max(0, (int) ($params['daily_limit'] ?? 0))),
'active_start' => $startAt,
'active_end' => $endAt,
'remark' => mb_substr(trim((string) ($params['remark'] ?? '')), 0, 255),
'update_time' => $now,
]);
});
$planned = QywxPromotionMemberSchedulerService::reconcilePool($poolId);
$syncError = '';
if ($planned['queued']) {
try {
(new QywxPromotionRangeSyncService())->syncPool($poolId);
} catch (\Throwable $e) {
// 本地规则已保存;后台分钟任务会继续重试,不把成员状态回滚成错误值。
$syncError = $e->getMessage();
}
}
return ['id' => $id, 'pool_id' => $poolId, 'dispatch' => $planned, 'sync_error' => $syncError];
}
public static function toggleMember(int $id, int $status, int $adminId, array $adminInfo): array
{
self::assertMemberDispatchSchema();
$row = Db::name('qywx_promotion_pool_member')->where('id', $id)->whereNull('delete_time')->find();
if (!$row) {
throw new RuntimeException('分流成员不存在或已移除');
}
return self::saveMember([
'id' => $id,
'status' => $status,
'weight' => (int) ($row['weight'] ?? 1),
'daily_limit' => (int) ($row['daily_limit'] ?? 0),
'active_start' => (int) ($row['active_start'] ?? 0),
'active_end' => (int) ($row['active_end'] ?? 0),
'remark' => (string) ($row['remark'] ?? ''),
], $adminId, $adminInfo);
}
public static function saveLink(array $params, int $adminId, array $adminInfo): array public static function saveLink(array $params, int $adminId, array $adminInfo): array
{ {
$id = max(0, (int) ($params['id'] ?? 0)); $id = max(0, (int) ($params['id'] ?? 0));
$poolId = max(0, (int) ($params['pool_id'] ?? 0)); $poolId = max(0, (int) ($params['pool_id'] ?? 0));
$pool = self::assertScopedRow('qywx_promotion_pool', $poolId, $adminId, $adminInfo); $pool = self::assertScopedRow('qywx_promotion_pool', $poolId, $adminId, $adminInfo);
$existing = $id > 0 ? self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo) : null; $existing = $id > 0 ? self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo) : null;
if ($existing === null) {
$officialCount = (int) Db::name('qywx_promotion_link')
->where('pool_id', $poolId)
->whereNull('delete_time')
->where('remote_link_id', '<>', '')
->where('remote_status', '<>', 2)
->count();
if ($officialCount > 0) {
throw new RuntimeException('一个分流方案只能有一个官方获客链接,请直接编辑分流方案中的获客成员');
}
}
$name = trim((string) ($params['name'] ?? '')); $name = trim((string) ($params['name'] ?? ''));
if ($name === '' || mb_strlen($name) > 80) { if ($name === '' || mb_strlen($name) > 80) {
throw new RuntimeException('请输入 1-80 个字符的获客链接名称'); throw new RuntimeException('请输入 1-80 个字符的获客链接名称');
@@ -438,8 +745,8 @@ class WecomPromotionLogic
return $result; return $result;
} }
/** @return list<string> */ /** @return list<array{id:int,name:string,userid:string,dept_ids:list<int>,dept_names:list<string>}> */
private static function resolveMemberUserIds(array $adminIds, int $adminId, array $adminInfo): array private static function resolveMembers(array $adminIds, int $adminId, array $adminInfo): array
{ {
$requested = array_values(array_unique(array_filter(array_map('intval', $adminIds)))); $requested = array_values(array_unique(array_filter(array_map('intval', $adminIds))));
if ($requested === []) { if ($requested === []) {
@@ -447,47 +754,225 @@ class WecomPromotionLogic
} }
$available = []; $available = [];
foreach (self::memberOptions($adminId, $adminInfo) as $member) { foreach (self::memberOptions($adminId, $adminInfo) as $member) {
$available[$member['id']] = $member['userid']; $available[(int) $member['id']] = $member;
} }
$userIds = []; $members = [];
foreach ($requested as $requestedId) { foreach ($requested as $requestedId) {
if (!isset($available[$requestedId])) { if (!isset($available[$requestedId])) {
throw new RuntimeException('选择的获客成员超出当前角色或部门的数据范围,或尚未绑定企业微信 userid'); throw new RuntimeException('选择的获客成员超出当前角色或部门的数据范围,或尚未绑定企业微信 userid');
} }
$userIds[] = $available[$requestedId]; $members[] = $available[$requestedId];
} }
if (count($userIds) > 500) { if (count($members) > 500) {
throw new RuntimeException('单个获客链接最多配置 500 名成员'); throw new RuntimeException('单个获客链接最多配置 500 名成员');
} }
return array_values(array_unique($userIds)); return $members;
}
/** @return list<string> */
private static function resolveMemberUserIds(array $adminIds, int $adminId, array $adminInfo): array
{
return array_values(array_column(self::resolveMembers($adminIds, $adminId, $adminInfo), 'userid'));
}
/** @param list<array{id:int,userid:string}> $members */
private static function preferredDispatchUserId(int $poolId, array $members): string
{
if ($members === []) {
throw new RuntimeException('请至少选择一名获客成员');
}
$selected = [];
foreach ($members as $member) {
$selected[(string) $member['userid']] = true;
}
$rules = $poolId > 0
? Db::name('qywx_promotion_pool_member')->where('pool_id', $poolId)->whereNull('delete_time')->select()->toArray()
: [];
$rulesById = [];
$rulesByUserId = [];
foreach ($rules as $rule) {
$rulesById[(int) $rule['id']] = $rule;
$rulesByUserId[(string) $rule['userid']] = $rule;
}
$desiredId = $poolId > 0
? (int) (Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->value('desired_member_id') ?? 0)
: 0;
$now = time();
$today = date('Y-m-d', $now);
if (isset($rulesById[$desiredId])) {
$current = $rulesById[$desiredId];
if ((string) ($current['today_date'] ?? '') !== $today) {
$current['today_count'] = 0;
$current['today_date'] = $today;
}
if (isset($selected[(string) $current['userid']]) && QywxPromotionWeightedRandom::eligible($current, $now)) {
return (string) $current['userid'];
}
}
$randomCandidates = [];
$candidateUserIds = [];
foreach ($members as $index => $member) {
$userId = (string) $member['userid'];
$rule = $rulesByUserId[$userId] ?? [
'enabled' => 1,
'weight' => 1,
'daily_limit' => 0,
'today_count' => 0,
'today_date' => $today,
'active_start' => 0,
'active_end' => 0,
];
if ((string) ($rule['today_date'] ?? '') !== $today) {
$rule['today_count'] = 0;
$rule['today_date'] = $today;
}
$candidateId = $index + 1;
$rule['id'] = $candidateId;
$randomCandidates[] = $rule;
$candidateUserIds[$candidateId] = $userId;
}
$selection = QywxPromotionWeightedRandom::select($randomCandidates, $today, $now);
$selectedCandidateId = (int) ($selection['selected_id'] ?? 0);
if ($selectedCandidateId > 0 && isset($candidateUserIds[$selectedCandidateId])) {
return $candidateUserIds[$selectedCandidateId];
}
// 企业微信不接受空范围;仅在所有启用成员恰好达到上限时保留一名,队列会标记“无可用成员”。
foreach ($members as $member) {
$rule = $rulesByUserId[(string) $member['userid']] ?? null;
if ($rule === null || (int) ($rule['enabled'] ?? 1) === 1) {
return (string) $member['userid'];
}
}
throw new RuntimeException('至少需要启用一名获客医助');
}
/** @param list<array{id:int,userid:string}> $members */
private static function persistPoolMembers(int $poolId, array $members, int $now): void
{
$selectedUserIds = [];
foreach ($members as $member) {
$userId = (string) $member['userid'];
$selectedUserIds[] = $userId;
$existing = Db::name('qywx_promotion_pool_member')
->where('pool_id', $poolId)->where('userid', $userId)->find();
if ($existing) {
Db::name('qywx_promotion_pool_member')->where('id', (int) $existing['id'])->update([
'admin_id' => (int) $member['id'],
'userid' => $userId,
'delete_time' => null,
'update_time' => $now,
]);
continue;
}
Db::name('qywx_promotion_pool_member')->insert([
'pool_id' => $poolId,
'admin_id' => (int) $member['id'],
'userid' => $userId,
'enabled' => 1,
'weight' => 1,
'current_weight' => 0,
'daily_limit' => 0,
'today_count' => 0,
'today_date' => null,
'total_count' => 0,
'active_start' => 0,
'active_end' => 0,
'last_assigned_time' => 0,
'remark' => '',
'create_time' => $now,
'update_time' => $now,
]);
}
$removeQuery = Db::name('qywx_promotion_pool_member')
->where('pool_id', $poolId)->whereNull('delete_time');
if ($selectedUserIds !== []) {
$removeQuery->whereNotIn('userid', $selectedUserIds);
}
$removeQuery->update(['delete_time' => $now, 'enabled' => 0, 'update_time' => $now]);
}
/**
* 旧版方案第一次打开时,把官方链接原有 range.user_list 回填为成员规则;不修改远端链接。
*
* @param list<array<string,mixed>> $pools
* @param array<int,list<array<string,mixed>>> $linksByPool
* @param array<string,int> $adminIdByUserId
*/
private static function backfillPoolMembers(array $pools, array $linksByPool, array $adminIdByUserId): void
{
$now = time();
foreach ($pools as $pool) {
$poolId = (int) ($pool['id'] ?? 0);
if ($poolId <= 0 || Db::name('qywx_promotion_pool_member')->where('pool_id', $poolId)->whereNull('delete_time')->count() > 0) {
continue;
}
$officialLink = null;
foreach ($linksByPool[$poolId] ?? [] as $link) {
if (!empty($link['is_official']) && (int) ($link['remote_status'] ?? 0) !== 2) {
$officialLink = $link;
break;
}
}
if ($officialLink === null) {
continue;
}
$firstUserId = '';
foreach ((array) ($officialLink['range_userids'] ?? []) as $userIdValue) {
$userId = trim((string) $userIdValue);
$mappedAdminId = (int) ($adminIdByUserId[$userId] ?? 0);
if ($userId === '' || $mappedAdminId <= 0) {
continue;
}
if ($firstUserId === '') {
$firstUserId = $userId;
}
try {
Db::name('qywx_promotion_pool_member')->insert([
'pool_id' => $poolId,
'admin_id' => $mappedAdminId,
'userid' => $userId,
'enabled' => 1,
'weight' => 1,
'current_weight' => 0,
'daily_limit' => 0,
'today_count' => 0,
'today_date' => null,
'total_count' => 0,
'active_start' => 0,
'active_end' => 0,
'last_assigned_time' => 0,
'remark' => '',
'create_time' => $now,
'update_time' => $now,
]);
} catch (\Throwable) {
// 并发打开 overview 时唯一键会阻止重复回填。
}
}
if ($firstUserId !== '') {
QywxPromotionMemberSchedulerService::initialisePool(
$poolId,
(int) $officialLink['id'],
$firstUserId
);
// 旧链接可能仍包含多人范围;排队收敛为当前调度成员,官方 URL 本身保持不变。
Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->update([
'status' => 1,
'applied_member_id' => 0,
'applied_version' => 0,
'next_retry' => $now,
'update_time' => $now,
]);
}
}
} }
/** @return array<string,mixed> */ /** @return array<string,mixed> */
private static function normaliseRemoteLink(array $response, string $fallbackId = ''): array private static function normaliseRemoteLink(array $response, string $fallbackId = ''): array
{ {
$link = isset($response['link']) && is_array($response['link']) ? $response['link'] : $response; return QywxCustomerAcquisitionLinkService::normaliseRemoteResponse($response, $fallbackId);
$linkId = trim((string) ($link['link_id'] ?? $response['link_id'] ?? $fallbackId));
$url = trim((string) ($link['url'] ?? $link['link_url'] ?? $response['url'] ?? ''));
if ($linkId === '') {
throw new RuntimeException('企业微信获客链接详情缺少 link_id');
}
if (!QywxCustomerAcquisitionLinkService::isAllowed($url)) {
throw new RuntimeException('企业微信获客链接详情未返回有效的 https://work.weixin.qq.com/ca/... 地址');
}
$range = isset($link['range']) && is_array($link['range']) ? $link['range'] : [];
return [
'link_id' => $linkId,
'link_name' => trim((string) ($link['link_name'] ?? $link['name'] ?? $linkId)),
'url' => $url,
'create_time' => max(0, (int) ($link['create_time'] ?? 0)),
'range_userids' => self::normaliseScalarList($range['user_list'] ?? []),
'range_department_ids' => self::normaliseScalarList($range['department_list'] ?? []),
'skip_verify' => !empty($link['skip_verify']),
'priority_option' => isset($link['priority_option']) && is_array($link['priority_option']) ? $link['priority_option'] : [],
'snapshot' => $link,
];
} }
/** @return array<string,mixed> */ /** @return array<string,mixed> */
@@ -681,6 +1166,19 @@ class WecomPromotionLogic
return $time === false ? 0 : $time; return $time === false ? 0 : $time;
} }
private static function assertMemberDispatchSchema(): void
{
try {
Db::name('qywx_promotion_pool_member')->limit(1)->find();
Db::name('qywx_promotion_dispatch_event')->limit(1)->find();
Db::name('qywx_promotion_range_sync')->limit(1)->find();
} catch (\Throwable $e) {
throw new RuntimeException(
'获客成员调度数据表尚未安装,请先执行 server/sql/1.9.20260824/upgrade_qywx_promotion_member_dispatch.sql'
);
}
}
private static function mask(string $value): string private static function mask(string $value): string
{ {
$length = strlen($value); $length = strlen($value);
@@ -742,7 +1240,7 @@ class WecomPromotionLogic
'callback_ready' => $callbackTokenConfigured && $callbackAesConfigured, 'callback_ready' => $callbackTokenConfigured && $callbackAesConfigured,
// 复用自建应用现有的「API 接收消息」入口;获客助手事件与其他应用事件 // 复用自建应用现有的「API 接收消息」入口;获客助手事件与其他应用事件
// 由同一个控制器按 Event/ChangeType 分发,不需要再配置第二个回调地址。 // 由同一个控制器按 Event/ChangeType 分发,不需要再配置第二个回调地址。
'callback_url' => rtrim($domain, '/') . '/api/QywxExternalContactCallback/notify', 'callback_url' => rtrim($domain, '/') . '/api/qywx/external-contact/notify',
'official_doc' => 'https://developer.work.weixin.qq.com/document/path/97297', 'official_doc' => 'https://developer.work.weixin.qq.com/document/path/97297',
]; ];
} }
@@ -6,6 +6,8 @@ namespace app\api\controller;
use app\adminapi\logic\qywx\CustomerLogic; use app\adminapi\logic\qywx\CustomerLogic;
use app\common\service\qywx\QywxCustomerAcquisitionCustomerService; use app\common\service\qywx\QywxCustomerAcquisitionCustomerService;
use app\common\service\qywx\QywxPromotionMemberSchedulerService;
use app\common\service\qywx\QywxPromotionRangeSyncService;
use EasyWeChat\Kernel\Exceptions\BadRequestException; use EasyWeChat\Kernel\Exceptions\BadRequestException;
use EasyWeChat\Work\Application; use EasyWeChat\Work\Application;
use EasyWeChat\Work\Message; use EasyWeChat\Work\Message;
@@ -178,6 +180,29 @@ class QywxExternalContactCallbackController extends BaseApiController
return; return;
} }
if ($changeType === 'add_external_contact' && $state !== '' && $userId !== '') {
// customer_channel=zyt_pool:{id} 会原样进入 State;以实际 UserID 幂等记账并规划下一位。
try {
$dispatch = QywxPromotionMemberSchedulerService::recordFromState(
$state,
$userId,
$extId,
$eventTime,
'external_contact'
);
if (($dispatch['status'] ?? '') === 'counted' && (int) ($dispatch['pool_id'] ?? 0) > 0) {
try {
// 回调后立即切换;分钟任务仍负责网络异常、并发版本变化等情况的兜底重试。
(new QywxPromotionRangeSyncService())->syncPool((int) $dispatch['pool_id']);
} catch (\Throwable $e) {
Log::warning('qywx promotion immediate range sync failed: ' . $e->getMessage());
}
}
} catch (\Throwable $e) {
// 客户资料同步与成员调度互不阻塞;调度异常保留日志,获客会话回调仍可补偿。
Log::error('qywx promotion callback dispatch failed: ' . $e->getMessage(), ['exception' => $e]);
}
}
// 其余变更(添加/编辑/转接成功/标签变化等):以 get 详情为准 UPSERT,避免遗漏未枚举的 ChangeType // 其余变更(添加/编辑/转接成功/标签变化等):以 get 详情为准 UPSERT,避免遗漏未枚举的 ChangeType
CustomerLogic::upsertSingleExternalContactFromApi($extId); CustomerLogic::upsertSingleExternalContactFromApi($extId);
} }
@@ -20,13 +20,13 @@ class QywxPromotionPublicController extends BaseApiController
return response('/* promotion pool not found */', 404, ['Content-Type' => 'application/javascript; charset=utf-8']); return response('/* promotion pool not found */', 404, ['Content-Type' => 'application/javascript; charset=utf-8']);
} }
// 由安装脚本自身的 src 解析 API 域名,避免把请求 Host 写入可公开缓存的 JavaScript。 // 由安装脚本自身的 src 解析 API 域名,避免把请求 Host 写入可公开缓存的 JavaScript。
$goUrl = '/api/qywx-promotion/go/' . $key; $targetUrl = (string) ($pool['target_url'] ?? '');
$config = QywxPromotionWidgetService::decode($pool['widget_config_json'] ?? null); $config = QywxPromotionWidgetService::decode($pool['widget_config_json'] ?? null);
$javascript = QywxPromotionWidgetService::renderScript( $javascript = QywxPromotionWidgetService::renderScript(
$key, $key,
$goUrl, $targetUrl,
$config, $config,
(int) ($pool['status'] ?? 0) === 1 (int) ($pool['status'] ?? 0) === 1 && $targetUrl !== ''
); );
return response($javascript, 200, [ return response($javascript, 200, [
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace app\command;
use app\common\service\qywx\QywxPromotionRangeSyncService;
use think\console\Command;
use think\console\Input;
use think\console\Output;
/** 重试回调调度后尚未同步到企业微信的获客链接成员范围。 */
class QywxSyncPromotionRanges extends Command
{
protected function configure()
{
$this->setName('qywx:sync-promotion-ranges')
->setDescription('同步企业微信官方获客链接的下一名承接成员');
}
protected function execute(Input $input, Output $output): int
{
$result = (new QywxPromotionRangeSyncService())->syncPending(100);
$output->writeln(sprintf(
'QYWX_PROMOTION_RANGE_SYNC selected=%d synced=%d failed=%d',
$result['selected'],
$result['synced'],
$result['failed']
));
return 0;
}
}
@@ -183,11 +183,18 @@ class QywxCustomerAcquisitionApiService
return $this->request($method, $path, $body, true); return $this->request($method, $path, $body, true);
} }
$errorMessage = trim((string) ($decoded['errmsg'] ?? '未知错误')) ?: '未知错误';
if ($errcode === 60111) {
throw new RuntimeException(
'所选医助的企业微信 userid 不存在,或不在获客助手可调用应用的可见范围;'
. '请检查后台账号绑定和企业微信应用可见范围。企业微信返回:' . $errorMessage
);
}
throw new RuntimeException(sprintf( throw new RuntimeException(sprintf(
'企业微信获客助手接口失败[%d]%s', '企业微信获客助手接口失败[%d]%s',
$errcode, $errcode,
trim((string) ($decoded['errmsg'] ?? '未知错误')) ?: '未知错误' $errorMessage
)); ));
} }
@@ -93,6 +93,13 @@ class QywxCustomerAcquisitionCustomerService
'event_time' => $eventTime, 'event_time' => $eventTime,
'snapshot' => $message, 'snapshot' => $message,
], false); ], false);
self::recordPromotionAssignment(
(string) ($message['State'] ?? $message['state'] ?? ''),
$remoteLinkId,
$userId,
$externalUserId,
$eventTime
);
self::finishEvent($eventId, 1, '', $remoteLinkId, $externalUserId, $userId); self::finishEvent($eventId, 1, '', $remoteLinkId, $externalUserId, $userId);
return ['duplicate' => false, 'status' => 'success']; return ['duplicate' => false, 'status' => 'success'];
@@ -126,6 +133,13 @@ class QywxCustomerAcquisitionCustomerService
'event_time' => $eventTime, 'event_time' => $eventTime,
'snapshot' => $chat, 'snapshot' => $chat,
], true); ], true);
self::recordPromotionAssignment(
(string) ($chatInfo['state'] ?? $message['State'] ?? ''),
$remoteLinkId,
$userId,
$externalUserId,
$eventTime
);
self::finishEvent($eventId, 1, '', $remoteLinkId, $externalUserId, $userId); self::finishEvent($eventId, 1, '', $remoteLinkId, $externalUserId, $userId);
return ['duplicate' => false, 'status' => 'success']; return ['duplicate' => false, 'status' => 'success'];
@@ -419,6 +433,40 @@ class QywxCustomerAcquisitionCustomerService
return [$adminId, $deptId]; return [$adminId, $deptId];
} }
private static function recordPromotionAssignment(
string $state,
string $remoteLinkId,
string $userId,
string $externalUserId,
int $eventTime
): void {
$result = QywxPromotionMemberSchedulerService::recordFromState(
$state,
$userId,
$externalUserId,
$eventTime,
'customer_acquisition'
);
if ((int) ($result['pool_id'] ?? 0) <= 0) {
$result = QywxPromotionMemberSchedulerService::recordFromRemoteLink(
$remoteLinkId,
$userId,
$externalUserId,
$eventTime,
'customer_acquisition'
);
}
if (($result['status'] ?? '') !== 'counted' || (int) ($result['pool_id'] ?? 0) <= 0) {
return;
}
try {
// 优先在本次回调完成后切换,后台分钟任务继续承担失败重试。
(new QywxPromotionRangeSyncService())->syncPool((int) $result['pool_id']);
} catch (\Throwable) {
// 同步服务已经保存失败原因和下次重试时间,不能让远端网络错误回滚已记账的客户。
}
}
private static function encodeJson(mixed $value): string private static function encodeJson(mixed $value): string
{ {
$json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace app\common\service\qywx; namespace app\common\service\qywx;
use RuntimeException;
/** 企业微信获客助手链接校验。 */ /** 企业微信获客助手链接校验。 */
class QywxCustomerAcquisitionLinkService class QywxCustomerAcquisitionLinkService
{ {
@@ -39,4 +41,98 @@ class QywxCustomerAcquisitionLinkService
{ {
return 'https://work.weixin.qq.com/ca/xxxxxxxx'; return 'https://work.weixin.qq.com/ca/xxxxxxxx';
} }
/**
* 规范化企业微信 create_link/get 返回的链接详情。
*
* get 接口把 range、priority_option 放在响应根级,link 只包含链接本身;
* 旧响应或测试桩可能把这些字段放在 link 内,因此保留兼容回退。
*
* @return array{
* link_id:string,
* link_name:string,
* url:string,
* create_time:int,
* range_userids:list<string>,
* range_department_ids:list<string>,
* skip_verify:bool,
* priority_option:array<string,mixed>,
* snapshot:array<string,mixed>
* }
*/
public static function normaliseRemoteResponse(array $response, string $fallbackId = ''): array
{
$link = isset($response['link']) && is_array($response['link']) ? $response['link'] : $response;
$range = isset($response['range']) && is_array($response['range'])
? $response['range']
: (isset($link['range']) && is_array($link['range']) ? $link['range'] : []);
$priorityOption = isset($response['priority_option']) && is_array($response['priority_option'])
? $response['priority_option']
: (isset($link['priority_option']) && is_array($link['priority_option']) ? $link['priority_option'] : []);
$linkId = trim((string) ($link['link_id'] ?? $response['link_id'] ?? $fallbackId));
$url = trim((string) ($link['url'] ?? $link['link_url'] ?? $response['url'] ?? ''));
if ($linkId === '') {
throw new RuntimeException('企业微信获客链接详情缺少 link_id');
}
if (!self::isAllowed($url)) {
throw new RuntimeException('企业微信获客链接详情未返回有效的 https://work.weixin.qq.com/ca/... 地址');
}
return [
'link_id' => $linkId,
'link_name' => trim((string) ($link['link_name'] ?? $link['name'] ?? $linkId)),
'url' => $url,
'create_time' => max(0, (int) ($link['create_time'] ?? 0)),
'range_userids' => self::normaliseScalarList($range['user_list'] ?? []),
'range_department_ids' => self::normaliseScalarList($range['department_list'] ?? []),
'skip_verify' => !empty($link['skip_verify']),
'priority_option' => $priorityOption,
'snapshot' => $response,
];
}
/** 追加或替换企业微信获客助手的自定义渠道标识。 */
public static function withCustomerChannel(string $url, string $channel): string
{
$url = trim($url);
$channel = trim($channel);
if (!self::isAllowed($url) || $channel === '' || strlen($channel) > 64) {
return '';
}
// 冒号在查询参数中是合法字符,保留“命名空间:编号”的可读结构;其余字符仍编码。
$parameter = 'customer_channel=' . str_replace('%3A', ':', rawurlencode($channel));
if (preg_match('/([?&])customer_channel=[^&#]*/i', $url) === 1) {
return (string) preg_replace_callback(
'/([?&])customer_channel=[^&#]*/i',
static fn (array $matches): string => $matches[1] . $parameter,
$url,
1
);
}
$fragment = '';
$fragmentPosition = strpos($url, '#');
if ($fragmentPosition !== false) {
$fragment = substr($url, $fragmentPosition);
$url = substr($url, 0, $fragmentPosition);
}
return $url . (str_contains($url, '?') ? '&' : '?') . $parameter . $fragment;
}
/** @return list<string> */
private static function normaliseScalarList(mixed $values): array
{
$result = [];
$seen = [];
foreach ((array) $values as $value) {
$normalised = trim((string) $value);
$key = 'value:' . $normalised;
if ($normalised !== '' && !isset($seen[$key])) {
$seen[$key] = true;
$result[] = $normalised;
}
}
return $result;
}
} }
@@ -0,0 +1,335 @@
<?php
declare(strict_types=1);
namespace app\common\service\qywx;
use think\facade\Db;
/** 根据实际获客回调记账,并为同一个官方链接规划下一名承接成员。 */
class QywxPromotionMemberSchedulerService
{
public static function poolIdFromState(string $state): int
{
$state = trim($state);
return preg_match('/^zyt_pool:(\d+)$/', $state, $matches) === 1
? max(0, (int) $matches[1])
: 0;
}
/** @return array{status:string,pool_id:int,member_id:int,next_member_id:int} */
public static function recordFromState(
string $state,
string $userId,
string $externalUserId,
int $eventTime = 0,
string $source = 'external_contact'
): array {
$poolId = self::poolIdFromState($state);
if ($poolId <= 0) {
return ['status' => 'ignored_state', 'pool_id' => 0, 'member_id' => 0, 'next_member_id' => 0];
}
return self::record($poolId, $userId, $externalUserId, $eventTime, $source);
}
/** @return array{status:string,pool_id:int,member_id:int,next_member_id:int} */
public static function recordFromRemoteLink(
string $remoteLinkId,
string $userId,
string $externalUserId,
int $eventTime = 0,
string $source = 'customer_acquisition'
): array {
$poolId = (int) (Db::name('qywx_promotion_link')
->where('remote_link_id', trim($remoteLinkId))
->whereNull('delete_time')
->value('pool_id') ?? 0);
if ($poolId <= 0) {
return ['status' => 'ignored_link', 'pool_id' => 0, 'member_id' => 0, 'next_member_id' => 0];
}
return self::record($poolId, $userId, $externalUserId, $eventTime, $source);
}
/**
* 管理端变更成员开关、权重或上限后,确保当前远端成员仍可用;必要时立即排队切换。
*
* @return array{pool_id:int,next_member_id:int,queued:bool,blocked:bool}
*/
public static function reconcilePool(int $poolId): array
{
return Db::transaction(function () use ($poolId): array {
$now = time();
$today = date('Y-m-d', $now);
$linkId = self::promotionLinkId($poolId);
$sync = self::lockedSyncRow($poolId);
$members = self::lockedMembers($poolId);
if ($linkId <= 0 || $members === []) {
return ['pool_id' => $poolId, 'next_member_id' => 0, 'queued' => false, 'blocked' => true];
}
$desiredId = (int) ($sync['desired_member_id'] ?? 0);
$desiredEligible = false;
foreach ($members as &$member) {
if ((string) ($member['today_date'] ?? '') !== $today) {
$member['today_date'] = $today;
$member['today_count'] = 0;
}
if ((int) $member['id'] === $desiredId) {
$desiredEligible = QywxPromotionWeightedRandom::eligible($member, $now);
}
}
unset($member);
self::persistMemberCursors($members, $now);
if ($desiredEligible) {
if ((int) ($sync['status'] ?? 0) === 4) {
Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->update([
'status' => 0,
'next_retry' => 0,
'last_error' => '',
'update_time' => $now,
]);
}
return ['pool_id' => $poolId, 'next_member_id' => $desiredId, 'queued' => false, 'blocked' => false];
}
return self::selectAndQueueLocked($poolId, $linkId, $sync, $members, $today, $now);
});
}
public static function initialisePool(int $poolId, int $promotionLinkId, string $selectedUserId): int
{
$memberId = (int) (Db::name('qywx_promotion_pool_member')
->where('pool_id', $poolId)
->where('userid', $selectedUserId)
->whereNull('delete_time')
->value('id') ?? 0);
if ($memberId <= 0 || $promotionLinkId <= 0) {
return 0;
}
$now = time();
$existing = Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->find();
$data = [
'promotion_link_id' => $promotionLinkId,
'desired_member_id' => $memberId,
'applied_member_id' => $memberId,
'status' => 0,
'attempts' => 0,
'next_retry' => 0,
'lock_token' => '',
'lock_until' => 0,
'last_error' => '',
'update_time' => $now,
];
if ($existing) {
$version = max(1, (int) ($existing['desired_version'] ?? 0) + 1);
$data['desired_version'] = $version;
$data['applied_version'] = $version;
Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->update($data);
} else {
$data += ['pool_id' => $poolId, 'desired_version' => 1, 'applied_version' => 1, 'create_time' => $now];
Db::name('qywx_promotion_range_sync')->insert($data);
}
return $memberId;
}
/** @return array{status:string,pool_id:int,member_id:int,next_member_id:int} */
private static function record(
int $poolId,
string $userId,
string $externalUserId,
int $eventTime,
string $source
): array {
$userId = trim($userId);
$externalUserId = trim($externalUserId);
if ($poolId <= 0 || $userId === '' || $externalUserId === '') {
return ['status' => 'ignored_identity', 'pool_id' => $poolId, 'member_id' => 0, 'next_member_id' => 0];
}
return Db::transaction(function () use ($poolId, $userId, $externalUserId, $eventTime, $source): array {
$pool = Db::name('qywx_promotion_pool')->where('id', $poolId)->whereNull('delete_time')->lock(true)->find();
if (!$pool) {
return ['status' => 'ignored_pool', 'pool_id' => $poolId, 'member_id' => 0, 'next_member_id' => 0];
}
$members = self::lockedMembers($poolId);
$actualMember = null;
foreach ($members as $member) {
if ((string) ($member['userid'] ?? '') === $userId) {
$actualMember = $member;
break;
}
}
if ($actualMember === null) {
return ['status' => 'ignored_member', 'pool_id' => $poolId, 'member_id' => 0, 'next_member_id' => 0];
}
$actualMemberId = (int) $actualMember['id'];
$eventKey = hash('sha256', $poolId . '|' . $userId . '|' . $externalUserId);
try {
Db::name('qywx_promotion_dispatch_event')->insert([
'event_key' => $eventKey,
'pool_id' => $poolId,
'member_id' => $actualMemberId,
'userid' => $userId,
'external_userid' => $externalUserId,
'source' => mb_substr($source, 0, 32),
'event_time' => max(0, $eventTime),
'create_time' => time(),
]);
} catch (\Throwable $e) {
if (!Db::name('qywx_promotion_dispatch_event')->where('event_key', $eventKey)->find()) {
throw $e;
}
return ['status' => 'duplicate', 'pool_id' => $poolId, 'member_id' => $actualMemberId, 'next_member_id' => 0];
}
$now = time();
$today = date('Y-m-d', $now);
foreach ($members as &$member) {
if ((string) ($member['today_date'] ?? '') !== $today) {
$member['today_date'] = $today;
$member['today_count'] = 0;
}
if ((int) $member['id'] === $actualMemberId) {
$member['today_count'] = (int) ($member['today_count'] ?? 0) + 1;
$member['total_count'] = (int) ($member['total_count'] ?? 0) + 1;
$member['last_assigned_time'] = max($now, max(0, $eventTime));
}
}
unset($member);
self::persistMemberCursors($members, $now);
$linkId = self::promotionLinkId($poolId);
$sync = self::lockedSyncRow($poolId);
$desiredId = (int) ($sync['desired_member_id'] ?? 0);
if ($linkId <= 0 || ($desiredId > 0 && $desiredId !== $actualMemberId)) {
return ['status' => 'counted_stale', 'pool_id' => $poolId, 'member_id' => $actualMemberId, 'next_member_id' => $desiredId];
}
$planned = self::selectAndQueueLocked($poolId, $linkId, $sync, $members, $today, $now);
return [
'status' => $planned['blocked'] ? 'counted_blocked' : 'counted',
'pool_id' => $poolId,
'member_id' => $actualMemberId,
'next_member_id' => $planned['next_member_id'],
];
});
}
/** @return array{pool_id:int,next_member_id:int,queued:bool,blocked:bool} */
private static function selectAndQueueLocked(
int $poolId,
int $linkId,
?array $sync,
array $members,
string $today,
int $now
): array {
$selection = QywxPromotionWeightedRandom::select($members, $today, $now);
self::persistMemberCursors($selection['members'], $now);
$selectedId = (int) $selection['selected_id'];
if ($selectedId <= 0) {
self::upsertSync($poolId, $linkId, (int) ($sync['desired_member_id'] ?? 0), false, $sync, $now, '所有成员均已禁用、未生效或达到今日上限');
return ['pool_id' => $poolId, 'next_member_id' => 0, 'queued' => false, 'blocked' => true];
}
$changed = $selectedId !== (int) ($sync['desired_member_id'] ?? 0);
self::upsertSync($poolId, $linkId, $selectedId, $changed, $sync, $now);
return ['pool_id' => $poolId, 'next_member_id' => $selectedId, 'queued' => $changed, 'blocked' => false];
}
private static function upsertSync(
int $poolId,
int $linkId,
int $desiredMemberId,
bool $pending,
?array $existing,
int $now,
string $error = ''
): void {
$version = max(1, (int) ($existing['desired_version'] ?? 0) + ($pending ? 1 : 0));
$data = [
'promotion_link_id' => $linkId,
'desired_member_id' => $desiredMemberId,
'desired_version' => $version,
'status' => $error !== '' ? 4 : ($pending ? 1 : (int) ($existing['status'] ?? 0)),
'next_retry' => $error !== '' ? strtotime('tomorrow', $now) : ($pending ? $now : 0),
'last_error' => mb_substr($error, 0, 500),
'update_time' => $now,
];
if ($pending) {
$data['attempts'] = 0;
}
if ($existing) {
Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->update($data);
} else {
$data += [
'pool_id' => $poolId,
'applied_member_id' => 0,
'applied_version' => 0,
'attempts' => 0,
'lock_token' => '',
'lock_until' => 0,
'create_time' => $now,
];
Db::name('qywx_promotion_range_sync')->insert($data);
}
}
/** @return list<array<string,mixed>> */
private static function lockedMembers(int $poolId): array
{
return Db::name('qywx_promotion_pool_member')
->where('pool_id', $poolId)
->whereNull('delete_time')
->order('id', 'asc')
->lock(true)
->select()->toArray();
}
/** @return array<string,mixed>|null */
private static function lockedSyncRow(int $poolId): ?array
{
$row = Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->lock(true)->find();
return $row ?: null;
}
private static function promotionLinkId(int $poolId): int
{
$syncLinkId = (int) (Db::name('qywx_promotion_range_sync')
->where('pool_id', $poolId)->value('promotion_link_id') ?? 0);
if ($syncLinkId > 0) {
return $syncLinkId;
}
return (int) (Db::name('qywx_promotion_link')
->where('pool_id', $poolId)
->where('remote_link_id', '<>', '')
->where('remote_status', '<>', 2)
->whereNull('delete_time')
->order('id', 'desc')
->value('id') ?? 0);
}
/** @param list<array<string,mixed>> $members */
private static function persistMemberCursors(array $members, int $now): void
{
foreach ($members as $member) {
Db::name('qywx_promotion_pool_member')->where('id', (int) $member['id'])->update([
'current_weight' => (int) ($member['current_weight'] ?? 0),
'today_count' => max(0, (int) ($member['today_count'] ?? 0)),
'today_date' => (string) ($member['today_date'] ?? '') ?: null,
'total_count' => max(0, (int) ($member['total_count'] ?? 0)),
'last_assigned_time' => max(0, (int) ($member['last_assigned_time'] ?? 0)),
'update_time' => $now,
]);
}
}
}
@@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
namespace app\common\service\qywx;
use RuntimeException;
use think\facade\Db;
/** 把回调调度出的单个目标成员同步到同一条企业微信官方获客链接。 */
class QywxPromotionRangeSyncService
{
private QywxCustomerAcquisitionApiService $api;
public function __construct(?QywxCustomerAcquisitionApiService $api = null)
{
$this->api = $api ?? new QywxCustomerAcquisitionApiService();
}
/** @return array{status:string,pool_id:int,member_id:int} */
public function syncPool(int $poolId): array
{
$claim = Db::transaction(function () use ($poolId): ?array {
$row = Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->lock(true)->find();
if (!$row) {
return null;
}
$status = (int) ($row['status'] ?? 0);
$now = time();
if ($status === 0 || $status === 4 || ($status === 2 && (int) ($row['lock_until'] ?? 0) > $now)) {
return null;
}
if ($status === 3 && (int) ($row['next_retry'] ?? 0) > $now) {
return null;
}
$token = bin2hex(random_bytes(16));
Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->update([
'status' => 2,
'lock_token' => $token,
'lock_until' => $now + 90,
'attempts' => (int) ($row['attempts'] ?? 0) + 1,
'last_error' => '',
'update_time' => $now,
]);
$row['lock_token'] = $token;
return $row;
});
if ($claim === null) {
return ['status' => 'noop', 'pool_id' => $poolId, 'member_id' => 0];
}
$token = (string) $claim['lock_token'];
$desiredVersion = (int) ($claim['desired_version'] ?? 0);
$memberId = (int) ($claim['desired_member_id'] ?? 0);
try {
$pool = Db::name('qywx_promotion_pool')->where('id', $poolId)->whereNull('delete_time')->find();
$link = Db::name('qywx_promotion_link')
->where('id', (int) ($claim['promotion_link_id'] ?? 0))
->whereNull('delete_time')->find();
$member = Db::name('qywx_promotion_pool_member')
->where('id', $memberId)->where('pool_id', $poolId)->whereNull('delete_time')->find();
if (!$pool || !$link || !$member || (int) ($member['enabled'] ?? 0) !== 1) {
throw new RuntimeException('分流方案、官方链接或目标成员已失效');
}
$remoteLinkId = trim((string) ($link['remote_link_id'] ?? ''));
$userId = trim((string) ($member['userid'] ?? ''));
if ($remoteLinkId === '' || $userId === '') {
throw new RuntimeException('官方链接 ID 或目标成员 userid 为空');
}
$this->api->updateLink([
'link_id' => $remoteLinkId,
'link_name' => mb_substr((string) ($pool['name'] ?? '获客分流方案'), 0, 30),
'range' => ['user_list' => [$userId]],
'skip_verify' => (int) ($link['skip_verify'] ?? 0) === 1,
]);
$response = $this->api->getLink($remoteLinkId);
$remote = QywxCustomerAcquisitionLinkService::normaliseRemoteResponse($response, $remoteLinkId);
$actualUserIds = $remote['range_userids'];
if ($actualUserIds !== [$userId]) {
throw new RuntimeException('企业微信返回的成员范围与待同步成员不一致');
}
$url = $remote['url'];
$snapshot = json_encode($remote['snapshot'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Db::name('qywx_promotion_link')->where('id', (int) $link['id'])->update([
'wecom_url' => $url,
'remote_status' => 1,
'range_user_json' => json_encode($actualUserIds, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
'range_department_json' => json_encode($remote['range_department_ids'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
'remote_snapshot' => $snapshot === false ? null : $snapshot,
'last_sync_time' => time(),
'sync_error' => '',
'update_time' => time(),
]);
$freshVersion = (int) (Db::name('qywx_promotion_range_sync')
->where('pool_id', $poolId)->value('desired_version') ?? 0);
Db::name('qywx_promotion_range_sync')
->where('pool_id', $poolId)
->where('lock_token', $token)
->update([
'status' => $freshVersion === $desiredVersion ? 0 : 1,
'applied_member_id' => $memberId,
'applied_version' => $desiredVersion,
'next_retry' => $freshVersion === $desiredVersion ? 0 : time(),
'attempts' => 0,
'lock_token' => '',
'lock_until' => 0,
'last_error' => '',
'update_time' => time(),
]);
return ['status' => 'synced', 'pool_id' => $poolId, 'member_id' => $memberId];
} catch (\Throwable $e) {
$attempts = max(1, (int) ($claim['attempts'] ?? 0) + 1);
Db::name('qywx_promotion_range_sync')
->where('pool_id', $poolId)
->where('lock_token', $token)
->update([
'status' => 3,
'next_retry' => time() + min(300, 15 * $attempts),
'lock_token' => '',
'lock_until' => 0,
'last_error' => mb_substr($e->getMessage(), 0, 500),
'update_time' => time(),
]);
Db::name('qywx_promotion_link')
->where('id', (int) ($claim['promotion_link_id'] ?? 0))
->update(['sync_error' => mb_substr($e->getMessage(), 0, 500), 'update_time' => time()]);
throw $e;
}
}
/** @return array{selected:int,synced:int,failed:int} */
public function syncPending(int $limit = 100): array
{
$now = time();
$blockedPoolIds = Db::name('qywx_promotion_range_sync')
->where('status', 4)
->where('next_retry', '>', 0)
->where('next_retry', '<=', $now)
->limit(min(500, max(1, $limit)))
->column('pool_id');
foreach ($blockedPoolIds as $blockedPoolId) {
QywxPromotionMemberSchedulerService::reconcilePool((int) $blockedPoolId);
}
$poolIds = Db::name('qywx_promotion_range_sync')
->where(function ($query) use ($now): void {
$query->whereIn('status', [1, 3])->where('next_retry', '<=', $now)
->whereOr(function ($running) use ($now): void {
$running->where('status', 2)->where('lock_until', '<=', $now);
});
})
->order('next_retry', 'asc')
->limit(min(500, max(1, $limit)))
->column('pool_id');
$synced = 0;
$failed = 0;
foreach ($poolIds as $poolId) {
try {
$result = $this->syncPool((int) $poolId);
if ($result['status'] === 'synced') {
$synced++;
}
} catch (\Throwable) {
$failed++;
}
}
return ['selected' => count($poolIds), 'synced' => $synced, 'failed' => $failed];
}
}
@@ -6,10 +6,10 @@ namespace app\common\service\qywx;
use think\facade\Db; use think\facade\Db;
/** 公开获客助手链接分流:按权重随机,并在事务内维护当日限额与点击计数。 */ /** 公开获客助手链接:新方案直达单个官方链接,旧 /go 入口继续兼容历史分流。 */
class QywxPromotionRedirectService class QywxPromotionRedirectService
{ {
/** @return array{status:int,widget_config_json:?string}|null */ /** @return array{status:int,widget_config_json:?string,target_url:string}|null */
public static function publicPoolConfig(string $publicKey): ?array public static function publicPoolConfig(string $publicKey): ?array
{ {
if (preg_match('/^[a-f0-9]{32}$/', $publicKey) !== 1) { if (preg_match('/^[a-f0-9]{32}$/', $publicKey) !== 1) {
@@ -19,17 +19,32 @@ class QywxPromotionRedirectService
$row = Db::name('qywx_promotion_pool') $row = Db::name('qywx_promotion_pool')
->where('public_key', $publicKey) ->where('public_key', $publicKey)
->whereNull('delete_time') ->whereNull('delete_time')
->field('status,widget_config_json') ->field('id,status,widget_config_json')
->find(); ->find();
if (!$row) { if (!$row) {
return null; return null;
} }
$link = Db::name('qywx_promotion_link')
->where('pool_id', (int) $row['id'])
->where('status', 1)
->where('remote_status', 1)
->where('remote_link_id', '<>', '')
->whereNull('delete_time')
->order('id', 'desc')
->field('wecom_url')
->find();
$targetUrl = QywxCustomerAcquisitionLinkService::withCustomerChannel(
(string) ($link['wecom_url'] ?? ''),
'zyt_pool:' . (int) $row['id']
);
return [ return [
'status' => (int) ($row['status'] ?? 0), 'status' => (int) ($row['status'] ?? 0),
'widget_config_json' => isset($row['widget_config_json']) 'widget_config_json' => isset($row['widget_config_json'])
? (string) $row['widget_config_json'] ? (string) $row['widget_config_json']
: null, : null,
'target_url' => $targetUrl,
]; ];
} }
@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace app\common\service\qywx;
use InvalidArgumentException;
/** 获客方案成员的加权随机选择;数据库锁与持久化由调度服务负责。 */
class QywxPromotionWeightedRandom
{
/**
* @param list<array<string,mixed>> $members
* @param int|null $draw 测试用固定抽签值;生产环境留空并使用 random_int。
* @return array{selected_id:int,members:list<array<string,mixed>>,eligible_count:int,total_weight:int}
*/
public static function select(array $members, string $today, int $now, ?int $draw = null): array
{
$eligibleIndexes = [];
$totalWeight = 0;
foreach ($members as $index => &$member) {
if ((string) ($member['today_date'] ?? '') !== $today) {
$member['today_date'] = $today;
$member['today_count'] = 0;
}
// current_weight 是旧版平滑轮询游标;随机模式不再使用,统一归零。
$member['current_weight'] = 0;
if (!self::eligible($member, $now)) {
continue;
}
$weight = max(1, (int) ($member['weight'] ?? 1));
$eligibleIndexes[] = ['index' => $index, 'weight' => $weight];
$totalWeight += $weight;
}
unset($member);
if ($eligibleIndexes === []) {
return [
'selected_id' => 0,
'members' => array_values($members),
'eligible_count' => 0,
'total_weight' => 0,
];
}
$ticket = $draw ?? random_int(1, $totalWeight);
if ($ticket < 1 || $ticket > $totalWeight) {
throw new InvalidArgumentException('加权随机抽签值超出有效范围');
}
$cursor = 0;
$selectedIndex = (int) $eligibleIndexes[0]['index'];
foreach ($eligibleIndexes as $candidate) {
$cursor += (int) $candidate['weight'];
if ($ticket <= $cursor) {
$selectedIndex = (int) $candidate['index'];
break;
}
}
return [
'selected_id' => (int) ($members[$selectedIndex]['id'] ?? 0),
'members' => array_values($members),
'eligible_count' => count($eligibleIndexes),
'total_weight' => $totalWeight,
];
}
/** @param array<string,mixed> $member */
public static function eligible(array $member, int $now): bool
{
if ((int) ($member['enabled'] ?? 0) !== 1 || (int) ($member['weight'] ?? 0) <= 0) {
return false;
}
$start = max(0, (int) ($member['active_start'] ?? 0));
$end = max(0, (int) ($member['active_end'] ?? 0));
if (($start > 0 && $start > $now) || ($end > 0 && $end < $now)) {
return false;
}
$limit = max(0, (int) ($member['daily_limit'] ?? 0));
return $limit === 0 || (int) ($member['today_count'] ?? 0) < $limit;
}
}
@@ -131,11 +131,11 @@ class QywxPromotionWidgetService
} }
/** /**
* 生成可直接跨站安装的完整脚本。真实获客链接始终只由跳转端点选择 * 生成可直接跨站安装的完整脚本,点击后直达当前方案的企业微信官方链接
* *
* @param array<string, mixed> $config * @param array<string, mixed> $config
*/ */
public static function renderScript(string $key, string $goUrl, array $config, bool $poolEnabled = true): string public static function renderScript(string $key, string $targetUrl, array $config, bool $poolEnabled = true): string
{ {
$config = self::fromInput($config); $config = self::fromInput($config);
if (!$poolEnabled) { if (!$poolEnabled) {
@@ -143,14 +143,14 @@ class QywxPromotionWidgetService
} }
$jsonKey = self::jsonForScript($key); $jsonKey = self::jsonForScript($key);
$jsonGo = self::jsonForScript($goUrl); $jsonTarget = self::jsonForScript($targetUrl);
$jsonConfig = self::jsonForScript($config); $jsonConfig = self::jsonForScript($config);
return <<<JS return <<<JS
(function(w,d){ (function(w,d){
'use strict'; 'use strict';
var key={$jsonKey},goPath={$jsonGo},config={$jsonConfig},scriptNode=d.currentScript||null; var key={$jsonKey},targetPath={$jsonTarget},config={$jsonConfig},scriptNode=d.currentScript||null;
var go=resolveGoUrl(goPath); var targetUrl=resolveTargetUrl(targetPath);
var registry=w.WecomPromotion=w.WecomPromotion||{}; var registry=w.WecomPromotion=w.WecomPromotion||{};
var previous=registry[key]; var previous=registry[key];
if(previous&&previous.__widgetVersion===1&&typeof previous.destroy==='function'){ if(previous&&previous.__widgetVersion===1&&typeof previous.destroy==='function'){
@@ -170,7 +170,7 @@ class QywxPromotionWidgetService
return null; return null;
} }
function resolveGoUrl(value){ function resolveTargetUrl(value){
if(/^https?:\/\//i.test(value)){return value;} if(/^https?:\/\//i.test(value)){return value;}
var node=findScriptNode(); var node=findScriptNode();
if(node&&node.src&&typeof w.URL==='function'){ if(node&&node.src&&typeof w.URL==='function'){
@@ -179,14 +179,8 @@ class QywxPromotionWidgetService
return value; return value;
} }
function sourceUrl(){
var location=w.location||{};
var origin=location.origin||((location.protocol&&location.host)?location.protocol+'//'+location.host:'');
return origin+(location.pathname||'/');
}
function openPromotion(){ function openPromotion(){
w.location.assign(go+'?from='+encodeURIComponent(sourceUrl())); if(targetUrl){w.location.assign(targetUrl);}
} }
function handleDocumentClick(event){ function handleDocumentClick(event){
+4 -2
View File
@@ -30,8 +30,10 @@ return [
'qywx:scan-media-channel' => 'app\\command\\QywxScanMediaChannel', 'qywx:scan-media-channel' => 'app\\command\\QywxScanMediaChannel',
// 企业微信会话内容存档同步(需开通会话存档 License 并配置 msgaudit_* 相关项 + 动态库) // 企业微信会话内容存档同步(需开通会话存档 License 并配置 msgaudit_* 相关项 + 动态库)
'qywx:sync-msg-archive' => 'app\\command\\QywxSyncMsgArchive', 'qywx:sync-msg-archive' => 'app\\command\\QywxSyncMsgArchive',
// 获客助手 ChatKey 仅 30 分钟有效,部署时应每分钟执行一次 // 获客助手 ChatKey 仅 30 分钟有效,部署时应每分钟执行一次
'qywx:retry-customer-acquisition-events' => 'app\\command\\QywxRetryCustomerAcquisitionEvents', 'qywx:retry-customer-acquisition-events' => 'app\\command\\QywxRetryCustomerAcquisitionEvents',
// 回调确认实际承接成员后,按权重/上限切换同一条官方获客链接的成员范围
'qywx:sync-promotion-ranges' => 'app\\command\\QywxSyncPromotionRanges',
// 甘草订单物流路由同步(GET_TASK_ROUTE_LIST // 甘草订单物流路由同步(GET_TASK_ROUTE_LIST
'gancao:sync-logistics' => 'app\\command\\GancaoSyncLogisticsRoute', 'gancao:sync-logistics' => 'app\\command\\GancaoSyncLogisticsRoute',
'ej-pharmacy:sync-catalog' => 'app\\command\\EjPharmacySyncCatalog', 'ej-pharmacy:sync-catalog' => 'app\\command\\EjPharmacySyncCatalog',
+43 -13
View File
@@ -30,19 +30,51 @@ HOST = "https://公开访问域名"
- 更新获客链接 `update_link` - 更新获客链接 `update_link`
- 删除获客链接 `delete_link` - 删除获客链接 `delete_link`
“永久删除企业微信链接”会调用官方删除接口且无法恢复;“从本地移除”只退出当前分流池,不会修改企业微信后台 当前管理端按“一个分流方案对应一个官方获客链接”管理。删除分流方案只软删除本地记录,不调用企业微信 `delete_link`,因此不会破坏已有客户归因;官方永久删除接口仅保留给兼容接口使用,调用后无法恢复
获客成员来自后台管理员的 `work_wechat_userid`。管理员可管理全量;组长、医助等账号只返回 `DataScopeService` 当前角色与部门范围内的成员。同步远端链接时,非全量账号只导入 `range.user_list` 与其可见成员有交集的数据;企业微信部门 ID 尚未建立本地映射时按安全原则隐藏,不会越权放行。 获客成员来自后台管理员的 `work_wechat_userid`。管理员可管理全量;组长、医助等账号只返回 `DataScopeService` 当前角色与部门范围内的成员。同步远端链接时,非全量账号只导入 `range.user_list` 与其可见成员有交集的数据;企业微信部门 ID 尚未建立本地映射时按安全原则隐藏,不会越权放行。
`list_link` 只返回当前获客助手可调用应用通过 API 创建的官方链接。后台历史手工粘贴的 `work.weixin.qq.com/ca/...` 链接,以及其他应用创建的链接,不会出现在当前应用的同步列表中,也无法仅凭 URL 反查为官方 `link_id`。需要官方客户、统计和消息归因时,应在本页面使用“创建官方获客链接” `list_link` 只返回当前获客助手可调用应用通过 API 创建的官方链接。后台历史手工粘贴的 `work.weixin.qq.com/ca/...` 链接,以及其他应用创建的链接,不会出现在当前应用的同步列表中,也无法仅凭 URL 反查为官方 `link_id`。需要官方客户、统计和消息归因时,应在本页面创建分流方案并选择获客成员
链接分流只接受企业微信获客助手生成的链接: 创建分流方案时必须选择一名或多名医助。所有医助分别保存启用状态、权重、每日上限、有效时间和实际获客计数,但一个方案仍只创建一条企业微信官方链接。官方链接当前的 `range.user_list` 只放调度器选中的一名成员,回调确认实际承接结果后再按权重随机抽取下一名并更新同一个 `link_id`,因此对外 URL 始终不变。
新建方案时的首名成员也会从所选医助中随机产生;编辑已有方案时,如果当前成员仍可用则保持不变,避免无获客事件时无故切换。
系统只使用企业微信获客助手生成的链接:
```text ```text
https://work.weixin.qq.com/ca/xxxxxxxx https://work.weixin.qq.com/ca/xxxxxxxx
``` ```
“联系我”、客户群、自有网页或其他外部链接均会被拒绝;已有的非获客助手历史链接不会参与随机分流。Secret 与 access_token 不会返回到浏览器,也不会写入接口错误日志。 “联系我”、客户群、自有网页或其他外部链接均会被拒绝;已有的非获客助手历史链接不会作为方案主链接。Secret 与 access_token 不会返回到浏览器,也不会写入接口错误日志。
可复制的主链接会追加分流方案渠道参数,结构如下:
```text
https://work.weixin.qq.com/ca/xxxxxxxx?customer_channel=zyt_pool:123
```
其中 `customer_channel` 是本站写入的自定义渠道值,格式为 `zyt_pool:分流方案ID`;它与示例中的 `qywx_ca:...` 作用相同,但命名空间和数值由各系统自行定义。
## 回调驱动成员调度
部署时必须执行:
```text
server/sql/1.9.20260824/upgrade_qywx_promotion_member_dispatch.sql
```
并在企业微信后台把“API 接收消息”配置为:
```text
https://你的域名/api/qywx/external-contact/notify
```
调度优先使用 `change_external_contact/add_external_contact` 事件中的 `State``UserID``ExternalUserID``State` 来自主链接的 `customer_channel=zyt_pool:方案ID`,因此可以定位方案及实际承接医助;获客会话回调会通过 `ChatKey → get_chat_info` 作为补偿。方案、成员和客户组合使用唯一幂等键,同一实际获客不会因重复回调重复计数。
每次确认实际承接后,系统执行加权随机抽取:权重越大,被抽中的概率越高;禁用、尚未生效、已过期或达到今日上限的成员不会进入随机池。随机允许连续抽中同一成员。待同步范围由 `qywx:sync-promotion-ranges` 每分钟重试,管理端主动禁用当前成员时也会立即尝试同步。
权重和数量属于回调驱动的近实时控制,并非点击前的强事务:多个客户在企微回调或 `update_link` 生效前并发访问时,可能仍由同一成员承接;所有成员都达到上限时,企业微信不允许把 `range.user_list` 更新为空,系统会标记“无可用成员”并保留最后一次有效范围。因此数量上限用于自动退出后续调度,不承诺并发场景下绝对零超量。
如果需要为点击 IP 生成不可逆服务端哈希,可在 `[qywx_promotion]` 下额外设置独立的 `CREDENTIAL_KEY` 如果需要为点击 IP 生成不可逆服务端哈希,可在 `[qywx_promotion]` 下额外设置独立的 `CREDENTIAL_KEY`
@@ -50,9 +82,11 @@ https://work.weixin.qq.com/ca/xxxxxxxx
```html ```html
<script src="https://你的域名/api/qywx-promotion/js/分流方案KEY" defer></script> <script src="https://你的域名/api/qywx-promotion/js/分流方案KEY" defer></script>
<a href="https://你的域名/api/qywx-promotion/go/分流方案KEY" data-wecom-promotion="分流方案KEY">添加企业微信</a> <a href="https://work.weixin.qq.com/ca/xxxxxxxx?customer_channel=zyt_pool:123">添加企业微信</a>
``` ```
旧的 `/api/qywx-promotion/go/分流方案KEY` 地址继续保留,兼容已经投放的安装代码;新建方案、管理端复制链接和新版浮窗均直接打开官方获客链接。
## 公开浮窗 ## 公开浮窗
每个分流方案可选择是否由同一段公开 JS 自动挂载客服浮窗。关闭浮窗时,已有的 每个分流方案可选择是否由同一段公开 JS 自动挂载客服浮窗。关闭浮窗时,已有的
@@ -66,7 +100,7 @@ https://work.weixin.qq.com/ca/xxxxxxxx
- 标题、副标题、按钮文案和 `#RRGGBB` 主题色 - 标题、副标题、按钮文案和 `#RRGGBB` 主题色
- 16-160 像素底部距离、移动端展示开关和浮窗总开关 - 16-160 像素底部距离、移动端展示开关和浮窗总开关
公开脚本仅下发经过白名单校验的展示配置,不下发兜底链接或真实获客链接池。模板 公开脚本仅下发经过白名单校验的展示配置和当前方案的单个官方目标链接,不下发历史链接池或 Secret。模板
由脚本内置,管理端文案通过 DOM `textContent` 写入,不接受自定义 HTML、CSS 或脚本。 由脚本内置,管理端文案通过 DOM `textContent` 写入,不接受自定义 HTML、CSS 或脚本。
损坏配置、未知版本和非法枚举会按关闭浮窗处理。 损坏配置、未知版本和非法枚举会按关闭浮窗处理。
@@ -79,10 +113,7 @@ window.WecomPromotion['分流方案KEY'].hide()
window.WecomPromotion['分流方案KEY'].destroy() window.WecomPromotion['分流方案KEY'].destroy()
``` ```
公开脚本缓存 60 秒,因此浮窗样式开关更新最多延迟约 60 秒;方案运行状态仍会在 公开脚本缓存 60 秒,因此浮窗样式开关或官方目标链接更新最多延迟约 60 秒。新版浮窗直接打开企业微信官方链接,不再经过本站逐次 302;已经复制到外部的官方链接也不会因本地关闭方案而失效。管理端安装代码优先使用 `[app] HOST`,请在生产环境配置唯一的 HTTPS 公开域名。
每次服务端跳转时即时校验。脚本会从自身 `src` 解析跳转接口域名,不会把公开请求的
Host 写入缓存内容。管理端安装代码优先使用 `[app] HOST`,请在生产环境配置唯一的
HTTPS 公开域名。
接入站点若启用了严格 CSP,需要允许脚本域名,并给安装 `<script>` 添加站点当前请求 接入站点若启用了严格 CSP,需要允许脚本域名,并给安装 `<script>` 添加站点当前请求
`nonce`。公开脚本会把该 `nonce` 传给 Shadow DOM 内的动态样式: `nonce`。公开脚本会把该 `nonce` 传给 Shadow DOM 内的动态样式:
@@ -91,7 +122,6 @@ HTTPS 公开域名。
<script nonce="当前请求的 nonce" src="https://你的域名/api/qywx-promotion/js/分流方案KEY" defer></script> <script nonce="当前请求的 nonce" src="https://你的域名/api/qywx-promotion/js/分流方案KEY" defer></script>
``` ```
点击来源只上报页面的 origin 与 pathname,不包含查询参数或 fragment。推广页路径中也 `/go` 兼容入口记录点击来源时,只保存页面的 origin 与 pathname,不包含查询参数或 fragment。推广页路径中也不应放置手机号、患者 ID、重置令牌等敏感信息。
不应放置手机号、患者 ID、重置令牌等敏感信息。
随机分流在服务端完成。候选链接必须同时满足:方案启用、链接上线、处于有效时间段、未超过当日上限。权重越大,被选中的概率越高 用户始终看到同一个企业微信官方获客链接。当前激活成员由本站根据实际回调、启用状态、权重和上限动态更新;企业微信仍可能根据成员可服务状态和已有好友关系等规则影响最终承接结果,后续回调会按实际结果自动纠偏
@@ -1 +1 @@
import t from"./error-DGH5hr-_.js";import{o,q as r,r as a,v as n,D as c,s}from"./.pnpm-Dwh6tNxD.js";import"./index-C9ilrjDr.js";const p="/admin/assets/no_perms-jDxcYpYC.png",i={class:"error404"},x=o({__name:"403",setup(m){return(_,e)=>(r(),a("div",i,[n(t,{code:"403",title:"您的账号权限不足,请联系管理员添加权限!","show-btn":!1},{content:c(()=>[...e[0]||(e[0]=[s("div",{class:"flex justify-center"},[s("img",{class:"w-[150px] h-[150px]",src:p,alt:""})],-1)])]),_:1})]))}});export{x as default}; import t from"./error-BSPt5W5D.js";import{o,q as r,r as a,v as n,D as c,s}from"./.pnpm-Dwh6tNxD.js";import"./index-C0Q8AuuZ.js";const p="/admin/assets/no_perms-jDxcYpYC.png",i={class:"error404"},x=o({__name:"403",setup(m){return(_,e)=>(r(),a("div",i,[n(t,{code:"403",title:"您的账号权限不足,请联系管理员添加权限!","show-btn":!1},{content:c(()=>[...e[0]||(e[0]=[s("div",{class:"flex justify-center"},[s("img",{class:"w-[150px] h-[150px]",src:p,alt:""})],-1)])]),_:1})]))}});export{x as default};
@@ -1 +1 @@
import e from"./error-DGH5hr-_.js";import{o,q as r,r as t,v as s}from"./.pnpm-Dwh6tNxD.js";import"./index-C9ilrjDr.js";const a={class:"error404"},d=o({__name:"404",setup(c){return(n,_)=>(r(),t("div",a,[s(e,{code:"404",title:"哎呀,出错了!您访问的页面不存在…"})]))}});export{d as default}; import e from"./error-BSPt5W5D.js";import{o,q as r,r as t,v as s}from"./.pnpm-Dwh6tNxD.js";import"./index-C0Q8AuuZ.js";const a={class:"error404"},d=o({__name:"404",setup(c){return(n,_)=>(r(),t("div",a,[s(e,{code:"404",title:"哎呀,出错了!您访问的页面不存在…"})]))}});export{d as default};
@@ -1 +1 @@
import{o as C,R as D,q as d,r as I,ac as N,O as u,bj as B,D as o,v as l,bk as E,br as L,L as i,T as r,s as p,bi as R,M as w}from"./.pnpm-Dwh6tNxD.js";import{a as V}from"./doctor-DZ47WohD.js";import{m as A,_ as M}from"./index-C9ilrjDr.js";const P={class:"appointment-record-panel"},$={class:"cell-stack"},j={class:"font-medium"},q={class:"text-gray-500 text-sm"},O={class:"cell-stack"},F={class:"text-gray-500 text-sm"},G=C({__name:"AppointmentRecordPanel",props:{diagnosisId:{}},setup(v,{expose:y}){const f=v,m=w(!1),g=w([]),h=w({});function x(t){return t?String(t).length>=8?String(t).slice(0,5):t:""}function k(t){return t==="morning"?"上午":t==="afternoon"?"下午":t==="all"?"全天":t||"—"}function S(t){const n=t.channel_source??t.channels??"";if(n===""||n===null||n===void 0)return"—";const a=String(n),s=h.value[a]||a,c=String(t.channel_source_detail??"").trim();return c!==""?`${s}${c}`:s}function T(t){return[...t].sort((n,a)=>{const s=String(n.appointment_date||""),c=String(a.appointment_date||"");if(s!==c)return c.localeCompare(s);const _=String(n.appointment_time||""),e=String(a.appointment_time||"");return _!==e?e.localeCompare(_):Number(a.id||0)-Number(n.id||0)})}const z=async()=>{try{const t=await A({type:"channels"}),n=((t==null?void 0:t.channels)||[]).filter(s=>s.status!==0),a={};for(const s of n)s.value!=null&&(a[String(s.value)]=s.name||String(s.value));h.value=a}catch{h.value={}}},b=async()=>{if(f.diagnosisId){m.value=!0;try{await z();const t=await V({patient_id:f.diagnosisId,diag_scope_relax:1,page_no:1,page_size:500}),n=(t==null?void 0:t.lists)||[];g.value=T(n)}catch(t){console.error(t),g.value=[]}finally{m.value=!1}}};return D(()=>f.diagnosisId,()=>{b()},{immediate:!0}),y({refresh:b}),(t,n)=>{const a=E,s=L,c=B,_=R;return d(),I("div",P,[N((d(),u(c,{data:g.value,border:"",stripe:"","empty-text":"暂无挂号记录"},{default:o(()=>[l(a,{label:"ID",prop:"id",width:"72",align:"center"}),l(a,{label:"状态",width:"100",align:"center"},{default:o(({row:e})=>[l(s,{type:e.status===1?"success":e.status===2?"info":e.status===3?"primary":"danger",size:"small",effect:"light"},{default:o(()=>[i(r(e.status_desc||"—"),1)]),_:2},1032,["type"])]),_:1}),l(a,{label:"患者(挂号人)","min-width":"150"},{default:o(({row:e})=>[p("div",$,[p("span",j,r(e.patient_name||"—"),1),p("span",q,r(e.patient_phone||""),1)])]),_:1}),l(a,{label:"挂号医生",prop:"doctor_name",width:"110","show-overflow-tooltip":""}),l(a,{label:"挂号助理",width:"110","show-overflow-tooltip":""},{default:o(({row:e})=>[i(r(e.assistant_name||"—"),1)]),_:1}),l(a,{label:"预约时间","min-width":"130"},{default:o(({row:e})=>[p("div",O,[p("span",null,r(e.appointment_date||"—"),1),p("span",F,r(x(e.appointment_time)),1)])]),_:1}),l(a,{label:"时段",width:"80",align:"center"},{default:o(({row:e})=>[i(r(k(e.period)),1)]),_:1}),l(a,{label:"类型",width:"100","show-overflow-tooltip":""},{default:o(({row:e})=>[i(r(e.appointment_type_desc||"—"),1)]),_:1}),l(a,{label:"渠道",width:"110","show-overflow-tooltip":""},{default:o(({row:e})=>[i(r(S(e)),1)]),_:1}),l(a,{label:"确认诊单",width:"92",align:"center"},{default:o(({row:e})=>[e.diagnosis_confirmed?(d(),u(s,{key:0,type:"success",size:"small",effect:"plain"},{default:o(()=>[...n[0]||(n[0]=[i("已确认",-1)])]),_:1})):(d(),u(s,{key:1,type:"warning",size:"small",effect:"plain"},{default:o(()=>[...n[1]||(n[1]=[i("未确认",-1)])]),_:1}))]),_:1}),l(a,{label:"开方",width:"80",align:"center"},{default:o(({row:e})=>[e.has_prescription?(d(),u(s,{key:0,type:"success",size:"small",effect:"plain"},{default:o(()=>[...n[2]||(n[2]=[i("已开方",-1)])]),_:1})):(d(),u(s,{key:1,type:"info",size:"small",effect:"plain"},{default:o(()=>[...n[3]||(n[3]=[i("未开方",-1)])]),_:1}))]),_:1}),l(a,{label:"备注",prop:"remark","min-width":"100","show-overflow-tooltip":""}),l(a,{label:"创建时间",width:"165",prop:"create_time"})]),_:1},8,["data"])),[[_,m.value]])])}}}),Q=M(G,[["__scopeId","data-v-4de87dfa"]]);export{Q as default}; import{o as C,R as D,q as d,r as I,ac as N,O as u,bj as B,D as o,v as l,bk as E,br as L,L as i,T as r,s as p,bi as R,M as w}from"./.pnpm-Dwh6tNxD.js";import{a as V}from"./doctor-CTRg8la2.js";import{m as A,_ as M}from"./index-C0Q8AuuZ.js";const P={class:"appointment-record-panel"},$={class:"cell-stack"},j={class:"font-medium"},q={class:"text-gray-500 text-sm"},O={class:"cell-stack"},F={class:"text-gray-500 text-sm"},G=C({__name:"AppointmentRecordPanel",props:{diagnosisId:{}},setup(v,{expose:y}){const f=v,m=w(!1),g=w([]),h=w({});function x(t){return t?String(t).length>=8?String(t).slice(0,5):t:""}function k(t){return t==="morning"?"上午":t==="afternoon"?"下午":t==="all"?"全天":t||"—"}function S(t){const n=t.channel_source??t.channels??"";if(n===""||n===null||n===void 0)return"—";const a=String(n),s=h.value[a]||a,c=String(t.channel_source_detail??"").trim();return c!==""?`${s}${c}`:s}function T(t){return[...t].sort((n,a)=>{const s=String(n.appointment_date||""),c=String(a.appointment_date||"");if(s!==c)return c.localeCompare(s);const _=String(n.appointment_time||""),e=String(a.appointment_time||"");return _!==e?e.localeCompare(_):Number(a.id||0)-Number(n.id||0)})}const z=async()=>{try{const t=await A({type:"channels"}),n=((t==null?void 0:t.channels)||[]).filter(s=>s.status!==0),a={};for(const s of n)s.value!=null&&(a[String(s.value)]=s.name||String(s.value));h.value=a}catch{h.value={}}},b=async()=>{if(f.diagnosisId){m.value=!0;try{await z();const t=await V({patient_id:f.diagnosisId,diag_scope_relax:1,page_no:1,page_size:500}),n=(t==null?void 0:t.lists)||[];g.value=T(n)}catch(t){console.error(t),g.value=[]}finally{m.value=!1}}};return D(()=>f.diagnosisId,()=>{b()},{immediate:!0}),y({refresh:b}),(t,n)=>{const a=E,s=L,c=B,_=R;return d(),I("div",P,[N((d(),u(c,{data:g.value,border:"",stripe:"","empty-text":"暂无挂号记录"},{default:o(()=>[l(a,{label:"ID",prop:"id",width:"72",align:"center"}),l(a,{label:"状态",width:"100",align:"center"},{default:o(({row:e})=>[l(s,{type:e.status===1?"success":e.status===2?"info":e.status===3?"primary":"danger",size:"small",effect:"light"},{default:o(()=>[i(r(e.status_desc||"—"),1)]),_:2},1032,["type"])]),_:1}),l(a,{label:"患者(挂号人)","min-width":"150"},{default:o(({row:e})=>[p("div",$,[p("span",j,r(e.patient_name||"—"),1),p("span",q,r(e.patient_phone||""),1)])]),_:1}),l(a,{label:"挂号医生",prop:"doctor_name",width:"110","show-overflow-tooltip":""}),l(a,{label:"挂号助理",width:"110","show-overflow-tooltip":""},{default:o(({row:e})=>[i(r(e.assistant_name||"—"),1)]),_:1}),l(a,{label:"预约时间","min-width":"130"},{default:o(({row:e})=>[p("div",O,[p("span",null,r(e.appointment_date||"—"),1),p("span",F,r(x(e.appointment_time)),1)])]),_:1}),l(a,{label:"时段",width:"80",align:"center"},{default:o(({row:e})=>[i(r(k(e.period)),1)]),_:1}),l(a,{label:"类型",width:"100","show-overflow-tooltip":""},{default:o(({row:e})=>[i(r(e.appointment_type_desc||"—"),1)]),_:1}),l(a,{label:"渠道",width:"110","show-overflow-tooltip":""},{default:o(({row:e})=>[i(r(S(e)),1)]),_:1}),l(a,{label:"确认诊单",width:"92",align:"center"},{default:o(({row:e})=>[e.diagnosis_confirmed?(d(),u(s,{key:0,type:"success",size:"small",effect:"plain"},{default:o(()=>[...n[0]||(n[0]=[i("已确认",-1)])]),_:1})):(d(),u(s,{key:1,type:"warning",size:"small",effect:"plain"},{default:o(()=>[...n[1]||(n[1]=[i("未确认",-1)])]),_:1}))]),_:1}),l(a,{label:"开方",width:"80",align:"center"},{default:o(({row:e})=>[e.has_prescription?(d(),u(s,{key:0,type:"success",size:"small",effect:"plain"},{default:o(()=>[...n[2]||(n[2]=[i("已开方",-1)])]),_:1})):(d(),u(s,{key:1,type:"info",size:"small",effect:"plain"},{default:o(()=>[...n[3]||(n[3]=[i("未开方",-1)])]),_:1}))]),_:1}),l(a,{label:"备注",prop:"remark","min-width":"100","show-overflow-tooltip":""}),l(a,{label:"创建时间",width:"165",prop:"create_time"})]),_:1},8,["data"])),[[_,m.value]])])}}}),Q=M(G,[["__scopeId","data-v-4de87dfa"]]);export{Q as default};
@@ -1 +1 @@
import{o as T,R as C,q as _,r as b,ac as D,O as h,bj as $,D as r,v as n,bk as L,L as c,T as d,br as k,s as E,a1 as A,w as P,u as B,ch as F,bi as M,M as w}from"./.pnpm-Dwh6tNxD.js";import{ae as V}from"./tcm-RVDaIW5j.js";import{_ as q}from"./index-C9ilrjDr.js";const K={class:"assign-log-panel"},j={key:1,class:"text-gray-400"},z=T({__name:"AssignLogPanel",props:{diagnosisId:{}},setup(v,{expose:y}){const u=v,m=w(!1),p=w([]);function N(a){const e=a.related_po_creator_name;if(e!=null&&String(e).trim()!=="")return String(e);const t=Number(a.related_po_creator_id);return Number.isFinite(t)&&t>0?`ID:${t}`:"—"}function x(a){const e=a.related_po_create_time_text;if(e!=null&&String(e).trim()!=="")return String(e);const t=Number(a.related_po_create_time);if(!Number.isFinite(t)||t<=0)return"—";const s=new Date(t*1e3);if(Number.isNaN(s.getTime()))return"—";const o=l=>String(l).padStart(2,"0");return`${s.getFullYear()}-${o(s.getMonth()+1)}-${o(s.getDate())} ${o(s.getHours())}:${o(s.getMinutes())}:${o(s.getSeconds())}`}function f(a,e){const t=e==="from"?"from_assistant_name":"to_assistant_name",s=e==="from"?"from_assistant_id":"to_assistant_id",o=a[t];if(o!=null&&String(o).trim()!==""&&String(o)!=="—")return String(o);const l=Number(a[s]);return Number.isFinite(l)&&l>0?`ID:${l}`:"—"}const g=async()=>{if(u.diagnosisId){m.value=!0;try{const a=await V({id:u.diagnosisId}),e=Array.isArray(a)?a:[];p.value=e}catch(a){console.error(a),p.value=[]}finally{m.value=!1}}};return C(()=>u.diagnosisId,()=>{g()},{immediate:!0}),y({refresh:g}),(a,e)=>{const t=L,s=k,o=P,l=A,S=$,I=M;return _(),b("div",K,[D((_(),h(S,{data:p.value,border:"",stripe:"","empty-text":"暂无指派记录"},{default:r(()=>[n(t,{label:"操作时间",width:"175",prop:"create_time_text"}),n(t,{label:"原医助","min-width":"120"},{default:r(({row:i})=>[c(d(f(i,"from")),1)]),_:1}),n(t,{label:"新医助","min-width":"120"},{default:r(({row:i})=>[c(d(f(i,"to")),1)]),_:1}),n(t,{label:"继承",width:"72",align:"center"},{default:r(({row:i})=>[Number(i.is_inherit)===1?(_(),h(s,{key:0,type:"success",size:"small"},{default:r(()=>[...e[0]||(e[0]=[c("是",-1)])]),_:1})):(_(),b("span",j,"否"))]),_:1}),n(t,{label:"快照·业务单创建人","min-width":"130","show-overflow-tooltip":""},{default:r(({row:i})=>[c(d(N(i)),1)]),_:1}),n(t,{label:"快照·业务单创建时间",width:"190"},{header:r(()=>[e[1]||(e[1]=E("span",null,"快照·业务单创建时间",-1)),n(l,{placement:"top",content:"related_po_create_time:触发本次操作的处方业务订单 create_time;与原文助一致时表示医助创建订单时间"},{default:r(()=>[n(o,{class:"assign-log-col-hint"},{default:r(()=>[n(B(F))]),_:1})]),_:1})]),default:r(({row:i})=>[c(d(x(i)),1)]),_:1}),n(t,{label:"操作人",width:"110",prop:"operator_name"}),n(t,{label:"操作账号",width:"120",prop:"operator_account","show-overflow-tooltip":""}),n(t,{label:"IP",width:"130",prop:"ip","show-overflow-tooltip":""})]),_:1},8,["data"])),[[I,m.value]])])}}}),Y=q(z,[["__scopeId","data-v-f670e3e6"]]);export{Y as default}; import{o as T,R as C,q as _,r as b,ac as D,O as h,bj as $,D as r,v as n,bk as L,L as c,T as d,br as k,s as E,a1 as A,w as P,u as B,ch as F,bi as M,M as w}from"./.pnpm-Dwh6tNxD.js";import{ae as V}from"./tcm-WWBqr2Vo.js";import{_ as q}from"./index-C0Q8AuuZ.js";const K={class:"assign-log-panel"},j={key:1,class:"text-gray-400"},z=T({__name:"AssignLogPanel",props:{diagnosisId:{}},setup(v,{expose:y}){const u=v,m=w(!1),p=w([]);function N(a){const e=a.related_po_creator_name;if(e!=null&&String(e).trim()!=="")return String(e);const t=Number(a.related_po_creator_id);return Number.isFinite(t)&&t>0?`ID:${t}`:"—"}function x(a){const e=a.related_po_create_time_text;if(e!=null&&String(e).trim()!=="")return String(e);const t=Number(a.related_po_create_time);if(!Number.isFinite(t)||t<=0)return"—";const s=new Date(t*1e3);if(Number.isNaN(s.getTime()))return"—";const o=l=>String(l).padStart(2,"0");return`${s.getFullYear()}-${o(s.getMonth()+1)}-${o(s.getDate())} ${o(s.getHours())}:${o(s.getMinutes())}:${o(s.getSeconds())}`}function f(a,e){const t=e==="from"?"from_assistant_name":"to_assistant_name",s=e==="from"?"from_assistant_id":"to_assistant_id",o=a[t];if(o!=null&&String(o).trim()!==""&&String(o)!=="—")return String(o);const l=Number(a[s]);return Number.isFinite(l)&&l>0?`ID:${l}`:"—"}const g=async()=>{if(u.diagnosisId){m.value=!0;try{const a=await V({id:u.diagnosisId}),e=Array.isArray(a)?a:[];p.value=e}catch(a){console.error(a),p.value=[]}finally{m.value=!1}}};return C(()=>u.diagnosisId,()=>{g()},{immediate:!0}),y({refresh:g}),(a,e)=>{const t=L,s=k,o=P,l=A,S=$,I=M;return _(),b("div",K,[D((_(),h(S,{data:p.value,border:"",stripe:"","empty-text":"暂无指派记录"},{default:r(()=>[n(t,{label:"操作时间",width:"175",prop:"create_time_text"}),n(t,{label:"原医助","min-width":"120"},{default:r(({row:i})=>[c(d(f(i,"from")),1)]),_:1}),n(t,{label:"新医助","min-width":"120"},{default:r(({row:i})=>[c(d(f(i,"to")),1)]),_:1}),n(t,{label:"继承",width:"72",align:"center"},{default:r(({row:i})=>[Number(i.is_inherit)===1?(_(),h(s,{key:0,type:"success",size:"small"},{default:r(()=>[...e[0]||(e[0]=[c("是",-1)])]),_:1})):(_(),b("span",j,"否"))]),_:1}),n(t,{label:"快照·业务单创建人","min-width":"130","show-overflow-tooltip":""},{default:r(({row:i})=>[c(d(N(i)),1)]),_:1}),n(t,{label:"快照·业务单创建时间",width:"190"},{header:r(()=>[e[1]||(e[1]=E("span",null,"快照·业务单创建时间",-1)),n(l,{placement:"top",content:"related_po_create_time:触发本次操作的处方业务订单 create_time;与原文助一致时表示医助创建订单时间"},{default:r(()=>[n(o,{class:"assign-log-col-hint"},{default:r(()=>[n(B(F))]),_:1})]),_:1})]),default:r(({row:i})=>[c(d(x(i)),1)]),_:1}),n(t,{label:"操作人",width:"110",prop:"operator_name"}),n(t,{label:"操作账号",width:"120",prop:"operator_account","show-overflow-tooltip":""}),n(t,{label:"IP",width:"130",prop:"ip","show-overflow-tooltip":""})]),_:1},8,["data"])),[[I,m.value]])])}}}),Y=q(z,[["__scopeId","data-v-f670e3e6"]]);export{Y as default};
@@ -1 +1 @@
import{o as B,R as D,q as E,O,D as I,r as _,T as S,P as x,ac as W,s as h,ad as P,v as U,K as $,L as j,bt as H,p as K,M as v,dg as c}from"./.pnpm-Dwh6tNxD.js";import{af as Y}from"./tcm-RVDaIW5j.js";import{_ as q}from"./index-C9ilrjDr.js";const z={key:0,class:"watch-state"},F={key:1,class:"watch-state watch-error"},G=B({__name:"AssistantWatchCallDialog",props:{modelValue:{type:Boolean},diagnosisId:{}},emits:["update:modelValue","closed"],setup(R,{emit:A}){const u=R,g=A,y=K({get:()=>u.modelValue,set:t=>g("update:modelValue",t)}),r=v(null),l=v(!1),i=v(""),f=v("旁观视频通话"),n=new Map;let a=null,p=0;function w(t,e){return`${t}\0${String(e)}`}function N(t){return t.startsWith("patient_")?"患者":t.startsWith("doctor_")?"医护":t}async function T(t){if(!a||!r.value||t.streamType!==c.TYPE.STREAM_TYPE_MAIN)return;const e=w(t.userId,t.streamType);if(n.has(e))return;const o=document.createElement("div");o.className="watch-tile";const s=document.createElement("div");s.className="watch-tile-cap",s.textContent=N(t.userId);const d=document.createElement("div");d.className="watch-tile-view",o.appendChild(s),o.appendChild(d),r.value.appendChild(o),n.set(e,{wrap:o,userId:t.userId,streamType:t.streamType});try{await a.startRemoteVideo({userId:t.userId,streamType:t.streamType,view:d})}catch(b){console.warn("[AssistantWatchCall] startRemoteVideo",b)}}async function V(t){if(!a)return;const e=w(t.userId,t.streamType),o=n.get(e);if(o){try{await a.stopRemoteVideo({userId:t.userId,streamType:t.streamType})}catch{}o.wrap.remove(),n.delete(e)}}function C(){a&&(a.on(c.EVENT.REMOTE_VIDEO_AVAILABLE,T),a.on(c.EVENT.REMOTE_VIDEO_UNAVAILABLE,V))}function k(){a&&(a.off(c.EVENT.REMOTE_VIDEO_AVAILABLE,T),a.off(c.EVENT.REMOTE_VIDEO_UNAVAILABLE,V))}async function m(){if(k(),a){for(const[,t]of n){try{await a.stopRemoteVideo({userId:t.userId,streamType:t.streamType})}catch{}t.wrap.remove()}n.clear(),r.value&&(r.value.innerHTML="");try{await a.exitRoom()}catch{}try{a.destroy()}catch{}a=null}else n.clear(),r.value&&(r.value.innerHTML="")}async function L(){const t=++p;if(await m(),!u.diagnosisId){i.value="诊单无效";return}l.value=!0,i.value="",f.value="旁观视频通话";try{const e=await Y({diagnosis_id:u.diagnosisId});if(t!==p)return;e.patientName&&(f.value=`旁观视频通话 · ${e.patientName}`),a=c.create(),C();const o={sdkAppId:e.sdkAppId,userId:e.userId,userSig:e.userSig,autoReceiveAudio:!0,autoReceiveVideo:!0,...e.roomId!=null&&e.roomId>0?{roomId:e.roomId}:{strRoomId:e.strRoomId}};if(!(e.roomId!=null&&e.roomId>0)&&!e.strRoomId)throw new Error("缺少房间号");if(await a.enterRoom(o),t!==p){await m();return}l.value=!1}catch(e){l.value=!1;let o="进入房间失败";if(typeof e=="string")o=e;else if(e&&typeof e=="object"){const s=e;s.msg?o=String(s.msg):s.message&&(o=String(s.message))}i.value=o,await m()}}function M(){m(),l.value=!1,i.value="",f.value="旁观视频通话",g("closed")}return D(()=>[u.modelValue,u.diagnosisId],([t,e])=>{if(!t){p++,m();return}e>0&&L()}),(t,e)=>{const o=$,s=H;return E(),O(s,{modelValue:y.value,"onUpdate:modelValue":e[1]||(e[1]=d=>y.value=d),title:f.value,width:"760px","destroy-on-close":"","append-to-body":"","close-on-click-modal":!1,class:"assistant-watch-call-dialog",onClosed:M},{footer:I(()=>[e[3]||(e[3]=h("span",{class:"watch-hint"},"仅观看,不会开启摄像头与麦克风",-1)),U(o,{type:"primary",onClick:e[0]||(e[0]=d=>y.value=!1)},{default:I(()=>[...e[2]||(e[2]=[j("离开",-1)])]),_:1})]),default:I(()=>[l.value?(E(),_("div",z,"正在连接房间…")):i.value?(E(),_("div",F,S(i.value),1)):x("",!0),W(h("div",{ref_key:"gridRef",ref:r,class:"watch-grid"},null,512),[[P,!l.value&&!i.value]])]),_:1},8,["modelValue","title"])}}}),Z=q(G,[["__scopeId","data-v-7aff665d"]]);export{Z as default}; import{o as B,R as D,q as E,O,D as I,r as _,T as S,P as x,ac as W,s as h,ad as P,v as U,K as $,L as j,bt as H,p as K,M as v,dg as c}from"./.pnpm-Dwh6tNxD.js";import{af as Y}from"./tcm-WWBqr2Vo.js";import{_ as q}from"./index-C0Q8AuuZ.js";const z={key:0,class:"watch-state"},F={key:1,class:"watch-state watch-error"},G=B({__name:"AssistantWatchCallDialog",props:{modelValue:{type:Boolean},diagnosisId:{}},emits:["update:modelValue","closed"],setup(R,{emit:A}){const u=R,g=A,y=K({get:()=>u.modelValue,set:t=>g("update:modelValue",t)}),r=v(null),l=v(!1),i=v(""),f=v("旁观视频通话"),n=new Map;let a=null,p=0;function w(t,e){return`${t}\0${String(e)}`}function N(t){return t.startsWith("patient_")?"患者":t.startsWith("doctor_")?"医护":t}async function T(t){if(!a||!r.value||t.streamType!==c.TYPE.STREAM_TYPE_MAIN)return;const e=w(t.userId,t.streamType);if(n.has(e))return;const o=document.createElement("div");o.className="watch-tile";const s=document.createElement("div");s.className="watch-tile-cap",s.textContent=N(t.userId);const d=document.createElement("div");d.className="watch-tile-view",o.appendChild(s),o.appendChild(d),r.value.appendChild(o),n.set(e,{wrap:o,userId:t.userId,streamType:t.streamType});try{await a.startRemoteVideo({userId:t.userId,streamType:t.streamType,view:d})}catch(b){console.warn("[AssistantWatchCall] startRemoteVideo",b)}}async function V(t){if(!a)return;const e=w(t.userId,t.streamType),o=n.get(e);if(o){try{await a.stopRemoteVideo({userId:t.userId,streamType:t.streamType})}catch{}o.wrap.remove(),n.delete(e)}}function C(){a&&(a.on(c.EVENT.REMOTE_VIDEO_AVAILABLE,T),a.on(c.EVENT.REMOTE_VIDEO_UNAVAILABLE,V))}function k(){a&&(a.off(c.EVENT.REMOTE_VIDEO_AVAILABLE,T),a.off(c.EVENT.REMOTE_VIDEO_UNAVAILABLE,V))}async function m(){if(k(),a){for(const[,t]of n){try{await a.stopRemoteVideo({userId:t.userId,streamType:t.streamType})}catch{}t.wrap.remove()}n.clear(),r.value&&(r.value.innerHTML="");try{await a.exitRoom()}catch{}try{a.destroy()}catch{}a=null}else n.clear(),r.value&&(r.value.innerHTML="")}async function L(){const t=++p;if(await m(),!u.diagnosisId){i.value="诊单无效";return}l.value=!0,i.value="",f.value="旁观视频通话";try{const e=await Y({diagnosis_id:u.diagnosisId});if(t!==p)return;e.patientName&&(f.value=`旁观视频通话 · ${e.patientName}`),a=c.create(),C();const o={sdkAppId:e.sdkAppId,userId:e.userId,userSig:e.userSig,autoReceiveAudio:!0,autoReceiveVideo:!0,...e.roomId!=null&&e.roomId>0?{roomId:e.roomId}:{strRoomId:e.strRoomId}};if(!(e.roomId!=null&&e.roomId>0)&&!e.strRoomId)throw new Error("缺少房间号");if(await a.enterRoom(o),t!==p){await m();return}l.value=!1}catch(e){l.value=!1;let o="进入房间失败";if(typeof e=="string")o=e;else if(e&&typeof e=="object"){const s=e;s.msg?o=String(s.msg):s.message&&(o=String(s.message))}i.value=o,await m()}}function M(){m(),l.value=!1,i.value="",f.value="旁观视频通话",g("closed")}return D(()=>[u.modelValue,u.diagnosisId],([t,e])=>{if(!t){p++,m();return}e>0&&L()}),(t,e)=>{const o=$,s=H;return E(),O(s,{modelValue:y.value,"onUpdate:modelValue":e[1]||(e[1]=d=>y.value=d),title:f.value,width:"760px","destroy-on-close":"","append-to-body":"","close-on-click-modal":!1,class:"assistant-watch-call-dialog",onClosed:M},{footer:I(()=>[e[3]||(e[3]=h("span",{class:"watch-hint"},"仅观看,不会开启摄像头与麦克风",-1)),U(o,{type:"primary",onClick:e[0]||(e[0]=d=>y.value=!1)},{default:I(()=>[...e[2]||(e[2]=[j("离开",-1)])]),_:1})]),default:I(()=>[l.value?(E(),_("div",z,"正在连接房间…")):i.value?(E(),_("div",F,S(i.value),1)):x("",!0),W(h("div",{ref_key:"gridRef",ref:r,class:"watch-grid"},null,512),[[P,!l.value&&!i.value]])]),_:1},8,["modelValue","title"])}}}),Z=q(G,[["__scopeId","data-v-7aff665d"]]);export{Z as default};
@@ -1 +1 @@
import{o as N,di as O,R as P,q as d,r as f,v as o,D as i,K as V,L as n,P as h,ac as D,O as w,bj as L,bk as j,T as u,s as y,bi as z,M as v}from"./.pnpm-Dwh6tNxD.js";import M from"./RecordingPlaybackBlock-3c8TG8YE.js";import{U as k}from"./index-DBhmZUba.js";import{i as c,_ as q}from"./index-C9ilrjDr.js";import{aj as K,ak as x,al as A}from"./tcm-RVDaIW5j.js";import"./RecordingVideoPlayer-NZrEWtaq.js";import"./file-CNQVkxcM.js";const F={class:"call-record-panel"},G={key:0,class:"call-record-toolbar"},H={class:"call-record-empty"},J={class:"call-record-empty__desc"},Q={key:0,class:"text-primary"},W={key:1,class:"text-gray-400"},X=N({__name:"CallRecordPanel",props:{diagnosisId:{},readOnly:{type:Boolean,default:!1}},setup(_,{expose:R}){const r=_,p=v(!1),g=v([]),U=O("toolbarUploadRef"),m=async()=>{if(r.diagnosisId){p.value=!0;try{g.value=await K({diagnosis_id:r.diagnosisId})||[]}catch(e){console.error(e),g.value=[]}finally{p.value=!1}}};P(()=>r.diagnosisId,()=>{m()},{immediate:!0}),R({refresh:m});function C(e){return{1:"进行中",2:"已结束",3:"未接听",4:"已取消"}[e]??"—"}async function S(e){const t=b(e);if(!t){c.msgError("上传成功但未返回视频地址");return}try{const a=await A({diagnosis_id:r.diagnosisId});await x({diagnosis_id:r.diagnosisId,call_record_id:Number((a==null?void 0:a.id)||0),file_url:t}),c.msgSuccess("视频回放上传成功"),await m()}catch(a){c.msgError((a==null?void 0:a.message)||"写入回放失败")}}async function E(e,t){const a=b(t);if(!a){c.msgError("上传成功但未返回视频地址");return}try{await x({diagnosis_id:r.diagnosisId,call_record_id:Number(e.id||0),file_url:a}),c.msgSuccess("视频回放上传成功"),await m()}catch(l){c.msgError((l==null?void 0:l.message)||"写入回放失败")}}function b(e){var t,a;return String(((t=e==null?void 0:e.data)==null?void 0:t.uri)||((a=e==null?void 0:e.data)==null?void 0:a.url)||"").trim()}return(e,t)=>{const a=V,l=j,I=L,B=z;return d(),f("div",F,[_.readOnly?h("",!0):(d(),f("div",G,[o(k,{ref_key:"toolbarUploadRef",ref:U,type:"video",direct:"",multiple:!1,limit:1,"show-progress":!0,onSuccess:S},{default:i(()=>[o(a,{type:"primary"},{default:i(()=>[...t[0]||(t[0]=[n("上传视频",-1)])]),_:1})]),_:1},512)])),D((d(),w(I,{data:g.value,border:"",stripe:""},{empty:i(()=>[y("div",H,[t[1]||(t[1]=y("div",{class:"call-record-empty__title"},"暂无通话记录",-1)),y("div",J,u(_.readOnly?"暂无录制回放数据。":"现在可以直接点击上方“上传视频”。系统会自动生成一条默认通话记录来承载回放。"),1)])]),default:i(()=>[o(l,{label:"录制回放","min-width":"320"},{default:i(({row:s})=>[o(M,{"record-id":s.id,urls:s.recording_urls_list},null,8,["record-id","urls"])]),_:1}),o(l,{label:"开始时间",width:"170",prop:"start_time_text"}),o(l,{label:"结束时间",width:"170",prop:"end_time_text"}),o(l,{label:"通话类型",width:"100"},{default:i(({row:s})=>[n(u(s.call_type===1?"语音":"视频"),1)]),_:1}),o(l,{label:"房间号",width:"180"},{default:i(({row:s})=>[s.room_id?(d(),f("span",Q,u(s.room_id),1)):(d(),f("span",W,"—"))]),_:1}),o(l,{label:"时长",width:"110",prop:"duration_text"}),o(l,{label:"状态",width:"90"},{default:i(({row:s})=>[n(u(C(s.status)),1)]),_:1}),o(l,{label:"录制",width:"100"},{default:i(({row:s})=>[n(u(s.recording_status_text||"—"),1)]),_:1}),_.readOnly?h("",!0):(d(),w(l,{key:0,label:"上传回放",width:"180"},{default:i(({row:s})=>[o(k,{type:"video",direct:"",multiple:!1,limit:1,"show-progress":!0,onSuccess:T=>E(s,T)},{default:i(()=>[o(a,{type:"primary",plain:"",size:"small"},{default:i(()=>[...t[2]||(t[2]=[n("上传视频",-1)])]),_:1})]),_:1},8,["onSuccess"])]),_:1}))]),_:1},8,["data"])),[[B,p.value]])])}}}),sa=q(X,[["__scopeId","data-v-78d5c9e4"]]);export{sa as default}; import{o as N,di as O,R as P,q as d,r as f,v as o,D as i,K as V,L as n,P as h,ac as D,O as w,bj as L,bk as j,T as u,s as y,bi as z,M as v}from"./.pnpm-Dwh6tNxD.js";import M from"./RecordingPlaybackBlock-BeEg3fMj.js";import{U as k}from"./index-4SRyrKER.js";import{i as c,_ as q}from"./index-C0Q8AuuZ.js";import{aj as K,ak as x,al as A}from"./tcm-WWBqr2Vo.js";import"./RecordingVideoPlayer-xuuHPLFC.js";import"./file-BUuEowhq.js";const F={class:"call-record-panel"},G={key:0,class:"call-record-toolbar"},H={class:"call-record-empty"},J={class:"call-record-empty__desc"},Q={key:0,class:"text-primary"},W={key:1,class:"text-gray-400"},X=N({__name:"CallRecordPanel",props:{diagnosisId:{},readOnly:{type:Boolean,default:!1}},setup(_,{expose:R}){const r=_,p=v(!1),g=v([]),U=O("toolbarUploadRef"),m=async()=>{if(r.diagnosisId){p.value=!0;try{g.value=await K({diagnosis_id:r.diagnosisId})||[]}catch(e){console.error(e),g.value=[]}finally{p.value=!1}}};P(()=>r.diagnosisId,()=>{m()},{immediate:!0}),R({refresh:m});function C(e){return{1:"进行中",2:"已结束",3:"未接听",4:"已取消"}[e]??"—"}async function S(e){const t=b(e);if(!t){c.msgError("上传成功但未返回视频地址");return}try{const a=await A({diagnosis_id:r.diagnosisId});await x({diagnosis_id:r.diagnosisId,call_record_id:Number((a==null?void 0:a.id)||0),file_url:t}),c.msgSuccess("视频回放上传成功"),await m()}catch(a){c.msgError((a==null?void 0:a.message)||"写入回放失败")}}async function E(e,t){const a=b(t);if(!a){c.msgError("上传成功但未返回视频地址");return}try{await x({diagnosis_id:r.diagnosisId,call_record_id:Number(e.id||0),file_url:a}),c.msgSuccess("视频回放上传成功"),await m()}catch(l){c.msgError((l==null?void 0:l.message)||"写入回放失败")}}function b(e){var t,a;return String(((t=e==null?void 0:e.data)==null?void 0:t.uri)||((a=e==null?void 0:e.data)==null?void 0:a.url)||"").trim()}return(e,t)=>{const a=V,l=j,I=L,B=z;return d(),f("div",F,[_.readOnly?h("",!0):(d(),f("div",G,[o(k,{ref_key:"toolbarUploadRef",ref:U,type:"video",direct:"",multiple:!1,limit:1,"show-progress":!0,onSuccess:S},{default:i(()=>[o(a,{type:"primary"},{default:i(()=>[...t[0]||(t[0]=[n("上传视频",-1)])]),_:1})]),_:1},512)])),D((d(),w(I,{data:g.value,border:"",stripe:""},{empty:i(()=>[y("div",H,[t[1]||(t[1]=y("div",{class:"call-record-empty__title"},"暂无通话记录",-1)),y("div",J,u(_.readOnly?"暂无录制回放数据。":"现在可以直接点击上方“上传视频”。系统会自动生成一条默认通话记录来承载回放。"),1)])]),default:i(()=>[o(l,{label:"录制回放","min-width":"320"},{default:i(({row:s})=>[o(M,{"record-id":s.id,urls:s.recording_urls_list},null,8,["record-id","urls"])]),_:1}),o(l,{label:"开始时间",width:"170",prop:"start_time_text"}),o(l,{label:"结束时间",width:"170",prop:"end_time_text"}),o(l,{label:"通话类型",width:"100"},{default:i(({row:s})=>[n(u(s.call_type===1?"语音":"视频"),1)]),_:1}),o(l,{label:"房间号",width:"180"},{default:i(({row:s})=>[s.room_id?(d(),f("span",Q,u(s.room_id),1)):(d(),f("span",W,"—"))]),_:1}),o(l,{label:"时长",width:"110",prop:"duration_text"}),o(l,{label:"状态",width:"90"},{default:i(({row:s})=>[n(u(C(s.status)),1)]),_:1}),o(l,{label:"录制",width:"100"},{default:i(({row:s})=>[n(u(s.recording_status_text||"—"),1)]),_:1}),_.readOnly?h("",!0):(d(),w(l,{key:0,label:"上传回放",width:"180"},{default:i(({row:s})=>[o(k,{type:"video",direct:"",multiple:!1,limit:1,"show-progress":!0,onSuccess:T=>E(s,T)},{default:i(()=>[o(a,{type:"primary",plain:"",size:"small"},{default:i(()=>[...t[2]||(t[2]=[n("上传视频",-1)])]),_:1})]),_:1},8,["onSuccess"])]),_:1}))]),_:1},8,["data"])),[[B,p.value]])])}}}),sa=q(X,[["__scopeId","data-v-78d5c9e4"]]);export{sa as default};
@@ -1 +1 @@
import{o as T,ap as V,R as I,q as o,r as l,v as a,K as N,D as i,L as c,P as k,ac as z,bi as M,u as p,O as f,bk as O,T as _,F as P,br as j,s as F,bj as R,bB as A,M as w}from"./.pnpm-Dwh6tNxD.js";import{am as q}from"./tcm-RVDaIW5j.js";import{_ as H}from"./index-C9ilrjDr.js";const K={class:"case-record-list"},Y={key:0,class:"mb-3 flex justify-end"},G={key:0},J={key:1,class:"text-gray-400"},Q={class:"void-detail text-xs text-gray-500 mt-1"},U=T({__name:"CaseRecordList",props:{diagnosisId:{type:Number,default:0},readOnly:{type:Boolean,default:!1}},emits:["view","openPrescription"],setup(y,{expose:x,emit:C}){const m=y,b=C,r=w([]),d=w(!1),u=async()=>{if(m.diagnosisId){d.value=!0;try{const t=await q({diagnosis_id:m.diagnosisId});r.value=Array.isArray(t)?t:[]}catch(t){console.error("获取病历记录失败:",t),r.value=[]}finally{d.value=!1}}},S=t=>{if(!t)return"";const e=new Date(t*1e3);return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")} ${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`},B=t=>{b("view",t)},$=()=>{b("openPrescription")};return V(()=>{u()}),I(()=>m.diagnosisId,()=>{u()}),x({refresh:u}),(t,e)=>{const h=N,n=O,v=j,E=R,D=A,L=M;return o(),l("div",K,[y.readOnly?k("",!0):(o(),l("div",Y,[a(h,{type:"primary",size:"small",onClick:$},{default:i(()=>[...e[0]||(e[0]=[c("开方",-1)])]),_:1})])),z((o(),f(E,{data:p(r),border:""},{default:i(()=>[a(n,{prop:"prescription_date",label:"就诊日期",width:"120"}),a(n,{prop:"visit_no",label:"门诊号",width:"120"}),a(n,{prop:"clinical_diagnosis",label:"临床诊断","min-width":"160","show-overflow-tooltip":""}),a(n,{label:"处方摘要","min-width":"180"},{default:i(({row:s})=>[s.herbs&&s.herbs.length?(o(),l("span",G,_(s.herbs.slice(0,3).map(g=>`${g.name}${g.dosage}`).join("、"))+_(s.herbs.length>3?"...":""),1)):(o(),l("span",J,"—"))]),_:1}),a(n,{prop:"doctor_name",label:"医师",width:"90","show-overflow-tooltip":""}),a(n,{label:"状态",width:"140",align:"center"},{default:i(({row:s})=>[s.void_status===1?(o(),l(P,{key:0},[a(v,{type:"danger",size:"small"},{default:i(()=>[...e[1]||(e[1]=[c("已作废",-1)])]),_:1}),F("div",Q,_(s.void_by_name||"—")+" "+_(S(s.void_time)),1)],64)):(o(),f(v,{key:1,type:"success",size:"small"},{default:i(()=>[...e[2]||(e[2]=[c("正常",-1)])]),_:1}))]),_:1}),a(n,{label:"操作",width:"120",fixed:"right"},{default:i(({row:s})=>[a(h,{link:"",type:"primary",size:"small",onClick:g=>B(s)},{default:i(()=>[...e[3]||(e[3]=[c(" 查看 ",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[L,p(d)]]),!p(d)&&p(r).length===0?(o(),f(D,{key:1,description:"暂无病历记录,开方后会自动显示",class:"mt-4"})):k("",!0)])}}}),ee=H(U,[["__scopeId","data-v-043d2738"]]);export{ee as default}; import{o as T,ap as V,R as I,q as o,r as l,v as a,K as N,D as i,L as c,P as k,ac as z,bi as M,u as p,O as f,bk as O,T as _,F as P,br as j,s as F,bj as R,bB as A,M as w}from"./.pnpm-Dwh6tNxD.js";import{am as q}from"./tcm-WWBqr2Vo.js";import{_ as H}from"./index-C0Q8AuuZ.js";const K={class:"case-record-list"},Y={key:0,class:"mb-3 flex justify-end"},G={key:0},J={key:1,class:"text-gray-400"},Q={class:"void-detail text-xs text-gray-500 mt-1"},U=T({__name:"CaseRecordList",props:{diagnosisId:{type:Number,default:0},readOnly:{type:Boolean,default:!1}},emits:["view","openPrescription"],setup(y,{expose:x,emit:C}){const m=y,b=C,r=w([]),d=w(!1),u=async()=>{if(m.diagnosisId){d.value=!0;try{const t=await q({diagnosis_id:m.diagnosisId});r.value=Array.isArray(t)?t:[]}catch(t){console.error("获取病历记录失败:",t),r.value=[]}finally{d.value=!1}}},S=t=>{if(!t)return"";const e=new Date(t*1e3);return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")} ${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`},B=t=>{b("view",t)},$=()=>{b("openPrescription")};return V(()=>{u()}),I(()=>m.diagnosisId,()=>{u()}),x({refresh:u}),(t,e)=>{const h=N,n=O,v=j,E=R,D=A,L=M;return o(),l("div",K,[y.readOnly?k("",!0):(o(),l("div",Y,[a(h,{type:"primary",size:"small",onClick:$},{default:i(()=>[...e[0]||(e[0]=[c("开方",-1)])]),_:1})])),z((o(),f(E,{data:p(r),border:""},{default:i(()=>[a(n,{prop:"prescription_date",label:"就诊日期",width:"120"}),a(n,{prop:"visit_no",label:"门诊号",width:"120"}),a(n,{prop:"clinical_diagnosis",label:"临床诊断","min-width":"160","show-overflow-tooltip":""}),a(n,{label:"处方摘要","min-width":"180"},{default:i(({row:s})=>[s.herbs&&s.herbs.length?(o(),l("span",G,_(s.herbs.slice(0,3).map(g=>`${g.name}${g.dosage}`).join("、"))+_(s.herbs.length>3?"...":""),1)):(o(),l("span",J,"—"))]),_:1}),a(n,{prop:"doctor_name",label:"医师",width:"90","show-overflow-tooltip":""}),a(n,{label:"状态",width:"140",align:"center"},{default:i(({row:s})=>[s.void_status===1?(o(),l(P,{key:0},[a(v,{type:"danger",size:"small"},{default:i(()=>[...e[1]||(e[1]=[c("已作废",-1)])]),_:1}),F("div",Q,_(s.void_by_name||"—")+" "+_(S(s.void_time)),1)],64)):(o(),f(v,{key:1,type:"success",size:"small"},{default:i(()=>[...e[2]||(e[2]=[c("正常",-1)])]),_:1}))]),_:1}),a(n,{label:"操作",width:"120",fixed:"right"},{default:i(({row:s})=>[a(h,{link:"",type:"primary",size:"small",onClick:g=>B(s)},{default:i(()=>[...e[3]||(e[3]=[c(" 查看 ",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[L,p(d)]]),!p(d)&&p(r).length===0?(o(),f(D,{key:1,description:"暂无病历记录,开方后会自动显示",class:"mt-4"})):k("",!0)])}}}),ee=H(U,[["__scopeId","data-v-043d2738"]]);export{ee as default};
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
import{_ as o}from"./GancaoSubmissionReconcileButton.vue_vue_type_script_setup_true_lang-BhwrYz7M.js";import"./.pnpm-Dwh6tNxD.js";import"./tcm-WWBqr2Vo.js";import"./index-C0Q8AuuZ.js";export{o as default};
@@ -1 +0,0 @@
import{_ as o}from"./GancaoSubmissionReconcileButton.vue_vue_type_script_setup_true_lang-C7bAXama.js";import"./.pnpm-Dwh6tNxD.js";import"./tcm-RVDaIW5j.js";import"./index-C9ilrjDr.js";export{o as default};
@@ -1 +1 @@
import{o as U,be as F,q as f,r as k,F as M,ac as D,O as E,D as t,L as u,K as G,v as r,bt as B,bh as q,b9 as P,aw as A,b6 as L,bc as T,bf as K,b7 as z,P as g,p as W,M as v,ba as $}from"./.pnpm-Dwh6tNxD.js";import{p as j}from"./tcm-RVDaIW5j.js";import{i as C}from"./index-C9ilrjDr.js";const X=U({__name:"GancaoSubmissionReconcileButton",props:{order:{}},emits:["resolved"],setup(N,{emit:w}){const d=N,y=w,V=W(()=>{var m,p,a;const i=String(((m=d.order)==null?void 0:m.pharmacy_claim_target)||"").trim().toLowerCase(),e=String(((p=d.order)==null?void 0:p.pharmacy_claim_status)||"").trim().toUpperCase(),n=Number(((a=d.order)==null?void 0:a.pharmacy_claim_lease_expires_at)||0),c=e==="PENDING"&&n>0&&n<=Math.floor(Date.now()/1e3);return i==="gancao"&&(c||["UNKNOWN","PENDING_RECONCILE"].includes(e))}),s=v(!1),_=v(!1),o=$({resolution:"CONFIRM_SUCCESS",remote_order_no:"",note:""});function O(){o.resolution="CONFIRM_SUCCESS",o.remote_order_no="",o.note="",s.value=!0}async function b(){var e;const i=Number((e=d.order)==null?void 0:e.id);if(i){if(o.resolution==="CONFIRM_SUCCESS"&&!o.remote_order_no.trim()){C.msgError("请填写甘草药方单号");return}if(!o.note.trim()){C.msgError("请填写甘草后台核对依据");return}_.value=!0;try{await j({id:i,resolution:o.resolution,remote_order_no:o.resolution==="CONFIRM_SUCCESS"?o.remote_order_no.trim():"",note:o.note.trim()}),C.msgSuccess("甘草提交核对已记录"),s.value=!1,y("resolved")}catch{}finally{_.value=!1}}}return(i,e)=>{const n=G,c=q,m=K,p=T,a=L,S=z,R=P,x=B,I=F("perms");return V.value?(f(),k(M,{key:0},[D((f(),E(n,{type:"danger",size:"small",plain:"",onClick:O},{default:t(()=>[...e[5]||(e[5]=[u("核对甘草提交",-1)])]),_:1})),[[I,["tcm.prescriptionOrder/confirmGancaoSubmission"]]]),r(x,{modelValue:s.value,"onUpdate:modelValue":e[4]||(e[4]=l=>s.value=l),title:"人工核对甘草提交",width:"min(92vw, 520px)","append-to-body":"","destroy-on-close":"","close-on-click-modal":!1},{footer:t(()=>[r(n,{onClick:e[3]||(e[3]=l=>s.value=!1)},{default:t(()=>[...e[8]||(e[8]=[u("取消",-1)])]),_:1}),r(n,{type:"primary",loading:_.value,onClick:b},{default:t(()=>[...e[9]||(e[9]=[u("确认并记录",-1)])]),_:1},8,["loading"])]),default:t(()=>[r(c,{title:"请先在甘草后台核对。本操作会写入不可变更的审计记录。",type:"warning",closable:!1,"show-icon":"",class:"mb-4"}),r(R,{"label-width":"100px",onSubmit:A(b,["prevent"])},{default:t(()=>[r(a,{label:"核对结果",required:""},{default:t(()=>[r(p,{modelValue:o.resolution,"onUpdate:modelValue":e[0]||(e[0]=l=>o.resolution=l)},{default:t(()=>[r(m,{label:"CONFIRM_SUCCESS"},{default:t(()=>[...e[6]||(e[6]=[u("确认已创建",-1)])]),_:1}),r(m,{label:"CONFIRM_NOT_CREATED"},{default:t(()=>[...e[7]||(e[7]=[u("确认未创建",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),o.resolution==="CONFIRM_SUCCESS"?(f(),E(a,{key:0,label:"甘草单号",required:""},{default:t(()=>[r(S,{modelValue:o.remote_order_no,"onUpdate:modelValue":e[1]||(e[1]=l=>o.remote_order_no=l),maxlength:"64",clearable:""},null,8,["modelValue"])]),_:1})):g("",!0),r(a,{label:"核对依据",required:""},{default:t(()=>[r(S,{modelValue:o.note,"onUpdate:modelValue":e[2]||(e[2]=l=>o.note=l),type:"textarea",rows:3,maxlength:"1000","show-word-limit":"",placeholder:"例如:核对时间、甘草后台查询条件及结果"},null,8,["modelValue"])]),_:1})]),_:1})]),_:1},8,["modelValue"])],64)):g("",!0)}}});export{X as _}; import{o as U,be as F,q as f,r as k,F as M,ac as D,O as E,D as t,L as u,K as G,v as r,bt as B,bh as q,b9 as P,aw as A,b6 as L,bc as T,bf as K,b7 as z,P as g,p as W,M as v,ba as $}from"./.pnpm-Dwh6tNxD.js";import{p as j}from"./tcm-WWBqr2Vo.js";import{i as C}from"./index-C0Q8AuuZ.js";const X=U({__name:"GancaoSubmissionReconcileButton",props:{order:{}},emits:["resolved"],setup(N,{emit:w}){const d=N,y=w,V=W(()=>{var m,p,a;const i=String(((m=d.order)==null?void 0:m.pharmacy_claim_target)||"").trim().toLowerCase(),e=String(((p=d.order)==null?void 0:p.pharmacy_claim_status)||"").trim().toUpperCase(),n=Number(((a=d.order)==null?void 0:a.pharmacy_claim_lease_expires_at)||0),c=e==="PENDING"&&n>0&&n<=Math.floor(Date.now()/1e3);return i==="gancao"&&(c||["UNKNOWN","PENDING_RECONCILE"].includes(e))}),s=v(!1),_=v(!1),o=$({resolution:"CONFIRM_SUCCESS",remote_order_no:"",note:""});function O(){o.resolution="CONFIRM_SUCCESS",o.remote_order_no="",o.note="",s.value=!0}async function b(){var e;const i=Number((e=d.order)==null?void 0:e.id);if(i){if(o.resolution==="CONFIRM_SUCCESS"&&!o.remote_order_no.trim()){C.msgError("请填写甘草药方单号");return}if(!o.note.trim()){C.msgError("请填写甘草后台核对依据");return}_.value=!0;try{await j({id:i,resolution:o.resolution,remote_order_no:o.resolution==="CONFIRM_SUCCESS"?o.remote_order_no.trim():"",note:o.note.trim()}),C.msgSuccess("甘草提交核对已记录"),s.value=!1,y("resolved")}catch{}finally{_.value=!1}}}return(i,e)=>{const n=G,c=q,m=K,p=T,a=L,S=z,R=P,x=B,I=F("perms");return V.value?(f(),k(M,{key:0},[D((f(),E(n,{type:"danger",size:"small",plain:"",onClick:O},{default:t(()=>[...e[5]||(e[5]=[u("核对甘草提交",-1)])]),_:1})),[[I,["tcm.prescriptionOrder/confirmGancaoSubmission"]]]),r(x,{modelValue:s.value,"onUpdate:modelValue":e[4]||(e[4]=l=>s.value=l),title:"人工核对甘草提交",width:"min(92vw, 520px)","append-to-body":"","destroy-on-close":"","close-on-click-modal":!1},{footer:t(()=>[r(n,{onClick:e[3]||(e[3]=l=>s.value=!1)},{default:t(()=>[...e[8]||(e[8]=[u("取消",-1)])]),_:1}),r(n,{type:"primary",loading:_.value,onClick:b},{default:t(()=>[...e[9]||(e[9]=[u("确认并记录",-1)])]),_:1},8,["loading"])]),default:t(()=>[r(c,{title:"请先在甘草后台核对。本操作会写入不可变更的审计记录。",type:"warning",closable:!1,"show-icon":"",class:"mb-4"}),r(R,{"label-width":"100px",onSubmit:A(b,["prevent"])},{default:t(()=>[r(a,{label:"核对结果",required:""},{default:t(()=>[r(p,{modelValue:o.resolution,"onUpdate:modelValue":e[0]||(e[0]=l=>o.resolution=l)},{default:t(()=>[r(m,{label:"CONFIRM_SUCCESS"},{default:t(()=>[...e[6]||(e[6]=[u("确认已创建",-1)])]),_:1}),r(m,{label:"CONFIRM_NOT_CREATED"},{default:t(()=>[...e[7]||(e[7]=[u("确认未创建",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),o.resolution==="CONFIRM_SUCCESS"?(f(),E(a,{key:0,label:"甘草单号",required:""},{default:t(()=>[r(S,{modelValue:o.remote_order_no,"onUpdate:modelValue":e[1]||(e[1]=l=>o.remote_order_no=l),maxlength:"64",clearable:""},null,8,["modelValue"])]),_:1})):g("",!0),r(a,{label:"核对依据",required:""},{default:t(()=>[r(S,{modelValue:o.note,"onUpdate:modelValue":e[2]||(e[2]=l=>o.note=l),type:"textarea",rows:3,maxlength:"1000","show-word-limit":"",placeholder:"例如:核对时间、甘草后台查询条件及结果"},null,8,["modelValue"])]),_:1})]),_:1})]),_:1},8,["modelValue"])],64)):g("",!0)}}});export{X as _};
@@ -1 +1 @@
import{o as F,R as q,q as t,r as n,v as i,D as c,s as o,L as _,bh as G,w as H,u as x,cV as $,K as j,by as J,ac as K,F as E,G as O,O as f,bB as W,P as v,bi as Q,M as m,p as U,ae as X,T as r,br as Z,aa as ee,bq as se,a8 as ae,E as C}from"./.pnpm-Dwh6tNxD.js";import{d as te}from"./dayjs-Cbxn44tS.js";import{ar as ne,as as oe}from"./tcm-RVDaIW5j.js";import{p as re}from"./im-business-message-parse-CVnz1EnV.js";import{_ as le}from"./index-C9ilrjDr.js";const ie={class:"im-chat-record-panel"},ce={class:"toolbar mb-3"},de={class:"chat-wrap"},_e={key:0,class:"chat-list"},ue={class:"meta"},fe={class:"name"},me={class:"time"},pe={class:"bubble"},ye={key:0,class:"friendly-text"},ge={class:"friendly-main"},ve={key:0,class:"friendly-sub"},he={key:1,class:"text-content"},we={key:2,class:"text-content"},be={key:3,class:"muted"},ke=F({__name:"ImChatRecordPanel",props:{diagnosisId:{}},setup(B,{expose:M}){const u=B,d=m(!1),p=m(!1),y=ae([]),L=m(""),g=m(""),D={text:"",image:"图片",file:"文件",sound:"语音",video:"视频",location:"位置",custom:"自定义",face:"表情",other:""},h=U(()=>y.value.map(e=>{const a=P(e);let l="";return a!=null&&a.tag?l=a.tag:l=D[e.msg_type]||(e.msg_type!=="other"?e.msg_type:""),{raw:e,friendly:a,tag:l}}));function N(e){if(e==null||!e)return"—";const a=e>1e12?Math.floor(e/1e3):e;return te.unix(a).format("YYYY-MM-DD HH:mm:ss")}function P(e){const a=(e.text||"").trim();if(!a)return null;const l=a.startsWith("{")&&(/\bbusinessID\b/.test(a)||/\bcmd\b/.test(a));return e.msg_type==="custom"||l?re(a):null}function R(e){return e.is_from_doctor?e.from_staff_name?`医生(${e.from_staff_name}`:"医生/员工":g.value?`患者(${g.value}`:"患者"}async function w(){if(u.diagnosisId){d.value=!0;try{const e=await ne({diagnosis_id:u.diagnosisId,only_archived:1});y.value=(e==null?void 0:e.lists)||[],L.value=(e==null?void 0:e.patient_im_id)||"",g.value=(e==null?void 0:e.patient_name)||""}catch(e){console.error(e),y.value=[]}finally{d.value=!1}}}function b(){w()}async function S(){if(u.diagnosisId){p.value=!0;try{await oe({diagnosis_id:u.diagnosisId}),C.success('已发起后台同步,几秒后请点击"重新加载已归档"查看新消息')}catch(e){console.error(e),C.error("发起同步失败")}finally{p.value=!1}}}return q(()=>u.diagnosisId,()=>{w()},{immediate:!0}),M({refresh:b}),(e,a)=>{const l=G,k=H,I=j,T=Z,V=ee,Y=se,z=W,A=Q;return t(),n("div",ie,[i(l,{type:"info","show-icon":"",closable:!1,class:"mb-4",title:"说明"},{default:c(()=>[...a[0]||(a[0]=[o("p",{class:"panel-tip"},[_(" 展示单聊记录:已合并患者 与 "),o("strong",null,"所有医生 / 医助账号"),_("分别产生的会话,按时间排序。 数据由后台定时任务从腾讯云 IM 漫游消息同步至本地归档;点击下方按钮可立即触发后台同步。 ")],-1)])]),_:1}),o("div",ce,[i(I,{type:"primary",link:"",loading:p.value,onClick:S},{default:c(()=>[i(k,{class:"mr-1"},{default:c(()=>[i(x($))]),_:1}),a[1]||(a[1]=_(" 同步最新(后台异步) ",-1))]),_:1},8,["loading"]),i(I,{type:"primary",link:"",loading:d.value,onClick:b},{default:c(()=>[i(k,{class:"mr-1"},{default:c(()=>[i(x(J))]),_:1}),a[2]||(a[2]=_(" 重新加载已归档 ",-1))]),_:1},8,["loading"])]),K((t(),n("div",de,[!d.value&&h.value.length?(t(),n("div",_e,[(t(!0),n(E,null,O(h.value,s=>(t(),n("div",{key:s.raw.msg_id,class:X(["chat-row",s.raw.is_from_doctor?"from-doctor":"from-patient"])},[o("div",ue,[o("span",fe,r(R(s.raw)),1),o("span",me,r(N(s.raw.time)),1),s.tag?(t(),f(T,{key:0,size:"small",type:"info",class:"ml-2"},{default:c(()=>[_(r(s.tag),1)]),_:2},1024)):v("",!0)]),o("div",pe,[s.raw.msg_type==="image"&&s.raw.image_url?(t(),f(V,{key:0,src:s.raw.image_url,"preview-src-list":[s.raw.image_url],fit:"contain",class:"chat-img"},null,8,["src","preview-src-list"])):(s.raw.msg_type==="file"||s.raw.msg_type==="sound"||s.raw.msg_type==="video")&&s.raw.file_url?(t(),f(Y,{key:1,href:s.raw.file_url,target:"_blank",type:"primary"},{default:c(()=>[_(r(s.raw.file_name||"打开文件"),1)]),_:2},1032,["href"])):(t(),n(E,{key:2},[s.friendly?(t(),n("div",ye,[o("div",ge,r(s.friendly.main),1),s.friendly.sub?(t(),n("div",ve,r(s.friendly.sub),1)):v("",!0)])):s.raw.msg_type==="text"&&s.raw.text?(t(),n("div",he,r(s.raw.text),1)):s.raw.text?(t(),n("div",we,r(s.raw.text),1)):(t(),n("span",be,"(无法展示该消息类型)"))],64))])],2))),128))])):d.value?v("",!0):(t(),f(z,{key:1,description:"暂无 IM 聊天记录"}))])),[[A,d.value]])])}}}),Me=le(ke,[["__scopeId","data-v-58b699dd"]]);export{Me as default}; import{o as F,R as q,q as t,r as n,v as i,D as c,s as o,L as _,bh as G,w as H,u as x,cV as $,K as j,by as J,ac as K,F as E,G as O,O as f,bB as W,P as v,bi as Q,M as m,p as U,ae as X,T as r,br as Z,aa as ee,bq as se,a8 as ae,E as C}from"./.pnpm-Dwh6tNxD.js";import{d as te}from"./dayjs-Cbxn44tS.js";import{ar as ne,as as oe}from"./tcm-WWBqr2Vo.js";import{p as re}from"./im-business-message-parse-CVnz1EnV.js";import{_ as le}from"./index-C0Q8AuuZ.js";const ie={class:"im-chat-record-panel"},ce={class:"toolbar mb-3"},de={class:"chat-wrap"},_e={key:0,class:"chat-list"},ue={class:"meta"},fe={class:"name"},me={class:"time"},pe={class:"bubble"},ye={key:0,class:"friendly-text"},ge={class:"friendly-main"},ve={key:0,class:"friendly-sub"},he={key:1,class:"text-content"},we={key:2,class:"text-content"},be={key:3,class:"muted"},ke=F({__name:"ImChatRecordPanel",props:{diagnosisId:{}},setup(B,{expose:M}){const u=B,d=m(!1),p=m(!1),y=ae([]),L=m(""),g=m(""),D={text:"",image:"图片",file:"文件",sound:"语音",video:"视频",location:"位置",custom:"自定义",face:"表情",other:""},h=U(()=>y.value.map(e=>{const a=P(e);let l="";return a!=null&&a.tag?l=a.tag:l=D[e.msg_type]||(e.msg_type!=="other"?e.msg_type:""),{raw:e,friendly:a,tag:l}}));function N(e){if(e==null||!e)return"—";const a=e>1e12?Math.floor(e/1e3):e;return te.unix(a).format("YYYY-MM-DD HH:mm:ss")}function P(e){const a=(e.text||"").trim();if(!a)return null;const l=a.startsWith("{")&&(/\bbusinessID\b/.test(a)||/\bcmd\b/.test(a));return e.msg_type==="custom"||l?re(a):null}function R(e){return e.is_from_doctor?e.from_staff_name?`医生(${e.from_staff_name}`:"医生/员工":g.value?`患者(${g.value}`:"患者"}async function w(){if(u.diagnosisId){d.value=!0;try{const e=await ne({diagnosis_id:u.diagnosisId,only_archived:1});y.value=(e==null?void 0:e.lists)||[],L.value=(e==null?void 0:e.patient_im_id)||"",g.value=(e==null?void 0:e.patient_name)||""}catch(e){console.error(e),y.value=[]}finally{d.value=!1}}}function b(){w()}async function S(){if(u.diagnosisId){p.value=!0;try{await oe({diagnosis_id:u.diagnosisId}),C.success('已发起后台同步,几秒后请点击"重新加载已归档"查看新消息')}catch(e){console.error(e),C.error("发起同步失败")}finally{p.value=!1}}}return q(()=>u.diagnosisId,()=>{w()},{immediate:!0}),M({refresh:b}),(e,a)=>{const l=G,k=H,I=j,T=Z,V=ee,Y=se,z=W,A=Q;return t(),n("div",ie,[i(l,{type:"info","show-icon":"",closable:!1,class:"mb-4",title:"说明"},{default:c(()=>[...a[0]||(a[0]=[o("p",{class:"panel-tip"},[_(" 展示单聊记录:已合并患者 与 "),o("strong",null,"所有医生 / 医助账号"),_("分别产生的会话,按时间排序。 数据由后台定时任务从腾讯云 IM 漫游消息同步至本地归档;点击下方按钮可立即触发后台同步。 ")],-1)])]),_:1}),o("div",ce,[i(I,{type:"primary",link:"",loading:p.value,onClick:S},{default:c(()=>[i(k,{class:"mr-1"},{default:c(()=>[i(x($))]),_:1}),a[1]||(a[1]=_(" 同步最新(后台异步) ",-1))]),_:1},8,["loading"]),i(I,{type:"primary",link:"",loading:d.value,onClick:b},{default:c(()=>[i(k,{class:"mr-1"},{default:c(()=>[i(x(J))]),_:1}),a[2]||(a[2]=_(" 重新加载已归档 ",-1))]),_:1},8,["loading"])]),K((t(),n("div",de,[!d.value&&h.value.length?(t(),n("div",_e,[(t(!0),n(E,null,O(h.value,s=>(t(),n("div",{key:s.raw.msg_id,class:X(["chat-row",s.raw.is_from_doctor?"from-doctor":"from-patient"])},[o("div",ue,[o("span",fe,r(R(s.raw)),1),o("span",me,r(N(s.raw.time)),1),s.tag?(t(),f(T,{key:0,size:"small",type:"info",class:"ml-2"},{default:c(()=>[_(r(s.tag),1)]),_:2},1024)):v("",!0)]),o("div",pe,[s.raw.msg_type==="image"&&s.raw.image_url?(t(),f(V,{key:0,src:s.raw.image_url,"preview-src-list":[s.raw.image_url],fit:"contain",class:"chat-img"},null,8,["src","preview-src-list"])):(s.raw.msg_type==="file"||s.raw.msg_type==="sound"||s.raw.msg_type==="video")&&s.raw.file_url?(t(),f(Y,{key:1,href:s.raw.file_url,target:"_blank",type:"primary"},{default:c(()=>[_(r(s.raw.file_name||"打开文件"),1)]),_:2},1032,["href"])):(t(),n(E,{key:2},[s.friendly?(t(),n("div",ye,[o("div",ge,r(s.friendly.main),1),s.friendly.sub?(t(),n("div",ve,r(s.friendly.sub),1)):v("",!0)])):s.raw.msg_type==="text"&&s.raw.text?(t(),n("div",he,r(s.raw.text),1)):s.raw.text?(t(),n("div",we,r(s.raw.text),1)):(t(),n("span",be,"(无法展示该消息类型)"))],64))])],2))),128))])):d.value?v("",!0):(t(),f(z,{key:1,description:"暂无 IM 聊天记录"}))])),[[A,d.value]])])}}}),Me=le(ke,[["__scopeId","data-v-58b699dd"]]);export{Me as default};
@@ -1 +1 @@
import{o as S,q as a,r as l,ae as x,v as r,D as m,L as d,T as i,a0 as D,s as n,O as g,br as G,P as B,aa as K,u as v,bE as L,w as q,bF as A,bG as H,bH as O,F as P,K as U,p as u}from"./.pnpm-Dwh6tNxD.js";import{t as j,_ as J}from"./index-C9ilrjDr.js";const Q={class:"msg-body"},R={class:"msg-meta"},W={class:"msg-sender"},X={class:"msg-time"},Y={key:0,class:"content-text"},Z={key:2,class:"content-pending"},ee=["src"],se={key:4,class:"content-pending"},te=["src"],ae={key:6,class:"content-pending"},ne={key:7,class:"content-file"},le={class:"file-meta"},ie=["title"],oe={class:"file-info"},ce={key:8,class:"content-fallback"},re={class:"fallback-title"},me={class:"fallback-desc"},ue=S({__name:"MessageBubble",props:{message:{}},setup(s){const o=s,w=u(()=>o.message.from_is_staff),c=u(()=>(o.message.media||[]).find(t=>t.status===1)??null),_=u(()=>{const e=o.message.from_profile;return e&&e.name||o.message.from_user}),z=u(()=>{const e=o.message.from_profile;return(e==null?void 0:e.avatar)||""}),F=u(()=>(_.value||"").slice(-2)||"?"),E=u(()=>{const e=o.message.msgtype;return e==="image"||e==="video"||e==="voice"?"bubble-media":e==="file"?"bubble-file":"bubble-text"}),C=u(()=>{switch(o.message.msgtype){case"link":return"[链接]";case"weapp":return"[小程序]";case"chatrecord":return"[聊天记录]";case"location":return"[位置]";case"card":return"[名片]";case"meeting":return"[会议]";case"docmsg":return"[文档]";case"mixed":return"[混合消息]";case"emotion":return"[表情]";default:return`[${o.message.msgtype}]`}});function M(e){return e?j(e*1e3,"mm-dd hh:MM"):""}function N(e){return e?e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:e<1024*1024*1024?`${(e/1024/1024).toFixed(2)} MB`:`${(e/1024/1024/1024).toFixed(2)} GB`:""}function $(e){window.open(e,"_blank","noopener")}return(e,t)=>{var k,y,p,h;const T=D,b=G,V=K,f=q,I=U;return a(),l("div",{class:x(["msg-bubble",{"is-staff":w.value}])},[r(T,{class:"msg-avatar",size:36,src:z.value},{default:m(()=>[d(i(F.value),1)]),_:1},8,["src"]),n("div",Q,[n("div",R,[n("span",W,i(_.value),1),n("span",X,i(M(s.message.send_time)),1),s.message.action==="recall"?(a(),g(b,{key:0,size:"small",type:"info"},{default:m(()=>[...t[1]||(t[1]=[d(" 已撤回 ",-1)])]),_:1})):B("",!0)]),n("div",{class:x(["msg-content",E.value])},[s.message.msgtype==="text"||s.message.msgtype==="markdown"?(a(),l("div",Y,i(s.message.content),1)):s.message.msgtype==="image"&&((k=c.value)!=null&&k.file_url)?(a(),g(V,{key:1,src:c.value.file_url,"preview-src-list":[c.value.file_url],fit:"contain",class:"content-image","hide-on-click-modal":""},null,8,["src","preview-src-list"])):s.message.msgtype==="image"?(a(),l("div",Z,[r(f,null,{default:m(()=>[r(v(L))]),_:1}),t[2]||(t[2]=n("span",null,"图片下载中…",-1))])):s.message.msgtype==="video"&&((y=c.value)!=null&&y.file_url)?(a(),l("video",{key:3,src:c.value.file_url,controls:"",class:"content-video"},null,8,ee)):s.message.msgtype==="video"?(a(),l("div",se,[r(f,null,{default:m(()=>[r(v(A))]),_:1}),t[3]||(t[3]=n("span",null,"视频下载中…",-1))])):s.message.msgtype==="voice"&&((p=c.value)!=null&&p.file_url)?(a(),l("audio",{key:5,src:c.value.file_url,controls:"",class:"content-audio"},null,8,te)):s.message.msgtype==="voice"?(a(),l("div",ae,[r(f,null,{default:m(()=>[r(v(H))]),_:1}),n("span",null,"语音下载中…("+i(s.message.play_length)+"s",1)])):s.message.msgtype==="file"?(a(),l("div",ne,[r(f,{size:24},{default:m(()=>[r(v(O))]),_:1}),n("div",le,[n("div",{class:"file-name",title:s.message.file_name},i(s.message.file_name||"未命名文件"),9,ie),n("div",oe,[d(i(N(s.message.file_size))+" ",1),s.message.file_ext?(a(),l(P,{key:0},[d("· "+i(s.message.file_ext),1)],64)):B("",!0)])]),(h=c.value)!=null&&h.file_url?(a(),g(I,{key:0,size:"small",type:"primary",link:"",onClick:t[0]||(t[0]=de=>$(c.value.file_url))},{default:m(()=>[...t[4]||(t[4]=[d(" 下载 ",-1)])]),_:1})):(a(),g(b,{key:1,size:"small",type:"info"},{default:m(()=>[...t[5]||(t[5]=[d("下载中…",-1)])]),_:1}))])):(a(),l("div",ce,[n("div",re,i(C.value),1),n("div",me,i(s.message.content),1)]))],2)])],2)}}}),ve=J(ue,[["__scopeId","data-v-b5ff1168"]]);export{ve as default}; import{o as S,q as a,r as l,ae as x,v as r,D as m,L as d,T as i,a0 as D,s as n,O as g,br as G,P as B,aa as K,u as v,bE as L,w as q,bF as A,bG as H,bH as O,F as P,K as U,p as u}from"./.pnpm-Dwh6tNxD.js";import{t as j,_ as J}from"./index-C0Q8AuuZ.js";const Q={class:"msg-body"},R={class:"msg-meta"},W={class:"msg-sender"},X={class:"msg-time"},Y={key:0,class:"content-text"},Z={key:2,class:"content-pending"},ee=["src"],se={key:4,class:"content-pending"},te=["src"],ae={key:6,class:"content-pending"},ne={key:7,class:"content-file"},le={class:"file-meta"},ie=["title"],oe={class:"file-info"},ce={key:8,class:"content-fallback"},re={class:"fallback-title"},me={class:"fallback-desc"},ue=S({__name:"MessageBubble",props:{message:{}},setup(s){const o=s,w=u(()=>o.message.from_is_staff),c=u(()=>(o.message.media||[]).find(t=>t.status===1)??null),_=u(()=>{const e=o.message.from_profile;return e&&e.name||o.message.from_user}),z=u(()=>{const e=o.message.from_profile;return(e==null?void 0:e.avatar)||""}),F=u(()=>(_.value||"").slice(-2)||"?"),E=u(()=>{const e=o.message.msgtype;return e==="image"||e==="video"||e==="voice"?"bubble-media":e==="file"?"bubble-file":"bubble-text"}),C=u(()=>{switch(o.message.msgtype){case"link":return"[链接]";case"weapp":return"[小程序]";case"chatrecord":return"[聊天记录]";case"location":return"[位置]";case"card":return"[名片]";case"meeting":return"[会议]";case"docmsg":return"[文档]";case"mixed":return"[混合消息]";case"emotion":return"[表情]";default:return`[${o.message.msgtype}]`}});function M(e){return e?j(e*1e3,"mm-dd hh:MM"):""}function N(e){return e?e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:e<1024*1024*1024?`${(e/1024/1024).toFixed(2)} MB`:`${(e/1024/1024/1024).toFixed(2)} GB`:""}function $(e){window.open(e,"_blank","noopener")}return(e,t)=>{var k,y,p,h;const T=D,b=G,V=K,f=q,I=U;return a(),l("div",{class:x(["msg-bubble",{"is-staff":w.value}])},[r(T,{class:"msg-avatar",size:36,src:z.value},{default:m(()=>[d(i(F.value),1)]),_:1},8,["src"]),n("div",Q,[n("div",R,[n("span",W,i(_.value),1),n("span",X,i(M(s.message.send_time)),1),s.message.action==="recall"?(a(),g(b,{key:0,size:"small",type:"info"},{default:m(()=>[...t[1]||(t[1]=[d(" 已撤回 ",-1)])]),_:1})):B("",!0)]),n("div",{class:x(["msg-content",E.value])},[s.message.msgtype==="text"||s.message.msgtype==="markdown"?(a(),l("div",Y,i(s.message.content),1)):s.message.msgtype==="image"&&((k=c.value)!=null&&k.file_url)?(a(),g(V,{key:1,src:c.value.file_url,"preview-src-list":[c.value.file_url],fit:"contain",class:"content-image","hide-on-click-modal":""},null,8,["src","preview-src-list"])):s.message.msgtype==="image"?(a(),l("div",Z,[r(f,null,{default:m(()=>[r(v(L))]),_:1}),t[2]||(t[2]=n("span",null,"图片下载中…",-1))])):s.message.msgtype==="video"&&((y=c.value)!=null&&y.file_url)?(a(),l("video",{key:3,src:c.value.file_url,controls:"",class:"content-video"},null,8,ee)):s.message.msgtype==="video"?(a(),l("div",se,[r(f,null,{default:m(()=>[r(v(A))]),_:1}),t[3]||(t[3]=n("span",null,"视频下载中…",-1))])):s.message.msgtype==="voice"&&((p=c.value)!=null&&p.file_url)?(a(),l("audio",{key:5,src:c.value.file_url,controls:"",class:"content-audio"},null,8,te)):s.message.msgtype==="voice"?(a(),l("div",ae,[r(f,null,{default:m(()=>[r(v(H))]),_:1}),n("span",null,"语音下载中…("+i(s.message.play_length)+"s",1)])):s.message.msgtype==="file"?(a(),l("div",ne,[r(f,{size:24},{default:m(()=>[r(v(O))]),_:1}),n("div",le,[n("div",{class:"file-name",title:s.message.file_name},i(s.message.file_name||"未命名文件"),9,ie),n("div",oe,[d(i(N(s.message.file_size))+" ",1),s.message.file_ext?(a(),l(P,{key:0},[d("· "+i(s.message.file_ext),1)],64)):B("",!0)])]),(h=c.value)!=null&&h.file_url?(a(),g(I,{key:0,size:"small",type:"primary",link:"",onClick:t[0]||(t[0]=de=>$(c.value.file_url))},{default:m(()=>[...t[4]||(t[4]=[d(" 下载 ",-1)])]),_:1})):(a(),g(b,{key:1,size:"small",type:"info"},{default:m(()=>[...t[5]||(t[5]=[d("下载中…",-1)])]),_:1}))])):(a(),l("div",ce,[n("div",re,i(C.value),1),n("div",me,i(s.message.content),1)]))],2)])],2)}}}),ve=J(ue,[["__scopeId","data-v-b5ff1168"]]);export{ve as default};
@@ -1,2 +1,2 @@
import{o as ie,R as oe,q as n,r as o,O as k,K as le,D as r,L as P,P as m,v as l,s as d,F as w,G as E,bB as ae,b7 as re,bt as de,p as ue,M as g,T as U,aa as me,u as V,d7 as z,aw as M,w as ce,bH as pe,e as ge}from"./.pnpm-Dwh6tNxD.js";import{_ as fe}from"./picker-DYD_beSt.js";import{e as ve,c as _e,i as f,_ as ye}from"./index-C9ilrjDr.js";import{a as T,d as he}from"./patient-SYRPmtkf.js";import{h as ke}from"./perm-3ZlDfnJ_.js";import"./index-BM6NmNnG.js";import"./index-DB4KfTa0.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-4fo3NoYp.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./usePaging-DOuAwzL9.js";const we={class:"note-timeline-wrap"},Ie={key:0,class:"timeline-actions"},Ce={class:"upload-trigger"},be={class:"upload-trigger"},xe={key:1,class:"note-timeline"},Ee={class:"timeline-date"},Ve={class:"timeline-body"},Ne={key:0,class:"timeline-content"},Se={key:1,class:"timeline-images"},De={key:2,class:"timeline-images"},Be={key:0,class:"thumb-wrap"},Ae={key:1,class:"file-wrap"},Pe=["href","title"],Ue={class:"file-name"},W=8e3,ze=ie({__name:"NoteTimeline",props:{notes:{},diagnosisId:{},readonly:{type:Boolean}},emits:["refresh"],setup(u,{emit:j}){const c=u,I=j,X=ue(()=>ke(["doctor.appointment/addDoctorNote"])),v=g(!1),C=g(""),N=g(!1),q=ve(),_=t=>q.getImageUrl(t),S=g([]),D=g([]),y=g(0),h=g(0),H=["jpg","jpeg","png","gif","bmp","webp","svg"],B=t=>{var i;const e=((i=t.split(".").pop())==null?void 0:i.toLowerCase().split("?")[0])||"";return H.includes(e)},$=t=>{var i;const e=t.split("/");return decodeURIComponent(((i=e[e.length-1])==null?void 0:i.split("?")[0])||"文件")},K=t=>t.filter(B).map(_),Z=(t,e)=>{const i=t.filter(B),b=t[e];return i.indexOf(b)},J=t=>t?t.split(` import{o as ie,R as oe,q as n,r as o,O as k,K as le,D as r,L as P,P as m,v as l,s as d,F as w,G as E,bB as ae,b7 as re,bt as de,p as ue,M as g,T as U,aa as me,u as V,d7 as z,aw as M,w as ce,bH as pe,e as ge}from"./.pnpm-Dwh6tNxD.js";import{_ as fe}from"./picker-2bpe8Dva.js";import{e as ve,c as _e,i as f,_ as ye}from"./index-C0Q8AuuZ.js";import{a as T,d as he}from"./patient-SxYcZ8aN.js";import{h as ke}from"./perm-B_zXG10L.js";import"./index-CfJcNX3X.js";import"./index-B-j4AceK.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-39SS-XKl.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./usePaging-DOuAwzL9.js";const we={class:"note-timeline-wrap"},Ie={key:0,class:"timeline-actions"},Ce={class:"upload-trigger"},be={class:"upload-trigger"},xe={key:1,class:"note-timeline"},Ee={class:"timeline-date"},Ve={class:"timeline-body"},Ne={key:0,class:"timeline-content"},Se={key:1,class:"timeline-images"},De={key:2,class:"timeline-images"},Be={key:0,class:"thumb-wrap"},Ae={key:1,class:"file-wrap"},Pe=["href","title"],Ue={class:"file-name"},W=8e3,ze=ie({__name:"NoteTimeline",props:{notes:{},diagnosisId:{},readonly:{type:Boolean}},emits:["refresh"],setup(u,{emit:j}){const c=u,I=j,X=ue(()=>ke(["doctor.appointment/addDoctorNote"])),v=g(!1),C=g(""),N=g(!1),q=ve(),_=t=>q.getImageUrl(t),S=g([]),D=g([]),y=g(0),h=g(0),H=["jpg","jpeg","png","gif","bmp","webp","svg"],B=t=>{var i;const e=((i=t.split(".").pop())==null?void 0:i.toLowerCase().split("?")[0])||"";return H.includes(e)},$=t=>{var i;const e=t.split("/");return decodeURIComponent(((i=e[e.length-1])==null?void 0:i.split("?")[0])||"文件")},K=t=>t.filter(B).map(_),Z=(t,e)=>{const i=t.filter(B),b=t[e];return i.indexOf(b)},J=t=>t?t.split(`
`).filter(Boolean):[],Q=t=>{if(!c.diagnosisId)return;const e=Array.isArray(t)?t:[t];if(e.length<=y.value){y.value=e.length;return}const i=e.slice(y.value);y.value=e.length,i.length>0&&T({diagnosis_id:c.diagnosisId,tongue_images:i}).then(()=>{f.msgSuccess("舌苔照片已添加"),I("refresh")})},Y=t=>{if(!c.diagnosisId)return;const e=Array.isArray(t)?t:[t];if(e.length<=h.value){h.value=e.length;return}const i=e.slice(h.value);h.value=e.length,i.length>0&&T({diagnosis_id:c.diagnosisId,report_files:i}).then(()=>{f.msgSuccess("检查报告已添加"),I("refresh")})};oe(()=>c.notes,()=>{S.value=[],D.value=[],y.value=0,h.value=0});const ee=async()=>{if(!c.diagnosisId){f.msgWarning("当前无诊单,无法添加备注");return}const t=C.value.trim();if(!t){f.msgWarning("请输入备注内容");return}N.value=!0;try{await T({diagnosis_id:c.diagnosisId,content:t}),f.msgSuccess("备注保存成功"),C.value="",v.value=!1,I("refresh")}catch(e){f.msgError((e==null?void 0:e.message)||"保存失败")}finally{N.value=!1}},A=async(t,e,i)=>{try{await ge.confirm("确认删除?","提示",{type:"warning"})}catch{return}await he({note_id:t,image_type:e,image_path:i}),f.msgSuccess("已删除"),I("refresh")};return(t,e)=>{const i=le,b=_e,F=fe,L=me,x=ce,te=ae,se=re,ne=de;return n(),o("div",we,[!u.readonly&&u.diagnosisId?(n(),o("div",Ie,[X.value?(n(),k(i,{key:0,type:"primary",plain:"",size:"small",onClick:e[0]||(e[0]=s=>v.value=!0)},{default:r(()=>[...e[6]||(e[6]=[P(" 添加备注 ",-1)])]),_:1})):m("",!0),l(F,{modelValue:S.value,"onUpdate:modelValue":e[1]||(e[1]=s=>S.value=s),limit:99,type:"image","exclude-domain":!0,onChange:Q},{upload:r(()=>[d("div",Ce,[l(b,{size:20,name:"el-icon-Plus"}),e[7]||(e[7]=d("span",null,"舌苔照片",-1))])]),_:1},8,["modelValue"]),l(F,{modelValue:D.value,"onUpdate:modelValue":e[2]||(e[2]=s=>D.value=s),limit:99,type:"file","exclude-domain":!0,onChange:Y},{upload:r(()=>[d("div",be,[l(b,{size:20,name:"el-icon-Plus"}),e[8]||(e[8]=d("span",null,"检查报告",-1))])]),_:1},8,["modelValue"])])):m("",!0),u.notes.length?(n(),o("div",xe,[(n(!0),o(w,null,E(u.notes,s=>{var R,G;return n(),o("div",{key:s.id,class:"timeline-node"},[e[11]||(e[11]=d("div",{class:"timeline-dot"},null,-1)),d("div",Ee,U(s.note_date),1),d("div",Ve,[s.content?(n(),o("div",Ne,[(n(!0),o(w,null,E(J(s.content),(a,p)=>(n(),o("div",{key:p,class:"content-line"},U(a),1))),128))])):m("",!0),(R=s.tongue_images)!=null&&R.length?(n(),o("div",Se,[e[9]||(e[9]=d("span",{class:"images-label"},"舌苔照片",-1)),(n(!0),o(w,null,E(s.tongue_images,(a,p)=>(n(),o("div",{key:p,class:"thumb-wrap"},[l(L,{src:_(a),"preview-src-list":s.tongue_images.map(_),"initial-index":p,"z-index":W,fit:"cover",class:"timeline-thumb","preview-teleported":""},null,8,["src","preview-src-list","initial-index"]),u.readonly?m("",!0):(n(),k(x,{key:0,class:"thumb-delete",onClick:M(O=>A(s.id,"tongue_images",a),["stop"])},{default:r(()=>[l(V(z))]),_:1},8,["onClick"]))]))),128))])):m("",!0),(G=s.report_files)!=null&&G.length?(n(),o("div",De,[e[10]||(e[10]=d("span",{class:"images-label"},"检查报告",-1)),(n(!0),o(w,null,E(s.report_files,(a,p)=>(n(),o(w,{key:p},[B(a)?(n(),o("div",Be,[l(L,{src:_(a),"preview-src-list":K(s.report_files),"initial-index":Z(s.report_files,p),"z-index":W,fit:"cover",class:"timeline-thumb","preview-teleported":""},null,8,["src","preview-src-list","initial-index"]),u.readonly?m("",!0):(n(),k(x,{key:0,class:"thumb-delete",onClick:M(O=>A(s.id,"report_files",a),["stop"])},{default:r(()=>[l(V(z))]),_:1},8,["onClick"]))])):(n(),o("div",Ae,[d("a",{href:_(a),target:"_blank",class:"file-link",title:$(a)},[l(x,{size:20},{default:r(()=>[l(V(pe))]),_:1}),d("span",Ue,U($(a)),1)],8,Pe),u.readonly?m("",!0):(n(),k(x,{key:0,class:"file-delete",onClick:M(O=>A(s.id,"report_files",a),["stop"])},{default:r(()=>[l(V(z))]),_:1},8,["onClick"]))]))],64))),128))])):m("",!0)])])}),128))])):m("",!0),!u.notes.length&&u.readonly?(n(),k(te,{key:2,description:"暂无备注","image-size":48})):m("",!0),l(ne,{modelValue:v.value,"onUpdate:modelValue":e[5]||(e[5]=s=>v.value=s),title:"添加备注",width:"480px","append-to-body":""},{footer:r(()=>[l(i,{onClick:e[4]||(e[4]=s=>v.value=!1)},{default:r(()=>[...e[12]||(e[12]=[P("取消",-1)])]),_:1}),l(i,{type:"primary",loading:N.value,onClick:ee},{default:r(()=>[...e[13]||(e[13]=[P("保存",-1)])]),_:1},8,["loading"])]),default:r(()=>[l(se,{modelValue:C.value,"onUpdate:modelValue":e[3]||(e[3]=s=>C.value=s),type:"textarea",rows:4,placeholder:"输入今日备注...",maxlength:"500","show-word-limit":""},null,8,["modelValue"])]),_:1},8,["modelValue"])])}}}),Ke=ye(ze,[["__scopeId","data-v-530ad386"]]);export{Ke as default}; `).filter(Boolean):[],Q=t=>{if(!c.diagnosisId)return;const e=Array.isArray(t)?t:[t];if(e.length<=y.value){y.value=e.length;return}const i=e.slice(y.value);y.value=e.length,i.length>0&&T({diagnosis_id:c.diagnosisId,tongue_images:i}).then(()=>{f.msgSuccess("舌苔照片已添加"),I("refresh")})},Y=t=>{if(!c.diagnosisId)return;const e=Array.isArray(t)?t:[t];if(e.length<=h.value){h.value=e.length;return}const i=e.slice(h.value);h.value=e.length,i.length>0&&T({diagnosis_id:c.diagnosisId,report_files:i}).then(()=>{f.msgSuccess("检查报告已添加"),I("refresh")})};oe(()=>c.notes,()=>{S.value=[],D.value=[],y.value=0,h.value=0});const ee=async()=>{if(!c.diagnosisId){f.msgWarning("当前无诊单,无法添加备注");return}const t=C.value.trim();if(!t){f.msgWarning("请输入备注内容");return}N.value=!0;try{await T({diagnosis_id:c.diagnosisId,content:t}),f.msgSuccess("备注保存成功"),C.value="",v.value=!1,I("refresh")}catch(e){f.msgError((e==null?void 0:e.message)||"保存失败")}finally{N.value=!1}},A=async(t,e,i)=>{try{await ge.confirm("确认删除?","提示",{type:"warning"})}catch{return}await he({note_id:t,image_type:e,image_path:i}),f.msgSuccess("已删除"),I("refresh")};return(t,e)=>{const i=le,b=_e,F=fe,L=me,x=ce,te=ae,se=re,ne=de;return n(),o("div",we,[!u.readonly&&u.diagnosisId?(n(),o("div",Ie,[X.value?(n(),k(i,{key:0,type:"primary",plain:"",size:"small",onClick:e[0]||(e[0]=s=>v.value=!0)},{default:r(()=>[...e[6]||(e[6]=[P(" 添加备注 ",-1)])]),_:1})):m("",!0),l(F,{modelValue:S.value,"onUpdate:modelValue":e[1]||(e[1]=s=>S.value=s),limit:99,type:"image","exclude-domain":!0,onChange:Q},{upload:r(()=>[d("div",Ce,[l(b,{size:20,name:"el-icon-Plus"}),e[7]||(e[7]=d("span",null,"舌苔照片",-1))])]),_:1},8,["modelValue"]),l(F,{modelValue:D.value,"onUpdate:modelValue":e[2]||(e[2]=s=>D.value=s),limit:99,type:"file","exclude-domain":!0,onChange:Y},{upload:r(()=>[d("div",be,[l(b,{size:20,name:"el-icon-Plus"}),e[8]||(e[8]=d("span",null,"检查报告",-1))])]),_:1},8,["modelValue"])])):m("",!0),u.notes.length?(n(),o("div",xe,[(n(!0),o(w,null,E(u.notes,s=>{var R,G;return n(),o("div",{key:s.id,class:"timeline-node"},[e[11]||(e[11]=d("div",{class:"timeline-dot"},null,-1)),d("div",Ee,U(s.note_date),1),d("div",Ve,[s.content?(n(),o("div",Ne,[(n(!0),o(w,null,E(J(s.content),(a,p)=>(n(),o("div",{key:p,class:"content-line"},U(a),1))),128))])):m("",!0),(R=s.tongue_images)!=null&&R.length?(n(),o("div",Se,[e[9]||(e[9]=d("span",{class:"images-label"},"舌苔照片",-1)),(n(!0),o(w,null,E(s.tongue_images,(a,p)=>(n(),o("div",{key:p,class:"thumb-wrap"},[l(L,{src:_(a),"preview-src-list":s.tongue_images.map(_),"initial-index":p,"z-index":W,fit:"cover",class:"timeline-thumb","preview-teleported":""},null,8,["src","preview-src-list","initial-index"]),u.readonly?m("",!0):(n(),k(x,{key:0,class:"thumb-delete",onClick:M(O=>A(s.id,"tongue_images",a),["stop"])},{default:r(()=>[l(V(z))]),_:1},8,["onClick"]))]))),128))])):m("",!0),(G=s.report_files)!=null&&G.length?(n(),o("div",De,[e[10]||(e[10]=d("span",{class:"images-label"},"检查报告",-1)),(n(!0),o(w,null,E(s.report_files,(a,p)=>(n(),o(w,{key:p},[B(a)?(n(),o("div",Be,[l(L,{src:_(a),"preview-src-list":K(s.report_files),"initial-index":Z(s.report_files,p),"z-index":W,fit:"cover",class:"timeline-thumb","preview-teleported":""},null,8,["src","preview-src-list","initial-index"]),u.readonly?m("",!0):(n(),k(x,{key:0,class:"thumb-delete",onClick:M(O=>A(s.id,"report_files",a),["stop"])},{default:r(()=>[l(V(z))]),_:1},8,["onClick"]))])):(n(),o("div",Ae,[d("a",{href:_(a),target:"_blank",class:"file-link",title:$(a)},[l(x,{size:20},{default:r(()=>[l(V(pe))]),_:1}),d("span",Ue,U($(a)),1)],8,Pe),u.readonly?m("",!0):(n(),k(x,{key:0,class:"file-delete",onClick:M(O=>A(s.id,"report_files",a),["stop"])},{default:r(()=>[l(V(z))]),_:1},8,["onClick"]))]))],64))),128))])):m("",!0)])])}),128))])):m("",!0),!u.notes.length&&u.readonly?(n(),k(te,{key:2,description:"暂无备注","image-size":48})):m("",!0),l(ne,{modelValue:v.value,"onUpdate:modelValue":e[5]||(e[5]=s=>v.value=s),title:"添加备注",width:"480px","append-to-body":""},{footer:r(()=>[l(i,{onClick:e[4]||(e[4]=s=>v.value=!1)},{default:r(()=>[...e[12]||(e[12]=[P("取消",-1)])]),_:1}),l(i,{type:"primary",loading:N.value,onClick:ee},{default:r(()=>[...e[13]||(e[13]=[P("保存",-1)])]),_:1},8,["loading"])]),default:r(()=>[l(se,{modelValue:C.value,"onUpdate:modelValue":e[3]||(e[3]=s=>C.value=s),type:"textarea",rows:4,placeholder:"输入今日备注...",maxlength:"500","show-word-limit":""},null,8,["modelValue"])]),_:1},8,["modelValue"])])}}}),Ke=ye(ze,[["__scopeId","data-v-530ad386"]]);export{Ke as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{m as I,f as p,a as w}from"./diag-display-DCz_VAqj.js";import{o as y,q as x,r as B,s as e,T as a,u as i,P as N}from"./.pnpm-Dwh6tNxD.js";import{_ as V}from"./index-C9ilrjDr.js";const b={class:"card patient-card"},q={class:"patient-hero"},D={class:"patient-name"},E={class:"patient-meta"},G={key:0},S=y({__name:"PatientInfoCard",props:{apt:{default:()=>({})},diag:{default:()=>({})}},setup(t){return(T,n)=>{var d,s,o,c,m,l,r,g,u,f,h,v,k,P,C;return x(),B("div",b,[n[0]||(n[0]=e("div",{class:"card-title"},"患者信息",-1)),e("div",q,[e("div",D,a(((d=t.apt)==null?void 0:d.patient_name)||"—"),1),e("div",E,[e("div",null,a(i(I)((s=t.apt)==null?void 0:s.patient_phone))+" · "+a(i(p)((o=t.diag)==null?void 0:o.gender))+" · "+a(((c=t.diag)==null?void 0:c.age)!=null?t.diag.age+"岁":"—"),1),e("div",null,a((m=t.diag)!=null&&m.height?t.diag.height+"cm":"—")+" / "+a((l=t.diag)!=null&&l.weight?t.diag.weight+"kg":"—")+" · "+a(((r=t.diag)==null?void 0:r.region)||"—"),1),e("div",null," 预约:"+a((g=t.apt)==null?void 0:g.appointment_date)+" "+a((u=t.apt)==null?void 0:u.appointment_time)+" · "+a(i(w)((f=t.apt)==null?void 0:f.period)),1),e("div",null,"医生:"+a(((h=t.apt)==null?void 0:h.doctor_name)||"—")+" 客服:"+a(((v=t.apt)==null?void 0:v.assistant_name)||"—"),1),e("div",null," 状态:"+a(((k=t.apt)==null?void 0:k.status_desc)||"—")+" · "+a((P=t.apt)!=null&&P.has_prescription?"已开方":"未开方"),1),(C=t.apt)!=null&&C.remark?(x(),B("div",G,"备注:"+a(t.apt.remark),1)):N("",!0)])])])}}}),F=V(S,[["__scopeId","data-v-1b0de09c"]]);export{F as default}; import{m as I,f as p,a as w}from"./diag-display-DCz_VAqj.js";import{o as y,q as x,r as B,s as e,T as a,u as i,P as N}from"./.pnpm-Dwh6tNxD.js";import{_ as V}from"./index-C0Q8AuuZ.js";const b={class:"card patient-card"},q={class:"patient-hero"},D={class:"patient-name"},E={class:"patient-meta"},G={key:0},S=y({__name:"PatientInfoCard",props:{apt:{default:()=>({})},diag:{default:()=>({})}},setup(t){return(T,n)=>{var d,s,o,c,m,l,r,g,u,f,h,v,k,P,C;return x(),B("div",b,[n[0]||(n[0]=e("div",{class:"card-title"},"患者信息",-1)),e("div",q,[e("div",D,a(((d=t.apt)==null?void 0:d.patient_name)||"—"),1),e("div",E,[e("div",null,a(i(I)((s=t.apt)==null?void 0:s.patient_phone))+" · "+a(i(p)((o=t.diag)==null?void 0:o.gender))+" · "+a(((c=t.diag)==null?void 0:c.age)!=null?t.diag.age+"岁":"—"),1),e("div",null,a((m=t.diag)!=null&&m.height?t.diag.height+"cm":"—")+" / "+a((l=t.diag)!=null&&l.weight?t.diag.weight+"kg":"—")+" · "+a(((r=t.diag)==null?void 0:r.region)||"—"),1),e("div",null," 预约:"+a((g=t.apt)==null?void 0:g.appointment_date)+" "+a((u=t.apt)==null?void 0:u.appointment_time)+" · "+a(i(w)((f=t.apt)==null?void 0:f.period)),1),e("div",null,"医生:"+a(((h=t.apt)==null?void 0:h.doctor_name)||"—")+" 客服:"+a(((v=t.apt)==null?void 0:v.assistant_name)||"—"),1),e("div",null," 状态:"+a(((k=t.apt)==null?void 0:k.status_desc)||"—")+" · "+a((P=t.apt)!=null&&P.has_prescription?"已开方":"未开方"),1),(C=t.apt)!=null&&C.remark?(x(),B("div",G,"备注:"+a(t.apt.remark),1)):N("",!0)])])])}}}),F=V(S,[["__scopeId","data-v-1b0de09c"]]);export{F as default};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{o as L,q as i,r as c,s as d,F as v,O as m,bq as q,D as _,L as k,P as y,G as B,p as f,T as w}from"./.pnpm-Dwh6tNxD.js";import H from"./RecordingVideoPlayer-NZrEWtaq.js";import{e as I,_ as P}from"./index-C9ilrjDr.js";const R={key:0,class:"recording-list"},C={class:"recording-item"},V={key:1,class:"recording-alternates"},N={class:"recording-alternates__links"},D={key:1,class:"text-gray-400"},E=L({__name:"RecordingPlaybackBlock",props:{recordId:{},urls:{}},setup(l){const $=l,h=I(),u=f(()=>{const e=$.urls||[],t=new Set,r=[];for(const o of e){const s=String(o??"").trim();!s||t.has(s)||(t.add(s),r.push(s))}return r}),a=f(()=>{const e=u.value;if(!e.length)return null;const t=e.find(n=>/\.mp4(\?|#|$)/i.test(n));if(t)return t;const r=e.find(n=>/\.m3u8(\?|#|$)/i.test(n)&&/vod-qcloud\.com/i.test(n));if(r)return r;const o=e.find(n=>/\.m3u8(\?|#|$)/i.test(n)&&/\.cos\.[^/]+\.myqcloud\.com/i.test(n));if(o)return o;const s=e.find(n=>/\.m3u8(\?|#|$)/i.test(n));return s||e[0]}),p=f(()=>{const e=u.value,t=a.value;return t?e.filter(r=>r!==t):e.slice(1)});function b(e){if(!e||typeof e!="string")return!1;const t=e.trim();return/^https?:\/\//i.test(t)?/\.(mp4|webm|ogg|mov|mkv|m4v|m3u8)(\?|#|$)/i.test(t):/\.(mp4|webm|ogg|mov|mkv|m4v|m3u8)(\?|#|$)/i.test(t)||t.startsWith("/uploads/")}function g(e){return h.getImageUrl(String(e||"").trim())}function S(e,t){const r=e.trim();return/\.mp4(\?|#|$)/i.test(r)?`MP4 ${t+1}`:/vod-qcloud\.com/i.test(r)&&/\.m3u8/i.test(r)?`点播 ${t+1}`:/\.cos\.[^/]+\.myqcloud\.com/i.test(r)&&/\.m3u8/i.test(r)?`COS HLS ${t+1}`:/\.m3u8/i.test(r)?`HLS ${t+1}`:`链接 ${t+1}`}return(e,t)=>{const r=q;return u.value.length?(i(),c("div",R,[d("div",C,[a.value?(i(),c(v,{key:0},[b(a.value)?(i(),m(H,{key:`${l.recordId}-${a.value}`,src:a.value},null,8,["src"])):(i(),m(r,{key:1,href:g(a.value),target:"_blank",type:"primary"},{default:_(()=>[...t[0]||(t[0]=[k(" 打开回放 ",-1)])]),_:1},8,["href"]))],64)):y("",!0),p.value.length?(i(),c("div",V,[t[1]||(t[1]=d("span",{class:"recording-alternates__label"},"备用地址",-1)),d("div",N,[(i(!0),c(v,null,B(p.value,(o,s)=>(i(),m(r,{key:`${l.recordId}-alt-${s}-${o.slice(-32)}`,href:g(o),target:"_blank",type:"primary",class:"recording-alternates__link"},{default:_(()=>[k(w(S(o,s)),1)]),_:2},1032,["href"]))),128))])])):y("",!0)])])):(i(),c("span",D,"暂无"))}}}),x=P(E,[["__scopeId","data-v-d67b2e91"]]);export{x as default}; import{o as L,q as i,r as c,s as d,F as v,O as m,bq as q,D as _,L as k,P as y,G as B,p as f,T as w}from"./.pnpm-Dwh6tNxD.js";import H from"./RecordingVideoPlayer-xuuHPLFC.js";import{e as I,_ as P}from"./index-C0Q8AuuZ.js";const R={key:0,class:"recording-list"},C={class:"recording-item"},V={key:1,class:"recording-alternates"},N={class:"recording-alternates__links"},D={key:1,class:"text-gray-400"},E=L({__name:"RecordingPlaybackBlock",props:{recordId:{},urls:{}},setup(l){const $=l,h=I(),u=f(()=>{const e=$.urls||[],t=new Set,r=[];for(const o of e){const s=String(o??"").trim();!s||t.has(s)||(t.add(s),r.push(s))}return r}),a=f(()=>{const e=u.value;if(!e.length)return null;const t=e.find(n=>/\.mp4(\?|#|$)/i.test(n));if(t)return t;const r=e.find(n=>/\.m3u8(\?|#|$)/i.test(n)&&/vod-qcloud\.com/i.test(n));if(r)return r;const o=e.find(n=>/\.m3u8(\?|#|$)/i.test(n)&&/\.cos\.[^/]+\.myqcloud\.com/i.test(n));if(o)return o;const s=e.find(n=>/\.m3u8(\?|#|$)/i.test(n));return s||e[0]}),p=f(()=>{const e=u.value,t=a.value;return t?e.filter(r=>r!==t):e.slice(1)});function b(e){if(!e||typeof e!="string")return!1;const t=e.trim();return/^https?:\/\//i.test(t)?/\.(mp4|webm|ogg|mov|mkv|m4v|m3u8)(\?|#|$)/i.test(t):/\.(mp4|webm|ogg|mov|mkv|m4v|m3u8)(\?|#|$)/i.test(t)||t.startsWith("/uploads/")}function g(e){return h.getImageUrl(String(e||"").trim())}function S(e,t){const r=e.trim();return/\.mp4(\?|#|$)/i.test(r)?`MP4 ${t+1}`:/vod-qcloud\.com/i.test(r)&&/\.m3u8/i.test(r)?`点播 ${t+1}`:/\.cos\.[^/]+\.myqcloud\.com/i.test(r)&&/\.m3u8/i.test(r)?`COS HLS ${t+1}`:/\.m3u8/i.test(r)?`HLS ${t+1}`:`链接 ${t+1}`}return(e,t)=>{const r=q;return u.value.length?(i(),c("div",R,[d("div",C,[a.value?(i(),c(v,{key:0},[b(a.value)?(i(),m(H,{key:`${l.recordId}-${a.value}`,src:a.value},null,8,["src"])):(i(),m(r,{key:1,href:g(a.value),target:"_blank",type:"primary"},{default:_(()=>[...t[0]||(t[0]=[k(" 打开回放 ",-1)])]),_:1},8,["href"]))],64)):y("",!0),p.value.length?(i(),c("div",V,[t[1]||(t[1]=d("span",{class:"recording-alternates__label"},"备用地址",-1)),d("div",N,[(i(!0),c(v,null,B(p.value,(o,s)=>(i(),m(r,{key:`${l.recordId}-alt-${s}-${o.slice(-32)}`,href:g(o),target:"_blank",type:"primary",class:"recording-alternates__link"},{default:_(()=>[k(w(S(o,s)),1)]),_:2},1032,["href"]))),128))])])):y("",!0)])])):(i(),c("span",D,"暂无"))}}}),x=P(E,[["__scopeId","data-v-d67b2e91"]]);export{x as default};
@@ -1,2 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/.pnpm-Dwh6tNxD.js","assets/.pnpm-B3v8nGpq.css"])))=>i.map(i=>d[i]); const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/.pnpm-Dwh6tNxD.js","assets/.pnpm-B3v8nGpq.css"])))=>i.map(i=>d[i]);
import{o as K,ap as z,n as S,R as H,aq as G,q as v,r as p,s as f,ac as O,ad as W,b8 as $,aw as j,F as w,v as k,D as I,u as J,b5 as Q,w as X,P,L as Y,bq as Z,M as _,p as E,al as ee}from"./.pnpm-Dwh6tNxD.js";import{e as ae,_ as ne}from"./index-C9ilrjDr.js";const te={class:"recording-playback__shell"},re=["preload"],oe=["onKeydown"],le=K({__name:"RecordingVideoPlayer",props:{src:{}},setup(B){const V=B,A=ae(),b=_(null),m=_(null),c=_(!1),s=_(!1),t=_(!1);let d=null,r=null,l=0,u=null;function i(){u!==null&&(clearTimeout(u),u=null)}const y=E(()=>{const e=String(V.src||"").trim();return e?N(A.getImageUrl(e)):""}),D=E(()=>{if(!s.value)return"none";const e=y.value;return e&&!L(e)?"metadata":"none"}),q=E(()=>!s.value||t.value);function N(e){if(!e||!/^https?:\/\//i.test(e))return e;try{const a=new URL(e),n=a.hostname.toLowerCase();if(n.includes("myqcloud.com")||n.includes("vod-qcloud.com")||n.includes("vod-qcloud"))return a.pathname.includes("+")&&(a.pathname=a.pathname.replace(/\+/g,"%2B")),a.href}catch{}return e}function L(e){return/\.m3u8(\?|#|$)/i.test(e)}function U(){var n;t.value=!1,i();const e=m.value,a=(n=e==null?void 0:e.error)==null?void 0:n.code;a===MediaError.MEDIA_ERR_ABORTED||a===1||(c.value=!0)}function h(){d&&(d.destroy(),d=null);const e=m.value;e&&(e.pause(),e.removeAttribute("src"),e.load())}function R(e,a){let n=!1;const o=()=>{n||a!==l||(n=!0,i(),t.value=!1)};e.addEventListener("loadedmetadata",()=>o(),{once:!0}),e.addEventListener("loadeddata",()=>o(),{once:!0}),e.addEventListener("canplay",()=>o(),{once:!0}),e.addEventListener("playing",()=>o(),{once:!0}),e.addEventListener("progress",()=>{try{e.buffered.length>0&&e.buffered.end(0)>0&&o("progress-buffered")}catch{}},{passive:!0})}async function g(){if(!s.value)return;await S();const e=m.value,a=y.value;if(!e)return;if(!a){t.value=!1,i();return}const n=++l;if(t.value=!0,i(),h(),c.value=!1,L(a)){try{const o=await ee(()=>import("./.pnpm-Dwh6tNxD.js").then(M=>M.dN),__vite__mapDeps([0,1])),x=o.default;if(x.isSupported()){d=new x({enableWorker:!0,lowLatencyMode:!1,maxBufferLength:20,maxMaxBufferLength:120}),d.on(o.Events.MANIFEST_PARSED,()=>{n===l&&(t.value=!1,i())}),d.on(o.Events.ERROR,(M,F)=>{if(F.fatal){if(n!==l)return;t.value=!1,i(),c.value=!0,h()}}),d.loadSource(a),d.attachMedia(e),u=setTimeout(()=>{u=null,n===l&&t.value&&(t.value=!1)},45e3);return}if(e.canPlayType("application/vnd.apple.mpegurl")){R(e,n),e.src=a,u=setTimeout(()=>{u=null,n===l&&t.value&&(t.value=!1)},45e3);return}}catch{if(n!==l)return;t.value=!1,i(),c.value=!0;return}if(n!==l)return;t.value=!1,i(),c.value=!0;return}R(e,n),e.src=a,u=setTimeout(()=>{u=null,n===l&&t.value&&(t.value=!1)},25e3)}function C(){const e=b.value;if(!e||typeof IntersectionObserver>"u"){s.value=!0,g();return}r=new IntersectionObserver(a=>{for(const n of a)if(n.isIntersecting){r==null||r.disconnect(),r=null,s.value=!0,g();break}},{root:null,rootMargin:"240px 0px",threshold:0}),r.observe(e)}function T(){r==null||r.disconnect(),r=null,s.value||(s.value=!0),g()}return z(()=>{S(()=>{C()})}),H(y,()=>{s.value&&g()}),G(()=>{r==null||r.disconnect(),r=null,i(),l++,h()}),(e,a)=>{const n=X,o=Z;return v(),p("div",{ref_key:"wrapRef",ref:b,class:"recording-playback"},[f("div",te,[O(f("video",{ref_key:"videoRef",ref:m,class:"recording-video",controls:"",playsinline:"","webkit-playsinline":"",preload:D.value,onError:U},null,40,re),[[W,!c.value]]),!c.value&&q.value?(v(),p("div",{key:0,class:"recording-playback__overlay",role:"button",tabindex:"0",onClick:T,onKeydown:$(j(T,["prevent"]),["enter"])},[s.value?(v(),p(w,{key:1},[k(n,{class:"recording-playback__spin"},{default:I(()=>[k(J(Q))]),_:1}),a[2]||(a[2]=f("span",null,"正在加载…",-1))],64)):(v(),p(w,{key:0},[a[0]||(a[0]=f("span",{class:"recording-playback__overlay-title"},"预览待加载",-1)),a[1]||(a[1]=f("span",{class:"recording-playback__overlay-desc"},"滚动至此处将自动加载(减轻并发卡顿),也可点击立即加载",-1))],64))],40,oe)):P("",!0)]),c.value?(v(),p(w,{key:0},[k(o,{href:y.value||"#",target:"_blank",type:"primary"},{default:I(()=>[...a[3]||(a[3]=[Y("在新窗口打开",-1)])]),_:1},8,["href"]),a[4]||(a[4]=f("div",{class:"recording-playback__hint"}," 内嵌播放失败(编码不支持、跨域或私有存储等)时可尝试在新窗口打开。 ",-1))],64)):P("",!0)],512)}}}),ie=ne(le,[["__scopeId","data-v-749b0199"]]);export{ie as default}; import{o as K,ap as z,n as S,R as H,aq as G,q as v,r as p,s as f,ac as O,ad as W,b8 as $,aw as j,F as w,v as k,D as I,u as J,b5 as Q,w as X,P,L as Y,bq as Z,M as _,p as E,al as ee}from"./.pnpm-Dwh6tNxD.js";import{e as ae,_ as ne}from"./index-C0Q8AuuZ.js";const te={class:"recording-playback__shell"},re=["preload"],oe=["onKeydown"],le=K({__name:"RecordingVideoPlayer",props:{src:{}},setup(B){const V=B,A=ae(),b=_(null),m=_(null),c=_(!1),s=_(!1),t=_(!1);let d=null,r=null,l=0,u=null;function i(){u!==null&&(clearTimeout(u),u=null)}const y=E(()=>{const e=String(V.src||"").trim();return e?N(A.getImageUrl(e)):""}),D=E(()=>{if(!s.value)return"none";const e=y.value;return e&&!L(e)?"metadata":"none"}),q=E(()=>!s.value||t.value);function N(e){if(!e||!/^https?:\/\//i.test(e))return e;try{const a=new URL(e),n=a.hostname.toLowerCase();if(n.includes("myqcloud.com")||n.includes("vod-qcloud.com")||n.includes("vod-qcloud"))return a.pathname.includes("+")&&(a.pathname=a.pathname.replace(/\+/g,"%2B")),a.href}catch{}return e}function L(e){return/\.m3u8(\?|#|$)/i.test(e)}function U(){var n;t.value=!1,i();const e=m.value,a=(n=e==null?void 0:e.error)==null?void 0:n.code;a===MediaError.MEDIA_ERR_ABORTED||a===1||(c.value=!0)}function h(){d&&(d.destroy(),d=null);const e=m.value;e&&(e.pause(),e.removeAttribute("src"),e.load())}function R(e,a){let n=!1;const o=()=>{n||a!==l||(n=!0,i(),t.value=!1)};e.addEventListener("loadedmetadata",()=>o(),{once:!0}),e.addEventListener("loadeddata",()=>o(),{once:!0}),e.addEventListener("canplay",()=>o(),{once:!0}),e.addEventListener("playing",()=>o(),{once:!0}),e.addEventListener("progress",()=>{try{e.buffered.length>0&&e.buffered.end(0)>0&&o("progress-buffered")}catch{}},{passive:!0})}async function g(){if(!s.value)return;await S();const e=m.value,a=y.value;if(!e)return;if(!a){t.value=!1,i();return}const n=++l;if(t.value=!0,i(),h(),c.value=!1,L(a)){try{const o=await ee(()=>import("./.pnpm-Dwh6tNxD.js").then(M=>M.dN),__vite__mapDeps([0,1])),x=o.default;if(x.isSupported()){d=new x({enableWorker:!0,lowLatencyMode:!1,maxBufferLength:20,maxMaxBufferLength:120}),d.on(o.Events.MANIFEST_PARSED,()=>{n===l&&(t.value=!1,i())}),d.on(o.Events.ERROR,(M,F)=>{if(F.fatal){if(n!==l)return;t.value=!1,i(),c.value=!0,h()}}),d.loadSource(a),d.attachMedia(e),u=setTimeout(()=>{u=null,n===l&&t.value&&(t.value=!1)},45e3);return}if(e.canPlayType("application/vnd.apple.mpegurl")){R(e,n),e.src=a,u=setTimeout(()=>{u=null,n===l&&t.value&&(t.value=!1)},45e3);return}}catch{if(n!==l)return;t.value=!1,i(),c.value=!0;return}if(n!==l)return;t.value=!1,i(),c.value=!0;return}R(e,n),e.src=a,u=setTimeout(()=>{u=null,n===l&&t.value&&(t.value=!1)},25e3)}function C(){const e=b.value;if(!e||typeof IntersectionObserver>"u"){s.value=!0,g();return}r=new IntersectionObserver(a=>{for(const n of a)if(n.isIntersecting){r==null||r.disconnect(),r=null,s.value=!0,g();break}},{root:null,rootMargin:"240px 0px",threshold:0}),r.observe(e)}function T(){r==null||r.disconnect(),r=null,s.value||(s.value=!0),g()}return z(()=>{S(()=>{C()})}),H(y,()=>{s.value&&g()}),G(()=>{r==null||r.disconnect(),r=null,i(),l++,h()}),(e,a)=>{const n=X,o=Z;return v(),p("div",{ref_key:"wrapRef",ref:b,class:"recording-playback"},[f("div",te,[O(f("video",{ref_key:"videoRef",ref:m,class:"recording-video",controls:"",playsinline:"","webkit-playsinline":"",preload:D.value,onError:U},null,40,re),[[W,!c.value]]),!c.value&&q.value?(v(),p("div",{key:0,class:"recording-playback__overlay",role:"button",tabindex:"0",onClick:T,onKeydown:$(j(T,["prevent"]),["enter"])},[s.value?(v(),p(w,{key:1},[k(n,{class:"recording-playback__spin"},{default:I(()=>[k(J(Q))]),_:1}),a[2]||(a[2]=f("span",null,"正在加载…",-1))],64)):(v(),p(w,{key:0},[a[0]||(a[0]=f("span",{class:"recording-playback__overlay-title"},"预览待加载",-1)),a[1]||(a[1]=f("span",{class:"recording-playback__overlay-desc"},"滚动至此处将自动加载(减轻并发卡顿),也可点击立即加载",-1))],64))],40,oe)):P("",!0)]),c.value?(v(),p(w,{key:0},[k(o,{href:y.value||"#",target:"_blank",type:"primary"},{default:I(()=>[...a[3]||(a[3]=[Y("在新窗口打开",-1)])]),_:1},8,["href"]),a[4]||(a[4]=f("div",{class:"recording-playback__hint"}," 内嵌播放失败(编码不支持、跨域或私有存储等)时可尝试在新窗口打开。 ",-1))],64)):P("",!0)],512)}}}),ie=ne(le,[["__scopeId","data-v-749b0199"]]);export{ie as default};
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
import{o as T,q as e,r as t,v as m,b7 as V,s as d,D as w,L as I,K as E,P as r,F as u,G as v,T as g,O as C,bB as z,M as p}from"./.pnpm-Dwh6tNxD.js";import{a4 as L}from"./tcm-RVDaIW5j.js";import{i as M,_ as S}from"./index-C9ilrjDr.js";/* empty css */const D={class:"tracking-timeline-wrap"},F={key:0,class:"timeline-input"},H={class:"input-actions"},q={key:1,class:"tracking-timeline"},A={class:"timeline-date"},G={class:"timeline-body"},K={key:0,class:"timeline-content"},O=T({__name:"TrackingNoteTimeline",props:{notes:{},diagnosisId:{},readonly:{type:Boolean}},emits:["refresh"],setup(n,{emit:f}){const c=n,y=f,o=p(""),l=p(!1),_=s=>s?s.split(` import{o as T,q as e,r as t,v as m,b7 as V,s as d,D as w,L as I,K as E,P as r,F as u,G as v,T as g,O as C,bB as z,M as p}from"./.pnpm-Dwh6tNxD.js";import{a4 as L}from"./tcm-WWBqr2Vo.js";import{i as M,_ as S}from"./index-C0Q8AuuZ.js";/* empty css */const D={class:"tracking-timeline-wrap"},F={key:0,class:"timeline-input"},H={class:"input-actions"},q={key:1,class:"tracking-timeline"},A={class:"timeline-date"},G={class:"timeline-body"},K={key:0,class:"timeline-content"},O=T({__name:"TrackingNoteTimeline",props:{notes:{},diagnosisId:{},readonly:{type:Boolean}},emits:["refresh"],setup(n,{emit:f}){const c=n,y=f,o=p(""),l=p(!1),_=s=>s?s.split(`
`).filter(Boolean):[],k=async()=>{if(!c.diagnosisId)return;const s=o.value.trim();if(s){l.value=!0;try{await L({diagnosis_id:c.diagnosisId,tracking_content:s}),M.msgSuccess("已添加"),o.value="",y("refresh")}finally{l.value=!1}}};return(s,i)=>{const b=V,h=E,B=z;return e(),t("div",D,[!n.readonly&&n.diagnosisId?(e(),t("div",F,[m(b,{modelValue:o.value,"onUpdate:modelValue":i[0]||(i[0]=a=>o.value=a),type:"textarea",rows:2,placeholder:"输入跟踪备注,回车换行;保存后将以「[HH:MM] 内容」追加到当天记录",maxlength:"1000","show-word-limit":"",resize:"none",disabled:l.value},null,8,["modelValue","disabled"]),d("div",H,[m(h,{type:"primary",size:"small",loading:l.value,disabled:!o.value.trim(),onClick:k},{default:w(()=>[...i[1]||(i[1]=[I(" 添加 ",-1)])]),_:1},8,["loading","disabled"])])])):r("",!0),n.notes.length?(e(),t("div",q,[(e(!0),t(u,null,v(n.notes,a=>(e(),t("div",{key:a.id,class:"timeline-node"},[i[2]||(i[2]=d("div",{class:"timeline-dot"},null,-1)),d("div",A,g(a.note_date),1),d("div",G,[a.content?(e(),t("div",K,[(e(!0),t(u,null,v(_(a.content),(x,N)=>(e(),t("div",{key:N,class:"content-line"},g(x),1))),128))])):r("",!0)])]))),128))])):r("",!0),!n.notes.length&&n.readonly?(e(),C(B,{key:2,description:"暂无跟踪备注","image-size":48})):r("",!0)])}}}),Q=S(O,[["__scopeId","data-v-82b635bd"]]);export{Q as default}; `).filter(Boolean):[],k=async()=>{if(!c.diagnosisId)return;const s=o.value.trim();if(s){l.value=!0;try{await L({diagnosis_id:c.diagnosisId,tracking_content:s}),M.msgSuccess("已添加"),o.value="",y("refresh")}finally{l.value=!1}}};return(s,i)=>{const b=V,h=E,B=z;return e(),t("div",D,[!n.readonly&&n.diagnosisId?(e(),t("div",F,[m(b,{modelValue:o.value,"onUpdate:modelValue":i[0]||(i[0]=a=>o.value=a),type:"textarea",rows:2,placeholder:"输入跟踪备注,回车换行;保存后将以「[HH:MM] 内容」追加到当天记录",maxlength:"1000","show-word-limit":"",resize:"none",disabled:l.value},null,8,["modelValue","disabled"]),d("div",H,[m(h,{type:"primary",size:"small",loading:l.value,disabled:!o.value.trim(),onClick:k},{default:w(()=>[...i[1]||(i[1]=[I(" 添加 ",-1)])]),_:1},8,["loading","disabled"])])])):r("",!0),n.notes.length?(e(),t("div",q,[(e(!0),t(u,null,v(n.notes,a=>(e(),t("div",{key:a.id,class:"timeline-node"},[i[2]||(i[2]=d("div",{class:"timeline-dot"},null,-1)),d("div",A,g(a.note_date),1),d("div",G,[a.content?(e(),t("div",K,[(e(!0),t(u,null,v(_(a.content),(x,N)=>(e(),t("div",{key:N,class:"content-line"},g(x),1))),128))])):r("",!0)])]))),128))])):r("",!0),!n.notes.length&&n.readonly?(e(),C(B,{key:2,description:"暂无跟踪备注","image-size":48})):r("",!0)])}}}),Q=S(O,[["__scopeId","data-v-82b635bd"]]);export{Q as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{_ as o}from"./account-adjust.vue_vue_type_script_setup_true_lang-D0MhgEI6.js";import"./.pnpm-Dwh6tNxD.js";import"./index-BM6NmNnG.js";import"./index-C9ilrjDr.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./account-adjust.vue_vue_type_script_setup_true_lang-BCHVvk0O.js";import"./.pnpm-Dwh6tNxD.js";import"./index-CfJcNX3X.js";import"./index-C0Q8AuuZ.js";export{o as default};
@@ -1 +1 @@
import{o as h,R as v,q,O as B,D as l,s as D,v as t,b9 as F,u as n,b6 as I,L as u,T as w,bc as j,bf as S,b7 as T,a8 as y,ba as U,p as G}from"./.pnpm-Dwh6tNxD.js";import{_ as L}from"./index-BM6NmNnG.js";import{i as V}from"./index-C9ilrjDr.js";const M={class:"pr-8"},H=h({__name:"account-adjust",props:{show:{type:Boolean,required:!0},value:{type:[Number,String],required:!0}},emits:["update:show","confirm"],setup(d,{emit:k}){const s=y(),i=d,f=k,o=U({action:1,num:"",remark:""}),m=y(),c=G(()=>Number(i.value)+Number(o.num)*(o.action==1?1:-1)),R={num:[{required:!0,message:"请输入调整的金额"}]},g=e=>{if(e.includes("-"))return V.msgError("请输入正整数");o.num=e},x=async()=>{var e;await((e=s.value)==null?void 0:e.validate()),f("confirm",o)},E=()=>{var e;f("update:show",!1),(e=s.value)==null||e.resetFields()};return v(()=>i.show,e=>{var a,r;e?(a=m.value)==null||a.open():(r=m.value)==null||r.close()}),v(c,e=>{e<0&&(V.msgError("调整后余额需大于0"),o.num="")}),(e,a)=>{const r=I,_=S,C=j,b=T,N=F;return q(),B(L,{ref_key:"popupRef",ref:m,title:"余额调整",width:"500px",onConfirm:x,async:!0,onClose:E},{default:l(()=>[D("div",M,[t(N,{ref_key:"formRef",ref:s,model:n(o),"label-width":"120px",rules:R},{default:l(()=>[t(r,{label:"当前余额"},{default:l(()=>[u("¥ "+w(d.value),1)]),_:1}),t(r,{label:"余额增减",required:"",prop:"action"},{default:l(()=>[t(C,{modelValue:n(o).action,"onUpdate:modelValue":a[0]||(a[0]=p=>n(o).action=p)},{default:l(()=>[t(_,{value:1},{default:l(()=>[...a[2]||(a[2]=[u("增加余额",-1)])]),_:1}),t(_,{value:2},{default:l(()=>[...a[3]||(a[3]=[u("扣减余额",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),t(r,{label:"调整余额",prop:"num"},{default:l(()=>[t(b,{"model-value":n(o).num,placeholder:"请输入调整的金额",type:"number",onInput:g},null,8,["model-value"])]),_:1}),t(r,{label:"调整后余额"},{default:l(()=>[u(" ¥ "+w(n(c)),1)]),_:1}),t(r,{label:"备注",prop:"remark"},{default:l(()=>[t(b,{modelValue:n(o).remark,"onUpdate:modelValue":a[1]||(a[1]=p=>n(o).remark=p),type:"textarea",rows:4},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])])]),_:1},512)}}});export{H as _}; import{o as h,R as v,q,O as B,D as l,s as D,v as t,b9 as F,u as n,b6 as I,L as u,T as w,bc as j,bf as S,b7 as T,a8 as y,ba as U,p as G}from"./.pnpm-Dwh6tNxD.js";import{_ as L}from"./index-CfJcNX3X.js";import{i as V}from"./index-C0Q8AuuZ.js";const M={class:"pr-8"},H=h({__name:"account-adjust",props:{show:{type:Boolean,required:!0},value:{type:[Number,String],required:!0}},emits:["update:show","confirm"],setup(d,{emit:k}){const s=y(),i=d,f=k,o=U({action:1,num:"",remark:""}),m=y(),c=G(()=>Number(i.value)+Number(o.num)*(o.action==1?1:-1)),R={num:[{required:!0,message:"请输入调整的金额"}]},g=e=>{if(e.includes("-"))return V.msgError("请输入正整数");o.num=e},x=async()=>{var e;await((e=s.value)==null?void 0:e.validate()),f("confirm",o)},E=()=>{var e;f("update:show",!1),(e=s.value)==null||e.resetFields()};return v(()=>i.show,e=>{var a,r;e?(a=m.value)==null||a.open():(r=m.value)==null||r.close()}),v(c,e=>{e<0&&(V.msgError("调整后余额需大于0"),o.num="")}),(e,a)=>{const r=I,_=S,C=j,b=T,N=F;return q(),B(L,{ref_key:"popupRef",ref:m,title:"余额调整",width:"500px",onConfirm:x,async:!0,onClose:E},{default:l(()=>[D("div",M,[t(N,{ref_key:"formRef",ref:s,model:n(o),"label-width":"120px",rules:R},{default:l(()=>[t(r,{label:"当前余额"},{default:l(()=>[u("¥ "+w(d.value),1)]),_:1}),t(r,{label:"余额增减",required:"",prop:"action"},{default:l(()=>[t(C,{modelValue:n(o).action,"onUpdate:modelValue":a[0]||(a[0]=p=>n(o).action=p)},{default:l(()=>[t(_,{value:1},{default:l(()=>[...a[2]||(a[2]=[u("增加余额",-1)])]),_:1}),t(_,{value:2},{default:l(()=>[...a[3]||(a[3]=[u("扣减余额",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),t(r,{label:"调整余额",prop:"num"},{default:l(()=>[t(b,{"model-value":n(o).num,placeholder:"请输入调整的金额",type:"number",onInput:g},null,8,["model-value"])]),_:1}),t(r,{label:"调整后余额"},{default:l(()=>[u(" ¥ "+w(n(c)),1)]),_:1}),t(r,{label:"备注",prop:"remark"},{default:l(()=>[t(b,{modelValue:n(o).remark,"onUpdate:modelValue":a[1]||(a[1]=p=>n(o).remark=p),type:"textarea",rows:4},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])])]),_:1},512)}}});export{H as _};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
import{_ as o}from"./add-nav.vue_vue_type_script_setup_true_lang-CH8U6Lyn.js";import"./.pnpm-Dwh6tNxD.js";import"./index-39SS-XKl.js";import"./index-C0Q8AuuZ.js";import"./picker-D3OnL1mV.js";import"./index-CfJcNX3X.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-B7li-XJN.js";import"./usePaging-DOuAwzL9.js";import"./picker-2bpe8Dva.js";import"./index-B-j4AceK.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -1 +0,0 @@
import{_ as o}from"./add-nav.vue_vue_type_script_setup_true_lang-D73JnCTp.js";import"./.pnpm-Dwh6tNxD.js";import"./index-4fo3NoYp.js";import"./index-C9ilrjDr.js";import"./picker-K9bQMD5v.js";import"./index-BM6NmNnG.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-C6Uwi7c8.js";import"./usePaging-DOuAwzL9.js";import"./picker-DYD_beSt.js";import"./index-DB4KfTa0.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -1 +1 @@
import{o as E,q as p,r as C,s as l,v as a,u as c,bQ as B,C as N,D as d,O as $,b7 as z,b6 as D,I,K as A,L,p as R}from"./.pnpm-Dwh6tNxD.js";import{_ as q}from"./index-4fo3NoYp.js";import{_ as F}from"./picker-K9bQMD5v.js";import{_ as K}from"./picker-DYD_beSt.js";import{c as O,i as r}from"./index-C9ilrjDr.js";const P={class:"bg-fill-light flex items-center w-full p-4 mb-4"},Q={class:"upload-btn w-[60px] h-[60px]"},S={class:"ml-3 flex-1"},T={class:"flex items-center"},j={class:"flex items-center mt-[18px]"},G={class:"flex-1 flex items-center"},H={class:"drag-move cursor-move ml-auto"},Z=E({__name:"add-nav",props:{modelValue:{type:Array,default:()=>[]},max:{type:Number,default:100},min:{type:Number,default:1}},emits:["update:modelValue"],setup(_,{emit:f}){const t=_,V=f,m=R({get(){return t.modelValue},set(s){V("update:modelValue",s)}}),x=()=>{var s;((s=t.modelValue)==null?void 0:s.length)<t.max?m.value.push({image:"",name:"导航名称",link:{},is_show:"1"}):r.msgError(`最多添加${t.max}`)},v=s=>{var e;if(((e=t.modelValue)==null?void 0:e.length)<=t.min)return r.msgError(`最少保留${t.min}`);m.value.splice(s,1)};return(s,e)=>{const u=O,g=K,b=z,h=F,k=I,w=D,y=q,U=A;return p(),C("div",null,[l("div",null,[a(c(B),{class:"draggable",modelValue:c(m),"onUpdate:modelValue":e[0]||(e[0]=o=>N(m)?m.value=o:null),animation:"300",handle:".drag-move","item-key":"index"},{item:d(({element:o,index:i})=>[(p(),$(y,{class:"w-[467px]",key:i,onClose:n=>v(i)},{default:d(()=>[l("div",P,[a(g,{modelValue:o.image,"onUpdate:modelValue":n=>o.image=n,"upload-class":"bg-body",size:"60px","exclude-domain":""},{upload:d(()=>[l("div",Q,[a(u,{name:"el-icon-Plus",size:20})])]),_:1},8,["modelValue","onUpdate:modelValue"]),l("div",S,[l("div",T,[e[1]||(e[1]=l("span",{class:"text-tx-regular flex-none mr-3"},"名称",-1)),a(b,{modelValue:o.name,"onUpdate:modelValue":n=>o.name=n,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),l("div",j,[e[2]||(e[2]=l("span",{class:"text-tx-regular flex-none mr-3"},"链接",-1)),a(h,{modelValue:o.link,"onUpdate:modelValue":n=>o.link=n},null,8,["modelValue","onUpdate:modelValue"])]),a(w,{label:"是否显示",class:"mt-[18px]"},{default:d(()=>[l("div",G,[a(k,{modelValue:o.is_show,"onUpdate:modelValue":n=>o.is_show=n,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),l("div",H,[a(u,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"])]),l("div",null,[a(U,{type:"primary",onClick:x},{default:d(()=>[...e[3]||(e[3]=[L("添加",-1)])]),_:1})])])}}});export{Z as _}; import{o as E,q as p,r as C,s as l,v as a,u as c,bQ as B,C as N,D as d,O as $,b7 as z,b6 as D,I,K as A,L,p as R}from"./.pnpm-Dwh6tNxD.js";import{_ as q}from"./index-39SS-XKl.js";import{_ as F}from"./picker-D3OnL1mV.js";import{_ as K}from"./picker-2bpe8Dva.js";import{c as O,i as r}from"./index-C0Q8AuuZ.js";const P={class:"bg-fill-light flex items-center w-full p-4 mb-4"},Q={class:"upload-btn w-[60px] h-[60px]"},S={class:"ml-3 flex-1"},T={class:"flex items-center"},j={class:"flex items-center mt-[18px]"},G={class:"flex-1 flex items-center"},H={class:"drag-move cursor-move ml-auto"},Z=E({__name:"add-nav",props:{modelValue:{type:Array,default:()=>[]},max:{type:Number,default:100},min:{type:Number,default:1}},emits:["update:modelValue"],setup(_,{emit:f}){const t=_,V=f,m=R({get(){return t.modelValue},set(s){V("update:modelValue",s)}}),x=()=>{var s;((s=t.modelValue)==null?void 0:s.length)<t.max?m.value.push({image:"",name:"导航名称",link:{},is_show:"1"}):r.msgError(`最多添加${t.max}`)},v=s=>{var e;if(((e=t.modelValue)==null?void 0:e.length)<=t.min)return r.msgError(`最少保留${t.min}`);m.value.splice(s,1)};return(s,e)=>{const u=O,g=K,b=z,h=F,k=I,w=D,y=q,U=A;return p(),C("div",null,[l("div",null,[a(c(B),{class:"draggable",modelValue:c(m),"onUpdate:modelValue":e[0]||(e[0]=o=>N(m)?m.value=o:null),animation:"300",handle:".drag-move","item-key":"index"},{item:d(({element:o,index:i})=>[(p(),$(y,{class:"w-[467px]",key:i,onClose:n=>v(i)},{default:d(()=>[l("div",P,[a(g,{modelValue:o.image,"onUpdate:modelValue":n=>o.image=n,"upload-class":"bg-body",size:"60px","exclude-domain":""},{upload:d(()=>[l("div",Q,[a(u,{name:"el-icon-Plus",size:20})])]),_:1},8,["modelValue","onUpdate:modelValue"]),l("div",S,[l("div",T,[e[1]||(e[1]=l("span",{class:"text-tx-regular flex-none mr-3"},"名称",-1)),a(b,{modelValue:o.name,"onUpdate:modelValue":n=>o.name=n,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),l("div",j,[e[2]||(e[2]=l("span",{class:"text-tx-regular flex-none mr-3"},"链接",-1)),a(h,{modelValue:o.link,"onUpdate:modelValue":n=>o.link=n},null,8,["modelValue","onUpdate:modelValue"])]),a(w,{label:"是否显示",class:"mt-[18px]"},{default:d(()=>[l("div",G,[a(k,{modelValue:o.is_show,"onUpdate:modelValue":n=>o.is_show=n,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),l("div",H,[a(u,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"])]),l("div",null,[a(U,{type:"primary",onClick:x},{default:d(()=>[...e[3]||(e[3]=[L("添加",-1)])]),_:1})])])}}});export{Z as _};
@@ -1 +1 @@
import{r as n}from"./index-C9ilrjDr.js";function e(t){return n.get({url:"/auth.admin/lists",params:t},{ignoreCancelToken:!0})}function i(t){return n.post({url:"/auth.admin/add",params:t})}function r(t){return n.post({url:"/auth.admin/edit",params:t})}function u(t){return n.post({url:"/auth.admin/delete",params:t})}function d(t){return n.get({url:"/auth.admin/detail",params:t})}export{e as a,r as b,u as c,i as d,d as e}; import{r as n}from"./index-C0Q8AuuZ.js";function e(t){return n.get({url:"/auth.admin/lists",params:t},{ignoreCancelToken:!0})}function i(t){return n.post({url:"/auth.admin/add",params:t})}function r(t){return n.post({url:"/auth.admin/edit",params:t})}function u(t){return n.post({url:"/auth.admin/delete",params:t})}function d(t){return n.get({url:"/auth.admin/detail",params:t})}export{e as a,r as b,u as c,i as d,d as e};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{r as e}from"./index-C9ilrjDr.js";function a(t){return e.get({url:"/article.articleCate/lists",params:t})}function l(t){return e.get({url:"/article.articleCate/all",params:t})}function i(t){return e.post({url:"/article.articleCate/add",params:t})}function c(t){return e.post({url:"/article.articleCate/edit",params:t})}function u(t){return e.post({url:"/article.articleCate/delete",params:t})}function n(t){return e.get({url:"/article.articleCate/detail",params:t})}function s(t){return e.post({url:"/article.articleCate/updateStatus",params:t})}function o(t){return e.get({url:"/article.article/lists",params:t})}function d(t){return e.post({url:"/article.article/add",params:t})}function f(t){return e.post({url:"/article.article/edit",params:t})}function C(t){return e.post({url:"/article.article/delete",params:t})}function p(t){return e.get({url:"/article.article/detail",params:t})}function g(t){return e.post({url:"/article.article/updateStatus",params:t})}export{a,u as b,s as c,c as d,i as e,n as f,p as g,f as h,d as i,l as j,o as k,g as l,C as m}; import{r as e}from"./index-C0Q8AuuZ.js";function a(t){return e.get({url:"/article.articleCate/lists",params:t})}function l(t){return e.get({url:"/article.articleCate/all",params:t})}function i(t){return e.post({url:"/article.articleCate/add",params:t})}function c(t){return e.post({url:"/article.articleCate/edit",params:t})}function u(t){return e.post({url:"/article.articleCate/delete",params:t})}function n(t){return e.get({url:"/article.articleCate/detail",params:t})}function s(t){return e.post({url:"/article.articleCate/updateStatus",params:t})}function o(t){return e.get({url:"/article.article/lists",params:t})}function d(t){return e.post({url:"/article.article/add",params:t})}function f(t){return e.post({url:"/article.article/edit",params:t})}function C(t){return e.post({url:"/article.article/delete",params:t})}function p(t){return e.get({url:"/article.article/detail",params:t})}function g(t){return e.post({url:"/article.article/updateStatus",params:t})}export{a,u as b,s as c,c as d,i as e,n as f,p as g,f as h,d as i,l as j,o as k,g as l,C as m};
@@ -1 +1 @@
import{r as e}from"./index-C9ilrjDr.js";function r(s){return e.get({url:"/asset.AssetUser/lists",params:s})}function u(s){return e.post({url:"/asset.AssetUser/add",params:s})}function a(s){return e.post({url:"/asset.AssetUser/edit",params:s})}function i(s){return e.post({url:"/asset.AssetUser/delete",params:s})}function o(s){return e.get({url:"/asset.AssetResource/lists",params:s})}function n(s){return e.post({url:"/asset.AssetResource/add",params:s})}function A(s){return e.post({url:"/asset.AssetResource/edit",params:s})}function c(s){return e.post({url:"/asset.AssetResource/delete",params:s})}export{o as a,c as b,A as c,n as d,r as e,a as f,i as g,u as h}; import{r as e}from"./index-C0Q8AuuZ.js";function r(s){return e.get({url:"/asset.AssetUser/lists",params:s})}function u(s){return e.post({url:"/asset.AssetUser/add",params:s})}function a(s){return e.post({url:"/asset.AssetUser/edit",params:s})}function i(s){return e.post({url:"/asset.AssetUser/delete",params:s})}function o(s){return e.get({url:"/asset.AssetResource/lists",params:s})}function n(s){return e.post({url:"/asset.AssetResource/add",params:s})}function A(s){return e.post({url:"/asset.AssetResource/edit",params:s})}function c(s){return e.post({url:"/asset.AssetResource/delete",params:s})}export{o as a,c as b,A as c,n as d,r as e,a as f,i as g,u as h};
@@ -1 +0,0 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-Cbk2Xa5e.js";import"./.pnpm-Dwh6tNxD.js";import"./index-4fo3NoYp.js";import"./index-C9ilrjDr.js";import"./picker-K9bQMD5v.js";import"./index-BM6NmNnG.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-C6Uwi7c8.js";import"./usePaging-DOuAwzL9.js";import"./picker-DYD_beSt.js";import"./index-DB4KfTa0.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -1 +0,0 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-Ctu7ajT8.js";import"./.pnpm-Dwh6tNxD.js";import"./add-nav.vue_vue_type_script_setup_true_lang-D73JnCTp.js";import"./index-4fo3NoYp.js";import"./index-C9ilrjDr.js";import"./picker-K9bQMD5v.js";import"./index-BM6NmNnG.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-C6Uwi7c8.js";import"./usePaging-DOuAwzL9.js";import"./picker-DYD_beSt.js";import"./index-DB4KfTa0.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-DwnFVvmv.js";import"./.pnpm-Dwh6tNxD.js";import"./index-39SS-XKl.js";import"./index-C0Q8AuuZ.js";import"./picker-D3OnL1mV.js";import"./index-CfJcNX3X.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-B7li-XJN.js";import"./usePaging-DOuAwzL9.js";import"./picker-2bpe8Dva.js";import"./index-B-j4AceK.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-2Q5vVd2y.js";import"./.pnpm-Dwh6tNxD.js";import"./index-39SS-XKl.js";import"./index-C0Q8AuuZ.js";import"./picker-D3OnL1mV.js";import"./index-CfJcNX3X.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-B7li-XJN.js";import"./usePaging-DOuAwzL9.js";import"./picker-2bpe8Dva.js";import"./index-B-j4AceK.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-BLTWBJHy.js";import"./.pnpm-Dwh6tNxD.js";import"./index-4fo3NoYp.js";import"./index-C9ilrjDr.js";import"./picker-K9bQMD5v.js";import"./index-BM6NmNnG.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-C6Uwi7c8.js";import"./usePaging-DOuAwzL9.js";import"./picker-DYD_beSt.js";import"./index-DB4KfTa0.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-BX-tcO7M.js";import"./.pnpm-Dwh6tNxD.js";import"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";import"./picker-2bpe8Dva.js";import"./index-CfJcNX3X.js";import"./index-C0Q8AuuZ.js";import"./index-B-j4AceK.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-39SS-XKl.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./usePaging-DOuAwzL9.js";export{o as default};
@@ -1 +1 @@
import{o as b,q as c,r as V,s as v,v as t,D as a,Y as x,X as E,u as l,b9 as g,F as k,p as w}from"./.pnpm-Dwh6tNxD.js";import{_ as p}from"./menu-set.vue_vue_type_script_setup_true_lang-BwM2_EVX.js";import"./index-4fo3NoYp.js";import"./index-C9ilrjDr.js";import"./picker-K9bQMD5v.js";import"./index-BM6NmNnG.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-C6Uwi7c8.js";import"./usePaging-DOuAwzL9.js";import"./picker-DYD_beSt.js";import"./index-DB4KfTa0.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";const $=b({__name:"attr",props:{modelValue:{type:Object,default:()=>({nav:[],menu:{}})}},emits:["update:modelValue"],setup(s,{emit:u}){const d=s,i=u,o=w({get(){return d.modelValue},set(n){i("update:modelValue",n)}});return(n,e)=>{const r=E,f=x,_=g;return c(),V(k,null,[e[2]||(e[2]=v("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2"}," pc导航设置 ",-1)),t(_,{class:"mt-4","label-width":"70px"},{default:a(()=>[t(f,{"model-value":"nav"},{default:a(()=>[t(r,{label:"主导航设置",name:"nav"},{default:a(()=>[t(p,{modelValue:l(o).nav,"onUpdate:modelValue":e[0]||(e[0]=m=>l(o).nav=m)},null,8,["modelValue"])]),_:1}),t(r,{label:"菜单设置",name:"menu"},{default:a(()=>[t(p,{modelValue:l(o).menu,"onUpdate:modelValue":e[1]||(e[1]=m=>l(o).menu=m)},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})],64)}}});export{$ as default}; import{o as b,q as c,r as V,s as v,v as t,D as a,Y as x,X as E,u as l,b9 as g,F as k,p as w}from"./.pnpm-Dwh6tNxD.js";import{_ as p}from"./menu-set.vue_vue_type_script_setup_true_lang-BLwvkWQS.js";import"./index-39SS-XKl.js";import"./index-C0Q8AuuZ.js";import"./picker-D3OnL1mV.js";import"./index-CfJcNX3X.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-B7li-XJN.js";import"./usePaging-DOuAwzL9.js";import"./picker-2bpe8Dva.js";import"./index-B-j4AceK.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";const $=b({__name:"attr",props:{modelValue:{type:Object,default:()=>({nav:[],menu:{}})}},emits:["update:modelValue"],setup(s,{emit:u}){const d=s,i=u,o=w({get(){return d.modelValue},set(n){i("update:modelValue",n)}});return(n,e)=>{const r=E,f=x,_=g;return c(),V(k,null,[e[2]||(e[2]=v("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2"}," pc导航设置 ",-1)),t(_,{class:"mt-4","label-width":"70px"},{default:a(()=>[t(f,{"model-value":"nav"},{default:a(()=>[t(r,{label:"主导航设置",name:"nav"},{default:a(()=>[t(p,{modelValue:l(o).nav,"onUpdate:modelValue":e[0]||(e[0]=m=>l(o).nav=m)},null,8,["modelValue"])]),_:1}),t(r,{label:"菜单设置",name:"menu"},{default:a(()=>[t(p,{modelValue:l(o).menu,"onUpdate:modelValue":e[1]||(e[1]=m=>l(o).menu=m)},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})],64)}}});export{$ as default};
@@ -1 +0,0 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-C0LPpfEu.js";import"./.pnpm-Dwh6tNxD.js";import"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";import"./picker-DYD_beSt.js";import"./index-BM6NmNnG.js";import"./index-C9ilrjDr.js";import"./index-DB4KfTa0.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-4fo3NoYp.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./usePaging-DOuAwzL9.js";export{o as default};
@@ -1 +0,0 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-B28CVUAq.js";import"./.pnpm-Dwh6tNxD.js";import"./index-4fo3NoYp.js";import"./index-C9ilrjDr.js";import"./picker-K9bQMD5v.js";import"./index-BM6NmNnG.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-C6Uwi7c8.js";import"./usePaging-DOuAwzL9.js";import"./picker-DYD_beSt.js";import"./index-DB4KfTa0.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-DyoPs8TD.js";import"./.pnpm-Dwh6tNxD.js";import"./index-39SS-XKl.js";import"./index-C0Q8AuuZ.js";import"./picker-D3OnL1mV.js";import"./index-CfJcNX3X.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-B7li-XJN.js";import"./usePaging-DOuAwzL9.js";import"./picker-2bpe8Dva.js";import"./index-B-j4AceK.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-B6uHrbrS.js";import"./.pnpm-Dwh6tNxD.js";import"./add-nav.vue_vue_type_script_setup_true_lang-CH8U6Lyn.js";import"./index-39SS-XKl.js";import"./index-C0Q8AuuZ.js";import"./picker-D3OnL1mV.js";import"./index-CfJcNX3X.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-B7li-XJN.js";import"./usePaging-DOuAwzL9.js";import"./picker-2bpe8Dva.js";import"./index-B-j4AceK.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-BEJoSc1o.js";import"./.pnpm-Dwh6tNxD.js";import"./picker-2bpe8Dva.js";import"./index-CfJcNX3X.js";import"./index-C0Q8AuuZ.js";import"./index-B-j4AceK.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-39SS-XKl.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./usePaging-DOuAwzL9.js";export{o as default};
@@ -1 +0,0 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-C-c-r9RC.js";import"./.pnpm-Dwh6tNxD.js";import"./picker-DYD_beSt.js";import"./index-BM6NmNnG.js";import"./index-C9ilrjDr.js";import"./index-DB4KfTa0.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-4fo3NoYp.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./usePaging-DOuAwzL9.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-Vz3txZfV.js";import"./.pnpm-Dwh6tNxD.js";import"./add-nav.vue_vue_type_script_setup_true_lang-CH8U6Lyn.js";import"./index-39SS-XKl.js";import"./index-C0Q8AuuZ.js";import"./picker-D3OnL1mV.js";import"./index-CfJcNX3X.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-B7li-XJN.js";import"./usePaging-DOuAwzL9.js";import"./picker-2bpe8Dva.js";import"./index-B-j4AceK.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -1 +0,0 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-CxVBLyr-.js";import"./.pnpm-Dwh6tNxD.js";import"./add-nav.vue_vue_type_script_setup_true_lang-D73JnCTp.js";import"./index-4fo3NoYp.js";import"./index-C9ilrjDr.js";import"./picker-K9bQMD5v.js";import"./index-BM6NmNnG.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-C6Uwi7c8.js";import"./usePaging-DOuAwzL9.js";import"./picker-DYD_beSt.js";import"./index-DB4KfTa0.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./attr-setting.vue_vue_type_script_setup_true_lang-x-XafvgN.js";import"./.pnpm-Dwh6tNxD.js";import"./index-LzaTAqVs.js";import"./attr-CcBlHQ6U.js";import"./index-39SS-XKl.js";import"./index-C0Q8AuuZ.js";import"./picker-D3OnL1mV.js";import"./index-CfJcNX3X.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-B7li-XJN.js";import"./usePaging-DOuAwzL9.js";import"./picker-2bpe8Dva.js";import"./index-B-j4AceK.js";import"./index-4SRyrKER.js";import"./file-BUuEowhq.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./content.vue_vue_type_script_setup_true_lang-67HcMoa-.js";import"./decoration-img-g1NB8PSI.js";import"./attr.vue_vue_type_script_setup_true_lang-BEJoSc1o.js";import"./content-CqaVEK4f.js";import"./attr.vue_vue_type_script_setup_true_lang-2Q5vVd2y.js";import"./content.vue_vue_type_script_setup_true_lang-BQxNT8cz.js";import"./attr.vue_vue_type_script_setup_true_lang-Vz3txZfV.js";import"./add-nav.vue_vue_type_script_setup_true_lang-CH8U6Lyn.js";import"./content-qYVlkYgK.js";import"./attr.vue_vue_type_script_setup_true_lang-B6uHrbrS.js";import"./content.vue_vue_type_script_setup_true_lang-Dpn7S-bh.js";import"./attr.vue_vue_type_script_setup_true_lang--WwiPUCL.js";import"./content-DytPruqB.js";import"./decoration-BcG7z9u1.js";import"./attr.vue_vue_type_script_setup_true_lang-BX-tcO7M.js";import"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";import"./content-BI4DYec0.js";import"./content.vue_vue_type_script_setup_true_lang-v-Of3bVR.js";import"./attr.vue_vue_type_script_setup_true_lang-B4PfotVc.js";import"./content-wxLxJxNK.js";import"./attr.vue_vue_type_script_setup_true_lang-DwnFVvmv.js";import"./content.vue_vue_type_script_setup_true_lang-BDl5oRtm.js";import"./attr.vue_vue_type_script_setup_true_lang-BJHLs4e8.js";import"./content-DzwjOGpc.js";export{o as default};
@@ -1 +0,0 @@
import{_ as o}from"./attr-setting.vue_vue_type_script_setup_true_lang-C6DcKNDl.js";import"./.pnpm-Dwh6tNxD.js";import"./index-Z-zCbKBZ.js";import"./attr-CR6HHmCW.js";import"./index-4fo3NoYp.js";import"./index-C9ilrjDr.js";import"./picker-K9bQMD5v.js";import"./index-BM6NmNnG.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-C6Uwi7c8.js";import"./usePaging-DOuAwzL9.js";import"./picker-DYD_beSt.js";import"./index-DB4KfTa0.js";import"./index-DBhmZUba.js";import"./file-CNQVkxcM.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./content.vue_vue_type_script_setup_true_lang-Dr8Z9LMW.js";import"./decoration-img-B97_laTE.js";import"./attr.vue_vue_type_script_setup_true_lang-C-c-r9RC.js";import"./content-CqZOVZ6z.js";import"./attr.vue_vue_type_script_setup_true_lang-BLTWBJHy.js";import"./content.vue_vue_type_script_setup_true_lang-B-U8T_iq.js";import"./attr.vue_vue_type_script_setup_true_lang-Ctu7ajT8.js";import"./add-nav.vue_vue_type_script_setup_true_lang-D73JnCTp.js";import"./content-CAhICNVX.js";import"./attr.vue_vue_type_script_setup_true_lang-CxVBLyr-.js";import"./content.vue_vue_type_script_setup_true_lang-QVsMdosa.js";import"./attr.vue_vue_type_script_setup_true_lang--WwiPUCL.js";import"./content-B0IFiMrg.js";import"./decoration-C0nTh_VL.js";import"./attr.vue_vue_type_script_setup_true_lang-C0LPpfEu.js";import"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";import"./content-DMHFaAWx.js";import"./content.vue_vue_type_script_setup_true_lang-BjvjVaXz.js";import"./attr.vue_vue_type_script_setup_true_lang-B4PfotVc.js";import"./content-3pdJCc10.js";import"./attr.vue_vue_type_script_setup_true_lang-Cbk2Xa5e.js";import"./content.vue_vue_type_script_setup_true_lang-ClT1-8Uj.js";import"./attr.vue_vue_type_script_setup_true_lang-BJHLs4e8.js";import"./content-B7paQOYI.js";export{o as default};
@@ -1 +1 @@
import{o as g,q as a,r as b,v as c,bg as y,D as r,s as x,T as _,a2 as h,O as i,a3 as w,a4 as v,u as C}from"./.pnpm-Dwh6tNxD.js";import{e as k}from"./index-Z-zCbKBZ.js";const B={class:"pages-setting"},D={class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},O=g({__name:"attr-setting",props:{widget:{type:Object,default:()=>({})},type:{type:String,default:"mobile"}},emits:["update:content"],setup(e,{emit:d}){const m=d,p=n=>{m("update:content",n)};return(n,E)=>{const f=y,u=h;return a(),b("div",B,[c(f,{shadow:"never",class:"!border-none flex"},{default:r(()=>{var t;return[x("div",D,_((t=e.widget)==null?void 0:t.title),1)]}),_:1}),c(u,{class:"w-full",style:{height:"calc(100% - 60px)"}},{default:r(()=>{var t,s,o,l;return[(a(),i(w,null,[(a(),i(v((s=C(k)[(t=e.widget)==null?void 0:t.name])==null?void 0:s.attr),{content:(o=e.widget)==null?void 0:o.content,styles:(l=e.widget)==null?void 0:l.styles,type:e.type,"onUpdate:content":p},null,40,["content","styles","type"]))],1024))]}),_:1})])}}});export{O as _}; import{o as g,q as a,r as b,v as c,bg as y,D as r,s as x,T as _,a2 as h,O as i,a3 as w,a4 as v,u as C}from"./.pnpm-Dwh6tNxD.js";import{e as k}from"./index-LzaTAqVs.js";const B={class:"pages-setting"},D={class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},O=g({__name:"attr-setting",props:{widget:{type:Object,default:()=>({})},type:{type:String,default:"mobile"}},emits:["update:content"],setup(e,{emit:d}){const m=d,p=n=>{m("update:content",n)};return(n,E)=>{const f=y,u=h;return a(),b("div",B,[c(f,{shadow:"never",class:"!border-none flex"},{default:r(()=>{var t;return[x("div",D,_((t=e.widget)==null?void 0:t.title),1)]}),_:1}),c(u,{class:"w-full",style:{height:"calc(100% - 60px)"}},{default:r(()=>{var t,s,o,l;return[(a(),i(w,null,[(a(),i(v((s=C(k)[(t=e.widget)==null?void 0:t.name])==null?void 0:s.attr),{content:(o=e.widget)==null?void 0:o.content,styles:(l=e.widget)==null?void 0:l.styles,type:e.type,"onUpdate:content":p},null,40,["content","styles","type"]))],1024))]}),_:1})])}}});export{O as _};
@@ -1 +1 @@
import{o as j,q as d,r as b,v as o,D as s,bg as F,s as n,u as p,bQ as S,O as _,b6 as q,P as r,b7 as z,I as A,K,L,b9 as P,p as Q,cm as v}from"./.pnpm-Dwh6tNxD.js";import{_ as R}from"./index-4fo3NoYp.js";import{c as T,i as k}from"./index-C9ilrjDr.js";import{_ as G}from"./picker-K9bQMD5v.js";import{_ as H}from"./picker-DYD_beSt.js";const J={class:"flex-1"},M={class:"bg-fill-light w-full p-4 mt-4"},W={class:"flex-1"},X={class:"flex-1 flex items-center"},Y={class:"drag-move cursor-move ml-auto"},Z={key:0,class:"mt-4"},f=5,ae=j({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})},type:{type:String,default:"mobile"}},emits:["update:content"],setup(m,{emit:y}){const u=y,c=m,g=Q({get:()=>c.content,set:a=>{u("update:content",a)}}),w=()=>{var a;if(((a=c.content.data)==null?void 0:a.length)<f){const e=v(c.content);e.data.push({is_show:"1",image:"",name:"",link:{}}),u("update:content",e)}else k.msgError(`最多添加${f}张图片`)},E=a=>{var i;if(((i=c.content.data)==null?void 0:i.length)<=1)return k.msgError("最少保留一张图片");const e=v(c.content);e.data.splice(a,1),u("update:content",e)};return(a,e)=>{const i=H,U=G,C=z,h=q,B=A,D=T,N=R,$=K,I=F,O=P;return d(),b("div",null,[o(O,{"label-width":"70px"},{default:s(()=>[o(I,{shadow:"never",class:"!border-none flex mt-2"},{default:s(()=>{var x;return[e[2]||(e[2]=n("div",{class:"flex items-end"},[n("div",{class:"text-base text-[#101010] font-medium"},"图片设置"),n("div",{class:"text-xs text-tx-secondary ml-2"}," 最多添加5张,建议图片尺寸:750px*200px ")],-1)),n("div",J,[o(p(S),{class:"draggable",modelValue:p(g).data,"onUpdate:modelValue":e[0]||(e[0]=t=>p(g).data=t),animation:"300",handle:".drag-move"},{item:s(({element:t,index:V})=>[(d(),_(N,{key:V,onClose:l=>E(V),class:"w-full"},{default:s(()=>[n("div",M,[o(i,{width:"396px",height:"196px",modelValue:t.image,"onUpdate:modelValue":l=>t.image=l,"upload-class":"bg-body","exclude-domain":""},null,8,["modelValue","onUpdate:modelValue"]),n("div",W,[o(h,{class:"mt-[18px]",label:"图片链接"},{default:s(()=>[m.type=="mobile"?(d(),_(U,{key:0,modelValue:t.link,"onUpdate:modelValue":l=>t.link=l},null,8,["modelValue","onUpdate:modelValue"])):r("",!0),m.type=="pc"?(d(),_(C,{key:1,placeholder:"请输入链接",modelValue:t.link.path,"onUpdate:modelValue":l=>t.link.path=l},null,8,["modelValue","onUpdate:modelValue"])):r("",!0)]),_:2},1024),o(h,{label:"是否显示",class:"mt-[18px] !mb-0"},{default:s(()=>[n("div",X,[o(B,{modelValue:t.is_show,"onUpdate:modelValue":l=>t.is_show=l,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),n("div",Y,[o(D,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"])]),((x=m.content.data)==null?void 0:x.length)<f?(d(),b("div",Z,[o($,{class:"w-full",type:"primary",onClick:w},{default:s(()=>[...e[1]||(e[1]=[L("添加图片",-1)])]),_:1})])):r("",!0)]}),_:1})]),_:1})])}}});export{ae as _}; import{o as j,q as d,r as b,v as o,D as s,bg as F,s as n,u as p,bQ as S,O as _,b6 as q,P as r,b7 as z,I as A,K,L,b9 as P,p as Q,cm as v}from"./.pnpm-Dwh6tNxD.js";import{_ as R}from"./index-39SS-XKl.js";import{c as T,i as k}from"./index-C0Q8AuuZ.js";import{_ as G}from"./picker-D3OnL1mV.js";import{_ as H}from"./picker-2bpe8Dva.js";const J={class:"flex-1"},M={class:"bg-fill-light w-full p-4 mt-4"},W={class:"flex-1"},X={class:"flex-1 flex items-center"},Y={class:"drag-move cursor-move ml-auto"},Z={key:0,class:"mt-4"},f=5,ae=j({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})},type:{type:String,default:"mobile"}},emits:["update:content"],setup(m,{emit:y}){const u=y,c=m,g=Q({get:()=>c.content,set:a=>{u("update:content",a)}}),w=()=>{var a;if(((a=c.content.data)==null?void 0:a.length)<f){const e=v(c.content);e.data.push({is_show:"1",image:"",name:"",link:{}}),u("update:content",e)}else k.msgError(`最多添加${f}张图片`)},E=a=>{var i;if(((i=c.content.data)==null?void 0:i.length)<=1)return k.msgError("最少保留一张图片");const e=v(c.content);e.data.splice(a,1),u("update:content",e)};return(a,e)=>{const i=H,U=G,C=z,h=q,B=A,D=T,N=R,$=K,I=F,O=P;return d(),b("div",null,[o(O,{"label-width":"70px"},{default:s(()=>[o(I,{shadow:"never",class:"!border-none flex mt-2"},{default:s(()=>{var x;return[e[2]||(e[2]=n("div",{class:"flex items-end"},[n("div",{class:"text-base text-[#101010] font-medium"},"图片设置"),n("div",{class:"text-xs text-tx-secondary ml-2"}," 最多添加5张,建议图片尺寸:750px*200px ")],-1)),n("div",J,[o(p(S),{class:"draggable",modelValue:p(g).data,"onUpdate:modelValue":e[0]||(e[0]=t=>p(g).data=t),animation:"300",handle:".drag-move"},{item:s(({element:t,index:V})=>[(d(),_(N,{key:V,onClose:l=>E(V),class:"w-full"},{default:s(()=>[n("div",M,[o(i,{width:"396px",height:"196px",modelValue:t.image,"onUpdate:modelValue":l=>t.image=l,"upload-class":"bg-body","exclude-domain":""},null,8,["modelValue","onUpdate:modelValue"]),n("div",W,[o(h,{class:"mt-[18px]",label:"图片链接"},{default:s(()=>[m.type=="mobile"?(d(),_(U,{key:0,modelValue:t.link,"onUpdate:modelValue":l=>t.link=l},null,8,["modelValue","onUpdate:modelValue"])):r("",!0),m.type=="pc"?(d(),_(C,{key:1,placeholder:"请输入链接",modelValue:t.link.path,"onUpdate:modelValue":l=>t.link.path=l},null,8,["modelValue","onUpdate:modelValue"])):r("",!0)]),_:2},1024),o(h,{label:"是否显示",class:"mt-[18px] !mb-0"},{default:s(()=>[n("div",X,[o(B,{modelValue:t.is_show,"onUpdate:modelValue":l=>t.is_show=l,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),n("div",Y,[o(D,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"])]),((x=m.content.data)==null?void 0:x.length)<f?(d(),b("div",Z,[o($,{class:"w-full",type:"primary",onClick:w},{default:s(()=>[...e[1]||(e[1]=[L("添加图片",-1)])]),_:1})])):r("",!0)]}),_:1})]),_:1})])}}});export{ae as _};
@@ -1 +1 @@
import{o as k,q as d,r as u,v as l,D as o,bg as F,s,bc as U,u as n,bf as B,L as x,b6 as C,bm as N,F as b,G as c,bn as O,b9 as j,p as D}from"./.pnpm-Dwh6tNxD.js";import{_ as G}from"./add-nav.vue_vue_type_script_setup_true_lang-D73JnCTp.js";const L={class:"flex-1 mt-4"},I=k({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(v,{emit:V}){const y=V,w=v,a=D({get:()=>w.content,set:m=>{y("update:content",m)}});return(m,e)=>{const r=B,E=U,p=O,_=N,i=C,f=F,g=j;return d(),u("div",null,[l(g,{"label-width":"70px"},{default:o(()=>[l(f,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[6]||(e[6]=s("div",{class:"flex items-end mb-4"},[s("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),l(E,{modelValue:n(a).style,"onUpdate:modelValue":e[0]||(e[0]=t=>n(a).style=t)},{default:o(()=>[l(r,{value:1},{default:o(()=>[...e[4]||(e[4]=[x("固定显示",-1)])]),_:1}),l(r,{value:2},{default:o(()=>[...e[5]||(e[5]=[x("分页滑动",-1)])]),_:1})]),_:1},8,["modelValue"]),l(i,{label:"每行数量",class:"mt-4"},{default:o(()=>[l(_,{modelValue:n(a).per_line,"onUpdate:modelValue":e[1]||(e[1]=t=>n(a).per_line=t),style:{width:"300px"}},{default:o(()=>[(d(),u(b,null,c(5,t=>l(p,{key:t,label:t+"个",value:t},null,8,["label","value"])),64))]),_:1},8,["modelValue"])]),_:1}),l(i,{label:"显示行数"},{default:o(()=>[l(_,{modelValue:n(a).show_line,"onUpdate:modelValue":e[2]||(e[2]=t=>n(a).show_line=t),style:{width:"300px"}},{default:o(()=>[(d(),u(b,null,c(2,t=>l(p,{key:t,label:t+"行",value:t},null,8,["label","value"])),64))]),_:1},8,["modelValue"])]),_:1})]),_:1}),l(f,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[7]||(e[7]=s("div",{class:"flex items-end"},[s("div",{class:"text-base text-[#101010] font-medium"},"菜单设置"),s("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),s("div",L,[l(G,{modelValue:n(a).data,"onUpdate:modelValue":e[3]||(e[3]=t=>n(a).data=t)},null,8,["modelValue"])])]),_:1})]),_:1})])}}});export{I as _}; import{o as k,q as d,r as u,v as l,D as o,bg as F,s,bc as U,u as n,bf as B,L as x,b6 as C,bm as N,F as b,G as c,bn as O,b9 as j,p as D}from"./.pnpm-Dwh6tNxD.js";import{_ as G}from"./add-nav.vue_vue_type_script_setup_true_lang-CH8U6Lyn.js";const L={class:"flex-1 mt-4"},I=k({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(v,{emit:V}){const y=V,w=v,a=D({get:()=>w.content,set:m=>{y("update:content",m)}});return(m,e)=>{const r=B,E=U,p=O,_=N,i=C,f=F,g=j;return d(),u("div",null,[l(g,{"label-width":"70px"},{default:o(()=>[l(f,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[6]||(e[6]=s("div",{class:"flex items-end mb-4"},[s("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),l(E,{modelValue:n(a).style,"onUpdate:modelValue":e[0]||(e[0]=t=>n(a).style=t)},{default:o(()=>[l(r,{value:1},{default:o(()=>[...e[4]||(e[4]=[x("固定显示",-1)])]),_:1}),l(r,{value:2},{default:o(()=>[...e[5]||(e[5]=[x("分页滑动",-1)])]),_:1})]),_:1},8,["modelValue"]),l(i,{label:"每行数量",class:"mt-4"},{default:o(()=>[l(_,{modelValue:n(a).per_line,"onUpdate:modelValue":e[1]||(e[1]=t=>n(a).per_line=t),style:{width:"300px"}},{default:o(()=>[(d(),u(b,null,c(5,t=>l(p,{key:t,label:t+"个",value:t},null,8,["label","value"])),64))]),_:1},8,["modelValue"])]),_:1}),l(i,{label:"显示行数"},{default:o(()=>[l(_,{modelValue:n(a).show_line,"onUpdate:modelValue":e[2]||(e[2]=t=>n(a).show_line=t),style:{width:"300px"}},{default:o(()=>[(d(),u(b,null,c(2,t=>l(p,{key:t,label:t+"行",value:t},null,8,["label","value"])),64))]),_:1},8,["modelValue"])]),_:1})]),_:1}),l(f,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[7]||(e[7]=s("div",{class:"flex items-end"},[s("div",{class:"text-base text-[#101010] font-medium"},"菜单设置"),s("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),s("div",L,[l(G,{modelValue:n(a).data,"onUpdate:modelValue":e[3]||(e[3]=t=>n(a).data=t)},null,8,["modelValue"])])]),_:1})]),_:1})])}}});export{I as _};
@@ -1 +1 @@
import{o as V,q as b,r as w,v as e,D as n,bg as x,b6 as g,b7 as k,u as o,s as E,b9 as U,p as v}from"./.pnpm-Dwh6tNxD.js";import{_ as q}from"./picker-DYD_beSt.js";const C=V({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(u,{emit:p}){const r=p,i=u,l=v({get:()=>i.content,set:d=>{r("update:content",d)}});return(d,t)=>{const s=k,m=g,_=q,c=x,f=U;return b(),w("div",null,[e(f,{"label-width":"90px",size:"large","label-position":"top"},{default:n(()=>[e(c,{shadow:"never",class:"!border-none flex mt-2"},{default:n(()=>[e(m,{label:"平台名称"},{default:n(()=>[e(s,{class:"w-[400px]","show-word-limit":"",maxlength:"20",modelValue:o(l).title,"onUpdate:modelValue":t[0]||(t[0]=a=>o(l).title=a)},null,8,["modelValue"])]),_:1}),e(m,{label:"客服二维码"},{default:n(()=>[E("div",null,[e(_,{modelValue:o(l).qrcode,"onUpdate:modelValue":t[1]||(t[1]=a=>o(l).qrcode=a),"exclude-domain":""},null,8,["modelValue"])])]),_:1}),e(m,{label:"备注"},{default:n(()=>[e(s,{class:"w-[400px]","show-word-limit":"",maxlength:"20",modelValue:o(l).remark,"onUpdate:modelValue":t[2]||(t[2]=a=>o(l).remark=a)},null,8,["modelValue"])]),_:1}),e(m,{label:"联系电话"},{default:n(()=>[e(s,{class:"w-[400px]",modelValue:o(l).mobile,"onUpdate:modelValue":t[3]||(t[3]=a=>o(l).mobile=a)},null,8,["modelValue"])]),_:1}),e(m,{label:"服务时间"},{default:n(()=>[e(s,{class:"w-[400px]","show-word-limit":"",maxlength:"20",modelValue:o(l).time,"onUpdate:modelValue":t[4]||(t[4]=a=>o(l).time=a)},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})])}}});export{C as _}; import{o as V,q as b,r as w,v as e,D as n,bg as x,b6 as g,b7 as k,u as o,s as E,b9 as U,p as v}from"./.pnpm-Dwh6tNxD.js";import{_ as q}from"./picker-2bpe8Dva.js";const C=V({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(u,{emit:p}){const r=p,i=u,l=v({get:()=>i.content,set:d=>{r("update:content",d)}});return(d,t)=>{const s=k,m=g,_=q,c=x,f=U;return b(),w("div",null,[e(f,{"label-width":"90px",size:"large","label-position":"top"},{default:n(()=>[e(c,{shadow:"never",class:"!border-none flex mt-2"},{default:n(()=>[e(m,{label:"平台名称"},{default:n(()=>[e(s,{class:"w-[400px]","show-word-limit":"",maxlength:"20",modelValue:o(l).title,"onUpdate:modelValue":t[0]||(t[0]=a=>o(l).title=a)},null,8,["modelValue"])]),_:1}),e(m,{label:"客服二维码"},{default:n(()=>[E("div",null,[e(_,{modelValue:o(l).qrcode,"onUpdate:modelValue":t[1]||(t[1]=a=>o(l).qrcode=a),"exclude-domain":""},null,8,["modelValue"])])]),_:1}),e(m,{label:"备注"},{default:n(()=>[e(s,{class:"w-[400px]","show-word-limit":"",maxlength:"20",modelValue:o(l).remark,"onUpdate:modelValue":t[2]||(t[2]=a=>o(l).remark=a)},null,8,["modelValue"])]),_:1}),e(m,{label:"联系电话"},{default:n(()=>[e(s,{class:"w-[400px]",modelValue:o(l).mobile,"onUpdate:modelValue":t[3]||(t[3]=a=>o(l).mobile=a)},null,8,["modelValue"])]),_:1}),e(m,{label:"服务时间"},{default:n(()=>[e(s,{class:"w-[400px]","show-word-limit":"",maxlength:"20",modelValue:o(l).time,"onUpdate:modelValue":t[4]||(t[4]=a=>o(l).time=a)},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})])}}});export{C as _};
@@ -1 +1 @@
import{o as E,q as s,O as r,D as t,v as l,bg as F,b6 as C,bc as N,u as a,bf as z,L as p,b7 as B,P as i,s as b,b9 as O,p as j}from"./.pnpm-Dwh6tNxD.js";import{_ as D}from"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";import{_ as I}from"./picker-DYD_beSt.js";const G=E({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(d,{emit:g}){const y=g,x=d,o=j({get:()=>x.content,set:f=>{y("update:content",f)}});return(f,e)=>{const m=z,_=N,u=C,v=B,V=I,k=D,U=F,w=O;return s(),r(w,{ref:"form","label-width":"80px",size:"large"},{default:t(()=>[l(U,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>[l(u,{label:"页面标题"},{default:t(()=>[l(_,{modelValue:a(o).title_type,"onUpdate:modelValue":e[0]||(e[0]=n=>a(o).title_type=n)},{default:t(()=>[l(m,{value:"1"},{default:t(()=>[...e[7]||(e[7]=[p("文字",-1)])]),_:1}),l(m,{value:"2"},{default:t(()=>[...e[8]||(e[8]=[p("图片",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),d.content.title_type==1?(s(),r(u,{key:0},{default:t(()=>[l(v,{modelValue:a(o).title,"onUpdate:modelValue":e[1]||(e[1]=n=>a(o).title=n),maxlength:"8","show-word-limit":"",class:"w-[300px]",placeholder:"请输入页面标题"},null,8,["modelValue"])]),_:1})):i("",!0),d.content.title_type==2?(s(),r(u,{key:1},{default:t(()=>[l(V,{modelValue:a(o).title_img,"onUpdate:modelValue":e[2]||(e[2]=n=>a(o).title_img=n),limit:1,size:"100px"},null,8,["modelValue"]),e[9]||(e[9]=b("div",{class:"form-tips"},"建议图片尺寸:300px*40px",-1))]),_:1})):i("",!0),d.content.title_type==1?(s(),r(u,{key:2,label:"文字颜色"},{default:t(()=>[l(_,{modelValue:a(o).text_color,"onUpdate:modelValue":e[3]||(e[3]=n=>a(o).text_color=n)},{default:t(()=>[l(m,{value:"1"},{default:t(()=>[...e[10]||(e[10]=[p("白色",-1)])]),_:1}),l(m,{value:"2"},{default:t(()=>[...e[11]||(e[11]=[p("黑色",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1})):i("",!0),l(u,{label:"页面背景"},{default:t(()=>[l(_,{modelValue:a(o).bg_type,"onUpdate:modelValue":e[4]||(e[4]=n=>a(o).bg_type=n)},{default:t(()=>[l(m,{value:"1"},{default:t(()=>[...e[12]||(e[12]=[p("背景颜色",-1)])]),_:1}),l(m,{value:"2"},{default:t(()=>[...e[13]||(e[13]=[p("背景图片",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),d.content.bg_type==1?(s(),r(u,{key:3},{default:t(()=>[l(k,{modelValue:a(o).bg_color,"onUpdate:modelValue":e[5]||(e[5]=n=>a(o).bg_color=n),"reset-color":"#F5F5F5"},null,8,["modelValue"])]),_:1})):i("",!0),d.content.bg_type==2?(s(),r(u,{key:4},{default:t(()=>[l(V,{modelValue:a(o).bg_image,"onUpdate:modelValue":e[6]||(e[6]=n=>a(o).bg_image=n),limit:1,size:"100px"},null,8,["modelValue"]),e[14]||(e[14]=b("div",{class:"form-tips"},"建议图片尺寸:750px*高度不限",-1))]),_:1})):i("",!0)]),_:1})]),_:1},512)}}});export{G as _}; import{o as E,q as s,O as r,D as t,v as l,bg as F,b6 as C,bc as N,u as a,bf as z,L as p,b7 as B,P as i,s as b,b9 as O,p as j}from"./.pnpm-Dwh6tNxD.js";import{_ as D}from"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";import{_ as I}from"./picker-2bpe8Dva.js";const G=E({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(d,{emit:g}){const y=g,x=d,o=j({get:()=>x.content,set:f=>{y("update:content",f)}});return(f,e)=>{const m=z,_=N,u=C,v=B,V=I,k=D,U=F,w=O;return s(),r(w,{ref:"form","label-width":"80px",size:"large"},{default:t(()=>[l(U,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>[l(u,{label:"页面标题"},{default:t(()=>[l(_,{modelValue:a(o).title_type,"onUpdate:modelValue":e[0]||(e[0]=n=>a(o).title_type=n)},{default:t(()=>[l(m,{value:"1"},{default:t(()=>[...e[7]||(e[7]=[p("文字",-1)])]),_:1}),l(m,{value:"2"},{default:t(()=>[...e[8]||(e[8]=[p("图片",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),d.content.title_type==1?(s(),r(u,{key:0},{default:t(()=>[l(v,{modelValue:a(o).title,"onUpdate:modelValue":e[1]||(e[1]=n=>a(o).title=n),maxlength:"8","show-word-limit":"",class:"w-[300px]",placeholder:"请输入页面标题"},null,8,["modelValue"])]),_:1})):i("",!0),d.content.title_type==2?(s(),r(u,{key:1},{default:t(()=>[l(V,{modelValue:a(o).title_img,"onUpdate:modelValue":e[2]||(e[2]=n=>a(o).title_img=n),limit:1,size:"100px"},null,8,["modelValue"]),e[9]||(e[9]=b("div",{class:"form-tips"},"建议图片尺寸:300px*40px",-1))]),_:1})):i("",!0),d.content.title_type==1?(s(),r(u,{key:2,label:"文字颜色"},{default:t(()=>[l(_,{modelValue:a(o).text_color,"onUpdate:modelValue":e[3]||(e[3]=n=>a(o).text_color=n)},{default:t(()=>[l(m,{value:"1"},{default:t(()=>[...e[10]||(e[10]=[p("白色",-1)])]),_:1}),l(m,{value:"2"},{default:t(()=>[...e[11]||(e[11]=[p("黑色",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1})):i("",!0),l(u,{label:"页面背景"},{default:t(()=>[l(_,{modelValue:a(o).bg_type,"onUpdate:modelValue":e[4]||(e[4]=n=>a(o).bg_type=n)},{default:t(()=>[l(m,{value:"1"},{default:t(()=>[...e[12]||(e[12]=[p("背景颜色",-1)])]),_:1}),l(m,{value:"2"},{default:t(()=>[...e[13]||(e[13]=[p("背景图片",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),d.content.bg_type==1?(s(),r(u,{key:3},{default:t(()=>[l(k,{modelValue:a(o).bg_color,"onUpdate:modelValue":e[5]||(e[5]=n=>a(o).bg_color=n),"reset-color":"#F5F5F5"},null,8,["modelValue"])]),_:1})):i("",!0),d.content.bg_type==2?(s(),r(u,{key:4},{default:t(()=>[l(V,{modelValue:a(o).bg_image,"onUpdate:modelValue":e[6]||(e[6]=n=>a(o).bg_image=n),limit:1,size:"100px"},null,8,["modelValue"]),e[14]||(e[14]=b("div",{class:"form-tips"},"建议图片尺寸:750px*高度不限",-1))]),_:1})):i("",!0)]),_:1})]),_:1},512)}}});export{G as _};
@@ -1 +1 @@
import{o as I,q as i,r as g,v as t,D as l,bg as O,s,u,bQ as j,O as F,b6 as q,b7 as z,I as A,K,L,P,b9 as Q,p as R,cm as b}from"./.pnpm-Dwh6tNxD.js";import{_ as S}from"./index-4fo3NoYp.js";import{c as T,i as v}from"./index-C9ilrjDr.js";import{_ as G}from"./picker-K9bQMD5v.js";import{_ as H}from"./picker-DYD_beSt.js";const J={class:"bg-fill-light flex items-center w-full p-4 mt-4"},M={class:"ml-3 flex-1"},W={class:"flex-1 flex items-center"},X={class:"drag-move cursor-move ml-auto"},Y={key:0,class:"mt-4"},p=5,le=I({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(r,{emit:h}){const m=h,c=r,f=R({get:()=>c.content,set:a=>{m("update:content",a)}}),k=()=>{var a;if(((a=c.content.data)==null?void 0:a.length)<p){const e=b(c.content);e.data.push({is_show:"1",image:"",name:"",link:{}}),m("update:content",e)}else v.msgError(`最多添加${p}张图片`)},w=a=>{var d;if(((d=c.content.data)==null?void 0:d.length)<=1)return v.msgError("最少保留一张图片");const e=b(c.content);e.data.splice(a,1),m("update:content",e)};return(a,e)=>{const d=H,y=z,_=q,E=G,U=A,C=T,B=S,D=K,N=O,$=Q;return i(),g("div",null,[t($,{"label-width":"70px"},{default:l(()=>[t(N,{shadow:"never",class:"!border-none flex mt-2"},{default:l(()=>{var x;return[e[2]||(e[2]=s("div",{class:"flex items-end mb-4"},[s("div",{class:"text-base text-[#101010] font-medium"},"菜单"),s("div",{class:"text-xs text-tx-secondary ml-2"}," 最多添加5张,建议图片尺寸:750px*200px ")],-1)),t(u(j),{class:"draggable",modelValue:u(f).data,"onUpdate:modelValue":e[0]||(e[0]=o=>u(f).data=o),animation:"300",handle:".drag-move","item-key":"index"},{item:l(({element:o,index:V})=>[(i(),F(B,{key:V,onClose:n=>w(V),class:"w-[467px]"},{default:l(()=>[s("div",J,[t(d,{modelValue:o.image,"onUpdate:modelValue":n=>o.image=n,"upload-class":"bg-body","exclude-domain":""},null,8,["modelValue","onUpdate:modelValue"]),s("div",M,[t(_,{label:"图片名称"},{default:l(()=>[t(y,{modelValue:o.name,"onUpdate:modelValue":n=>o.name=n,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),_:2},1024),t(_,{class:"mt-[18px]",label:"图片链接"},{default:l(()=>[t(E,{modelValue:o.link,"onUpdate:modelValue":n=>o.link=n},null,8,["modelValue","onUpdate:modelValue"])]),_:2},1024),t(_,{label:"是否显示",class:"mt-[18px]"},{default:l(()=>[s("div",W,[t(U,{modelValue:o.is_show,"onUpdate:modelValue":n=>o.is_show=n,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),s("div",X,[t(C,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"]),((x=r.content.data)==null?void 0:x.length)<p?(i(),g("div",Y,[t(D,{class:"w-full",type:"primary",onClick:k},{default:l(()=>[...e[1]||(e[1]=[L("添加图片",-1)])]),_:1})])):P("",!0)]}),_:1})]),_:1})])}}});export{le as _}; import{o as I,q as i,r as g,v as t,D as l,bg as O,s,u,bQ as j,O as F,b6 as q,b7 as z,I as A,K,L,P,b9 as Q,p as R,cm as b}from"./.pnpm-Dwh6tNxD.js";import{_ as S}from"./index-39SS-XKl.js";import{c as T,i as v}from"./index-C0Q8AuuZ.js";import{_ as G}from"./picker-D3OnL1mV.js";import{_ as H}from"./picker-2bpe8Dva.js";const J={class:"bg-fill-light flex items-center w-full p-4 mt-4"},M={class:"ml-3 flex-1"},W={class:"flex-1 flex items-center"},X={class:"drag-move cursor-move ml-auto"},Y={key:0,class:"mt-4"},p=5,le=I({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(r,{emit:h}){const m=h,c=r,f=R({get:()=>c.content,set:a=>{m("update:content",a)}}),k=()=>{var a;if(((a=c.content.data)==null?void 0:a.length)<p){const e=b(c.content);e.data.push({is_show:"1",image:"",name:"",link:{}}),m("update:content",e)}else v.msgError(`最多添加${p}张图片`)},w=a=>{var d;if(((d=c.content.data)==null?void 0:d.length)<=1)return v.msgError("最少保留一张图片");const e=b(c.content);e.data.splice(a,1),m("update:content",e)};return(a,e)=>{const d=H,y=z,_=q,E=G,U=A,C=T,B=S,D=K,N=O,$=Q;return i(),g("div",null,[t($,{"label-width":"70px"},{default:l(()=>[t(N,{shadow:"never",class:"!border-none flex mt-2"},{default:l(()=>{var x;return[e[2]||(e[2]=s("div",{class:"flex items-end mb-4"},[s("div",{class:"text-base text-[#101010] font-medium"},"菜单"),s("div",{class:"text-xs text-tx-secondary ml-2"}," 最多添加5张,建议图片尺寸:750px*200px ")],-1)),t(u(j),{class:"draggable",modelValue:u(f).data,"onUpdate:modelValue":e[0]||(e[0]=o=>u(f).data=o),animation:"300",handle:".drag-move","item-key":"index"},{item:l(({element:o,index:V})=>[(i(),F(B,{key:V,onClose:n=>w(V),class:"w-[467px]"},{default:l(()=>[s("div",J,[t(d,{modelValue:o.image,"onUpdate:modelValue":n=>o.image=n,"upload-class":"bg-body","exclude-domain":""},null,8,["modelValue","onUpdate:modelValue"]),s("div",M,[t(_,{label:"图片名称"},{default:l(()=>[t(y,{modelValue:o.name,"onUpdate:modelValue":n=>o.name=n,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),_:2},1024),t(_,{class:"mt-[18px]",label:"图片链接"},{default:l(()=>[t(E,{modelValue:o.link,"onUpdate:modelValue":n=>o.link=n},null,8,["modelValue","onUpdate:modelValue"])]),_:2},1024),t(_,{label:"是否显示",class:"mt-[18px]"},{default:l(()=>[s("div",W,[t(U,{modelValue:o.is_show,"onUpdate:modelValue":n=>o.is_show=n,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),s("div",X,[t(C,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"]),((x=r.content.data)==null?void 0:x.length)<p?(i(),g("div",Y,[t(D,{class:"w-full",type:"primary",onClick:k},{default:l(()=>[...e[1]||(e[1]=[L("添加图片",-1)])]),_:1})])):P("",!0)]}),_:1})]),_:1})])}}});export{le as _};
@@ -1 +1 @@
import{o as j,q as V,r as v,v as e,D as t,s,L as h,bg as q,b6 as A,u as m,bQ as K,ae as M,b7 as O,I as Q,K as R,T as w,P as G,b9 as H,F as J,p as y}from"./.pnpm-Dwh6tNxD.js";import{_ as W}from"./index-4fo3NoYp.js";import{_ as X}from"./picker-K9bQMD5v.js";import{_ as Y}from"./picker-DYD_beSt.js";import{c as Z,i as b}from"./index-C9ilrjDr.js";import{_ as ee}from"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";const le={class:"mb-[18px] max-w-[400px]"},oe={class:"bg-fill-light w-full p-4 mt-4"},te={class:"upload-btn w-[60px] h-[60px]"},se={class:"upload-btn w-[60px] h-[60px]"},ae={class:"flex-1 flex items-center"},ne={class:"drag-move cursor-move ml-auto"},de={key:0,class:"mt-4"},c=5,p=2,_e=j({__name:"attr",props:{modelValue:{type:Object,default:()=>({list:[],style:{}})}},emits:["update:modelValue"],setup(k,{emit:U}){const C=k,E=U,n=y({get(){return C.modelValue},set(a){E("update:modelValue",a)}}),$=y(()=>{var a;return((a=n.value.list)==null?void 0:a.filter(l=>l.is_show=="1"))||[]}),z=()=>{var a;((a=n.value.list)==null?void 0:a.length)<c?n.value.list.push({name:"",selected:"",unselected:"",is_show:1,link:{}}):b.msgError(`最多添加${c}`)},B=a=>{var l;if(((l=n.value.list)==null?void 0:l.length)<=p)return b.msgError(`最少保留${p}`);n.value.list.splice(a,1)},D=a=>a.relatedContext.index!=0,F=a=>{if($.value.length<p)return a.is_show=1,b.msgError(`最少显示${p}`)};return(a,l)=>{const _=q,x=ee,i=A,f=Z,g=Y,N=O,I=X,P=Q,S=W,T=R,L=H;return V(),v(J,null,[e(_,{shadow:"never",class:"!border-none flex"},{default:t(()=>[...l[3]||(l[3]=[s("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},[h(" 底部导航设置 "),s("span",{class:"form-tips ml-[10px] !mt-0"}," 至少添加2个导航,最多添加5个导航 ")],-1)])]),_:1}),e(L,{"label-width":"70px"},{default:t(()=>[e(_,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>[l[4]||(l[4]=s("div",{class:"flex items-end mb-4"},[s("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),e(i,{label:"默认颜色"},{default:t(()=>[e(x,{class:"max-w-[400px]",modelValue:m(n).style.default_color,"onUpdate:modelValue":l[0]||(l[0]=u=>m(n).style.default_color=u),"default-color":"#999999"},null,8,["modelValue"])]),_:1}),e(i,{label:"选中颜色",style:{"margin-bottom":"0"}},{default:t(()=>[e(x,{class:"max-w-[400px]",modelValue:m(n).style.selected_color,"onUpdate:modelValue":l[1]||(l[1]=u=>m(n).style.selected_color=u),"default-color":"#4173ff"},null,8,["modelValue"])]),_:1})]),_:1}),e(_,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>{var u;return[l[7]||(l[7]=s("div",{class:"flex items-end mb-4"},[s("div",{class:"text-base text-[#101010] font-medium"},"菜单设置"),s("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),s("div",le,[e(m(K),{class:"draggable",modelValue:m(n).list,"onUpdate:modelValue":l[2]||(l[2]=o=>m(n).list=o),animation:"300",draggable:".draggable",handle:".drag-move",move:D,"item-key":"index"},{item:t(({element:o,index:r})=>[e(S,{onClose:d=>B(r),class:M(["max-w-[400px]",{draggable:r!=0}]),"show-close":r!==0},{default:t(()=>[s("div",oe,[e(i,{label:"导航图标"},{default:t(()=>[e(g,{modelValue:o.unselected,"onUpdate:modelValue":d=>o.unselected=d,"upload-class":"bg-body","exclude-domain":"",size:"60px"},{upload:t(()=>[s("div",te,[e(f,{name:"el-icon-Plus",size:16}),l[5]||(l[5]=s("span",{class:"text-xs leading-5"}," 未选中 ",-1))])]),_:1},8,["modelValue","onUpdate:modelValue"]),e(g,{modelValue:o.selected,"onUpdate:modelValue":d=>o.selected=d,"exclude-domain":"","upload-class":"bg-body",size:"60px"},{upload:t(()=>[s("div",se,[e(f,{name:"el-icon-Plus",size:16}),l[6]||(l[6]=s("span",{class:"text-xs leading-5"}," 选中 ",-1))])]),_:1},8,["modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"导航名称"},{default:t(()=>[e(N,{modelValue:o.name,"onUpdate:modelValue":d=>o.name=d,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"链接地址"},{default:t(()=>[e(I,{"is-tab":!0,disabled:r===0,modelValue:o.link,"onUpdate:modelValue":d=>o.link=d},null,8,["disabled","modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"是否显示"},{default:t(()=>[s("div",ae,[e(P,{disabled:r==0,modelValue:o.is_show,"onUpdate:modelValue":d=>o.is_show=d,"active-value":1,"inactive-value":0,onChange:d=>F(o)},null,8,["disabled","modelValue","onUpdate:modelValue","onChange"]),s("div",ne,[e(f,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])]),_:2},1032,["onClose","show-close","class"])]),_:1},8,["modelValue"])]),((u=m(n).list)==null?void 0:u.length)<c?(V(),v("div",de,[e(T,{class:"w-full",type:"primary",onClick:z},{default:t(()=>{var o;return[h(" 添加导航 "+w((o=m(n).list)==null?void 0:o.length)+" / "+w(c),1)]}),_:1})])):G("",!0)]}),_:1})]),_:1})],64)}}});export{_e as _}; import{o as j,q as V,r as v,v as e,D as t,s,L as h,bg as q,b6 as A,u as m,bQ as K,ae as M,b7 as O,I as Q,K as R,T as w,P as G,b9 as H,F as J,p as y}from"./.pnpm-Dwh6tNxD.js";import{_ as W}from"./index-39SS-XKl.js";import{_ as X}from"./picker-D3OnL1mV.js";import{_ as Y}from"./picker-2bpe8Dva.js";import{c as Z,i as b}from"./index-C0Q8AuuZ.js";import{_ as ee}from"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";const le={class:"mb-[18px] max-w-[400px]"},oe={class:"bg-fill-light w-full p-4 mt-4"},te={class:"upload-btn w-[60px] h-[60px]"},se={class:"upload-btn w-[60px] h-[60px]"},ae={class:"flex-1 flex items-center"},ne={class:"drag-move cursor-move ml-auto"},de={key:0,class:"mt-4"},c=5,p=2,_e=j({__name:"attr",props:{modelValue:{type:Object,default:()=>({list:[],style:{}})}},emits:["update:modelValue"],setup(k,{emit:U}){const C=k,E=U,n=y({get(){return C.modelValue},set(a){E("update:modelValue",a)}}),$=y(()=>{var a;return((a=n.value.list)==null?void 0:a.filter(l=>l.is_show=="1"))||[]}),z=()=>{var a;((a=n.value.list)==null?void 0:a.length)<c?n.value.list.push({name:"",selected:"",unselected:"",is_show:1,link:{}}):b.msgError(`最多添加${c}`)},B=a=>{var l;if(((l=n.value.list)==null?void 0:l.length)<=p)return b.msgError(`最少保留${p}`);n.value.list.splice(a,1)},D=a=>a.relatedContext.index!=0,F=a=>{if($.value.length<p)return a.is_show=1,b.msgError(`最少显示${p}`)};return(a,l)=>{const _=q,x=ee,i=A,f=Z,g=Y,N=O,I=X,P=Q,S=W,T=R,L=H;return V(),v(J,null,[e(_,{shadow:"never",class:"!border-none flex"},{default:t(()=>[...l[3]||(l[3]=[s("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},[h(" 底部导航设置 "),s("span",{class:"form-tips ml-[10px] !mt-0"}," 至少添加2个导航,最多添加5个导航 ")],-1)])]),_:1}),e(L,{"label-width":"70px"},{default:t(()=>[e(_,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>[l[4]||(l[4]=s("div",{class:"flex items-end mb-4"},[s("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),e(i,{label:"默认颜色"},{default:t(()=>[e(x,{class:"max-w-[400px]",modelValue:m(n).style.default_color,"onUpdate:modelValue":l[0]||(l[0]=u=>m(n).style.default_color=u),"default-color":"#999999"},null,8,["modelValue"])]),_:1}),e(i,{label:"选中颜色",style:{"margin-bottom":"0"}},{default:t(()=>[e(x,{class:"max-w-[400px]",modelValue:m(n).style.selected_color,"onUpdate:modelValue":l[1]||(l[1]=u=>m(n).style.selected_color=u),"default-color":"#4173ff"},null,8,["modelValue"])]),_:1})]),_:1}),e(_,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>{var u;return[l[7]||(l[7]=s("div",{class:"flex items-end mb-4"},[s("div",{class:"text-base text-[#101010] font-medium"},"菜单设置"),s("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),s("div",le,[e(m(K),{class:"draggable",modelValue:m(n).list,"onUpdate:modelValue":l[2]||(l[2]=o=>m(n).list=o),animation:"300",draggable:".draggable",handle:".drag-move",move:D,"item-key":"index"},{item:t(({element:o,index:r})=>[e(S,{onClose:d=>B(r),class:M(["max-w-[400px]",{draggable:r!=0}]),"show-close":r!==0},{default:t(()=>[s("div",oe,[e(i,{label:"导航图标"},{default:t(()=>[e(g,{modelValue:o.unselected,"onUpdate:modelValue":d=>o.unselected=d,"upload-class":"bg-body","exclude-domain":"",size:"60px"},{upload:t(()=>[s("div",te,[e(f,{name:"el-icon-Plus",size:16}),l[5]||(l[5]=s("span",{class:"text-xs leading-5"}," 未选中 ",-1))])]),_:1},8,["modelValue","onUpdate:modelValue"]),e(g,{modelValue:o.selected,"onUpdate:modelValue":d=>o.selected=d,"exclude-domain":"","upload-class":"bg-body",size:"60px"},{upload:t(()=>[s("div",se,[e(f,{name:"el-icon-Plus",size:16}),l[6]||(l[6]=s("span",{class:"text-xs leading-5"}," 选中 ",-1))])]),_:1},8,["modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"导航名称"},{default:t(()=>[e(N,{modelValue:o.name,"onUpdate:modelValue":d=>o.name=d,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"链接地址"},{default:t(()=>[e(I,{"is-tab":!0,disabled:r===0,modelValue:o.link,"onUpdate:modelValue":d=>o.link=d},null,8,["disabled","modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"是否显示"},{default:t(()=>[s("div",ae,[e(P,{disabled:r==0,modelValue:o.is_show,"onUpdate:modelValue":d=>o.is_show=d,"active-value":1,"inactive-value":0,onChange:d=>F(o)},null,8,["disabled","modelValue","onUpdate:modelValue","onChange"]),s("div",ne,[e(f,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])]),_:2},1032,["onClose","show-close","class"])]),_:1},8,["modelValue"])]),((u=m(n).list)==null?void 0:u.length)<c?(V(),v("div",de,[e(T,{class:"w-full",type:"primary",onClick:z},{default:t(()=>{var o;return[h(" 添加导航 "+w((o=m(n).list)==null?void 0:o.length)+" / "+w(c),1)]}),_:1})])):G("",!0)]}),_:1})]),_:1})],64)}}});export{_e as _};
@@ -1 +1 @@
import{o as V,q as E,r as w,v as t,D as o,bg as y,b6 as g,b7 as B,u as s,s as a,bc as C,bf as N,L as u,b9 as U,p as j}from"./.pnpm-Dwh6tNxD.js";import{_ as k}from"./add-nav.vue_vue_type_script_setup_true_lang-D73JnCTp.js";const D={class:"flex-1"},O=V({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(p,{emit:i}){const f=i,_=p,l=j({get:()=>_.content,set:m=>{f("update:content",m)}});return(m,e)=>{const x=B,c=g,d=y,r=N,b=C,v=U;return E(),w("div",null,[t(v,{"label-width":"70px"},{default:o(()=>[t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[t(c,{label:"标题"},{default:o(()=>[t(x,{class:"w-[396px]",modelValue:s(l).title,"onUpdate:modelValue":e[0]||(e[0]=n=>s(l).title=n)},null,8,["modelValue"])]),_:1})]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[5]||(e[5]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),t(b,{modelValue:s(l).style,"onUpdate:modelValue":e[1]||(e[1]=n=>s(l).style=n)},{default:o(()=>[t(r,{value:1},{default:o(()=>[...e[3]||(e[3]=[u("横排",-1)])]),_:1}),t(r,{value:2},{default:o(()=>[...e[4]||(e[4]=[u("竖排",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[6]||(e[6]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"菜单"),a("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),a("div",D,[t(k,{modelValue:s(l).data,"onUpdate:modelValue":e[2]||(e[2]=n=>s(l).data=n)},null,8,["modelValue"])])]),_:1})]),_:1})])}}});export{O as _}; import{o as V,q as E,r as w,v as t,D as o,bg as y,b6 as g,b7 as B,u as s,s as a,bc as C,bf as N,L as u,b9 as U,p as j}from"./.pnpm-Dwh6tNxD.js";import{_ as k}from"./add-nav.vue_vue_type_script_setup_true_lang-CH8U6Lyn.js";const D={class:"flex-1"},O=V({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(p,{emit:i}){const f=i,_=p,l=j({get:()=>_.content,set:m=>{f("update:content",m)}});return(m,e)=>{const x=B,c=g,d=y,r=N,b=C,v=U;return E(),w("div",null,[t(v,{"label-width":"70px"},{default:o(()=>[t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[t(c,{label:"标题"},{default:o(()=>[t(x,{class:"w-[396px]",modelValue:s(l).title,"onUpdate:modelValue":e[0]||(e[0]=n=>s(l).title=n)},null,8,["modelValue"])]),_:1})]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[5]||(e[5]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),t(b,{modelValue:s(l).style,"onUpdate:modelValue":e[1]||(e[1]=n=>s(l).style=n)},{default:o(()=>[t(r,{value:1},{default:o(()=>[...e[3]||(e[3]=[u("横排",-1)])]),_:1}),t(r,{value:2},{default:o(()=>[...e[4]||(e[4]=[u("竖排",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[6]||(e[6]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"菜单"),a("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),a("div",D,[t(k,{modelValue:s(l).data,"onUpdate:modelValue":e[2]||(e[2]=n=>s(l).data=n)},null,8,["modelValue"])])]),_:1})]),_:1})])}}});export{O as _};
@@ -1 +0,0 @@
import{_ as o}from"./auth.vue_vue_type_script_setup_true_lang-DQuZW-nx.js";import"./.pnpm-Dwh6tNxD.js";import"./menu-_5V8MFoG.js";import"./index-C9ilrjDr.js";import"./role-C1NLqyir.js";import"./index-BM6NmNnG.js";export{o as default};
@@ -0,0 +1 @@
import{_ as o}from"./auth.vue_vue_type_script_setup_true_lang-CIpBYzCi.js";import"./.pnpm-Dwh6tNxD.js";import"./menu-0FOsMWO2.js";import"./index-C0Q8AuuZ.js";import"./role-BAOZKGDz.js";import"./index-CfJcNX3X.js";export{o as default};
@@ -1 +1 @@
import{o as O,q as k,r as U,v as s,D as d,ac as H,O as I,b9 as j,u as c,a2 as z,b6 as G,s as y,bd as J,C as P,bU as Q,bi as W,a8 as f,M as r,ba as X,n as b}from"./.pnpm-Dwh6tNxD.js";import{m as Y}from"./menu-_5V8MFoG.js";import{a as Z}from"./role-C1NLqyir.js";import{_ as $}from"./index-BM6NmNnG.js";import{x as ee}from"./index-C9ilrjDr.js";const te={class:"edit-popup"},re=O({__name:"auth",emits:["success","close"],setup(ae,{expose:x,emit:C}){const _=C,l=f(),h=f(),u=f(),g=r(!1),i=r(!0),m=r(!1),v=r([]),p=r([]),o=X({id:"",name:"",desc:"",sort:0,data_scope:1,menu_id:[]}),E={name:[{required:!0,message:"请输入名称",trigger:["blur"]}]},w=()=>{m.value=!0,Y().then(e=>{p.value=e,v.value=ee(e),b(()=>{A()}),m.value=!1})},R=()=>{var a,n;const e=(a=l.value)==null?void 0:a.getCheckedKeys(),t=(n=l.value)==null?void 0:n.getHalfCheckedKeys();return e==null||e.unshift.apply(e,t),e},A=()=>{o.menu_id.forEach(e=>{b(()=>{var t;(t=l.value)==null||t.setChecked(e,!0,!1)})})},D=e=>{const t=p.value;for(let a=0;a<t.length;a++)l.value.store.nodesMap[t[a].id].expanded=e},K=e=>{var t,a;e?(t=l.value)==null||t.setCheckedKeys(v.value.map(n=>n.id)):(a=l.value)==null||a.setCheckedKeys([])},V=async()=>{var e,t;await((e=h.value)==null?void 0:e.validate()),o.menu_id=R(),await Z(o),(t=u.value)==null||t.close(),_("success")},B=()=>{_("close")},S=()=>{var e;(e=u.value)==null||e.open()},T=async e=>{for(const t in o)e[t]!=null&&e[t]!=null&&(o[t]=e[t])};return w(),x({open:S,setFormData:T}),(e,t)=>{const a=J,n=Q,F=G,q=z,L=j,M=W;return k(),U("div",te,[s($,{ref_key:"popupRef",ref:u,title:"分配权限",async:!0,width:"550px",onConfirm:V,onClose:B},{default:d(()=>[H((k(),I(L,{class:"ls-form",ref_key:"formRef",ref:h,rules:E,model:c(o),"label-width":"60px"},{default:d(()=>[s(q,{class:"h-[400px] sm:h-[600px]"},{default:d(()=>[s(F,{label:"权限",prop:"menu_id"},{default:d(()=>[y("div",null,[s(a,{label:"展开/折叠",onChange:D}),s(a,{label:"全选/不全选",onChange:K}),s(a,{modelValue:c(i),"onUpdate:modelValue":t[0]||(t[0]=N=>P(i)?i.value=N:null),label:"父子联动"},null,8,["modelValue"]),y("div",null,[s(n,{ref_key:"treeRef",ref:l,data:c(p),props:{label:"name",children:"children"},"check-strictly":!c(i),"node-key":"id","default-expand-all":c(g),"show-checkbox":""},null,8,["data","check-strictly","default-expand-all"])])])]),_:1})]),_:1})]),_:1},8,["model"])),[[M,c(m)]])]),_:1},512)])}}});export{re as _}; import{o as O,q as k,r as U,v as s,D as d,ac as H,O as I,b9 as j,u as c,a2 as z,b6 as G,s as y,bd as J,C as P,bU as Q,bi as W,a8 as f,M as r,ba as X,n as b}from"./.pnpm-Dwh6tNxD.js";import{m as Y}from"./menu-0FOsMWO2.js";import{a as Z}from"./role-BAOZKGDz.js";import{_ as $}from"./index-CfJcNX3X.js";import{x as ee}from"./index-C0Q8AuuZ.js";const te={class:"edit-popup"},re=O({__name:"auth",emits:["success","close"],setup(ae,{expose:x,emit:C}){const _=C,l=f(),h=f(),u=f(),g=r(!1),i=r(!0),m=r(!1),v=r([]),p=r([]),o=X({id:"",name:"",desc:"",sort:0,data_scope:1,menu_id:[]}),E={name:[{required:!0,message:"请输入名称",trigger:["blur"]}]},w=()=>{m.value=!0,Y().then(e=>{p.value=e,v.value=ee(e),b(()=>{A()}),m.value=!1})},R=()=>{var a,n;const e=(a=l.value)==null?void 0:a.getCheckedKeys(),t=(n=l.value)==null?void 0:n.getHalfCheckedKeys();return e==null||e.unshift.apply(e,t),e},A=()=>{o.menu_id.forEach(e=>{b(()=>{var t;(t=l.value)==null||t.setChecked(e,!0,!1)})})},D=e=>{const t=p.value;for(let a=0;a<t.length;a++)l.value.store.nodesMap[t[a].id].expanded=e},K=e=>{var t,a;e?(t=l.value)==null||t.setCheckedKeys(v.value.map(n=>n.id)):(a=l.value)==null||a.setCheckedKeys([])},V=async()=>{var e,t;await((e=h.value)==null?void 0:e.validate()),o.menu_id=R(),await Z(o),(t=u.value)==null||t.close(),_("success")},B=()=>{_("close")},S=()=>{var e;(e=u.value)==null||e.open()},T=async e=>{for(const t in o)e[t]!=null&&e[t]!=null&&(o[t]=e[t])};return w(),x({open:S,setFormData:T}),(e,t)=>{const a=J,n=Q,F=G,q=z,L=j,M=W;return k(),U("div",te,[s($,{ref_key:"popupRef",ref:u,title:"分配权限",async:!0,width:"550px",onConfirm:V,onClose:B},{default:d(()=>[H((k(),I(L,{class:"ls-form",ref_key:"formRef",ref:h,rules:E,model:c(o),"label-width":"60px"},{default:d(()=>[s(q,{class:"h-[400px] sm:h-[600px]"},{default:d(()=>[s(F,{label:"权限",prop:"menu_id"},{default:d(()=>[y("div",null,[s(a,{label:"展开/折叠",onChange:D}),s(a,{label:"全选/不全选",onChange:K}),s(a,{modelValue:c(i),"onUpdate:modelValue":t[0]||(t[0]=N=>P(i)?i.value=N:null),label:"父子联动"},null,8,["modelValue"]),y("div",null,[s(n,{ref_key:"treeRef",ref:l,data:c(p),props:{label:"name",children:"children"},"check-strictly":!c(i),"node-key":"id","default-expand-all":c(g),"show-checkbox":""},null,8,["data","check-strictly","default-expand-all"])])])]),_:1})]),_:1})]),_:1},8,["model"])),[[M,c(m)]])]),_:1},512)])}}});export{re as _};
@@ -1 +1 @@
import{o as y,q as m,r as h,v as e,D as l,bh as K,b9 as P,u as a,b6 as N,b7 as O,b8 as j,bm as q,bn as z,F as I,G as R,O as w,K as S,L as _,bg as $,ac as A,bj as G,bk as H,s as p,T as v,ae as J,bi as M,C as Q,ba as W}from"./.pnpm-Dwh6tNxD.js";import{_ as X}from"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import{h as Y}from"./index-C9ilrjDr.js";import{_ as Z}from"./index.vue_vue_type_script_setup_true_lang-C9hPqHM6.js";import{g as ee,h as te}from"./finance-CnBidH5A.js";import{u as ae}from"./useDictOptions-VG1Pcf0t.js";import{u as ne}from"./usePaging-DOuAwzL9.js";const le={class:"flex items-center"},oe={class:"flex justify-end mt-4"},se=y({name:"balanceDetail"}),be=y({...se,setup(ie){const o=W({user_info:"",change_type:"",start_time:"",end_time:""}),{pager:i,getLists:d,resetPage:c,resetParams:C}=ne({fetchFun:ee,params:o}),{optionsData:x}=ae({change_type:{api:te}});return d(),(re,n)=>{const V=K,E=O,r=N,u=z,T=q,k=Z,f=S,D=P,b=$,s=H,U=Y,B=G,F=X,L=M;return m(),h("div",null,[e(b,{class:"!border-none",shadow:"never"},{default:l(()=>[e(V,{type:"warning",title:"温馨提示:用户账户变动记录",closable:!1,"show-icon":""}),e(D,{ref:"formRef",class:"mb-[-16px] mt-[16px]",model:a(o),inline:!0},{default:l(()=>[e(r,{class:"w-[280px]",label:"用户信息"},{default:l(()=>[e(E,{modelValue:a(o).user_info,"onUpdate:modelValue":n[0]||(n[0]=t=>a(o).user_info=t),placeholder:"请输入用户账号/昵称/手机号",clearable:"",onKeyup:j(a(c),["enter"])},null,8,["modelValue","onKeyup"])]),_:1}),e(r,{class:"w-[280px]",label:"变动类型"},{default:l(()=>[e(T,{modelValue:a(o).change_type,"onUpdate:modelValue":n[1]||(n[1]=t=>a(o).change_type=t)},{default:l(()=>[e(u,{label:"全部",value:""}),(m(!0),h(I,null,R(a(x).change_type,(t,g)=>(m(),w(u,{key:g,label:t,value:g},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),e(r,{label:"记录时间"},{default:l(()=>[e(k,{startTime:a(o).start_time,"onUpdate:startTime":n[2]||(n[2]=t=>a(o).start_time=t),endTime:a(o).end_time,"onUpdate:endTime":n[3]||(n[3]=t=>a(o).end_time=t)},null,8,["startTime","endTime"])]),_:1}),e(r,null,{default:l(()=>[e(f,{type:"primary",onClick:a(c)},{default:l(()=>[...n[5]||(n[5]=[_("查询",-1)])]),_:1},8,["onClick"]),e(f,{onClick:a(C)},{default:l(()=>[...n[6]||(n[6]=[_("重置",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["model"])]),_:1}),e(b,{class:"!border-none mt-4",shadow:"never"},{default:l(()=>[A((m(),w(B,{size:"large",data:a(i).lists},{default:l(()=>[e(s,{label:"用户账号",prop:"account","min-width":"100"}),e(s,{label:"用户昵称","min-width":"160"},{default:l(({row:t})=>[p("div",le,[e(U,{class:"flex-none mr-2",src:t.avatar,width:40,height:40,"preview-teleported":"",fit:"contain"},null,8,["src"]),_(" "+v(t.nickname),1)])]),_:1}),e(s,{label:"手机号码",prop:"mobile","min-width":"100"}),e(s,{label:"变动金额",prop:"change_amount","min-width":"100"},{default:l(({row:t})=>[p("span",{class:J({"text-error":t.action==2})},v(t.change_amount),3)]),_:1}),e(s,{label:"剩余金额",prop:"left_amount","min-width":"100"}),e(s,{label:"变动类型",prop:"change_type_desc","min-width":"120"}),e(s,{label:"来源单号",prop:"source_sn","min-width":"100"}),e(s,{label:"记录时间",prop:"create_time","min-width":"120"})]),_:1},8,["data"])),[[L,a(i).loading]]),p("div",oe,[e(F,{modelValue:a(i),"onUpdate:modelValue":n[4]||(n[4]=t=>Q(i)?i.value=t:null),onChange:a(d)},null,8,["modelValue","onChange"])])]),_:1})])}}});export{be as default}; import{o as y,q as m,r as h,v as e,D as l,bh as K,b9 as P,u as a,b6 as N,b7 as O,b8 as j,bm as q,bn as z,F as I,G as R,O as w,K as S,L as _,bg as $,ac as A,bj as G,bk as H,s as p,T as v,ae as J,bi as M,C as Q,ba as W}from"./.pnpm-Dwh6tNxD.js";import{_ as X}from"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import{h as Y}from"./index-C0Q8AuuZ.js";import{_ as Z}from"./index.vue_vue_type_script_setup_true_lang-C9hPqHM6.js";import{g as ee,h as te}from"./finance-BoalIhuu.js";import{u as ae}from"./useDictOptions-C-sTmCFl.js";import{u as ne}from"./usePaging-DOuAwzL9.js";const le={class:"flex items-center"},oe={class:"flex justify-end mt-4"},se=y({name:"balanceDetail"}),be=y({...se,setup(ie){const o=W({user_info:"",change_type:"",start_time:"",end_time:""}),{pager:i,getLists:d,resetPage:c,resetParams:C}=ne({fetchFun:ee,params:o}),{optionsData:x}=ae({change_type:{api:te}});return d(),(re,n)=>{const V=K,E=O,r=N,u=z,T=q,k=Z,f=S,D=P,b=$,s=H,U=Y,B=G,F=X,L=M;return m(),h("div",null,[e(b,{class:"!border-none",shadow:"never"},{default:l(()=>[e(V,{type:"warning",title:"温馨提示:用户账户变动记录",closable:!1,"show-icon":""}),e(D,{ref:"formRef",class:"mb-[-16px] mt-[16px]",model:a(o),inline:!0},{default:l(()=>[e(r,{class:"w-[280px]",label:"用户信息"},{default:l(()=>[e(E,{modelValue:a(o).user_info,"onUpdate:modelValue":n[0]||(n[0]=t=>a(o).user_info=t),placeholder:"请输入用户账号/昵称/手机号",clearable:"",onKeyup:j(a(c),["enter"])},null,8,["modelValue","onKeyup"])]),_:1}),e(r,{class:"w-[280px]",label:"变动类型"},{default:l(()=>[e(T,{modelValue:a(o).change_type,"onUpdate:modelValue":n[1]||(n[1]=t=>a(o).change_type=t)},{default:l(()=>[e(u,{label:"全部",value:""}),(m(!0),h(I,null,R(a(x).change_type,(t,g)=>(m(),w(u,{key:g,label:t,value:g},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),e(r,{label:"记录时间"},{default:l(()=>[e(k,{startTime:a(o).start_time,"onUpdate:startTime":n[2]||(n[2]=t=>a(o).start_time=t),endTime:a(o).end_time,"onUpdate:endTime":n[3]||(n[3]=t=>a(o).end_time=t)},null,8,["startTime","endTime"])]),_:1}),e(r,null,{default:l(()=>[e(f,{type:"primary",onClick:a(c)},{default:l(()=>[...n[5]||(n[5]=[_("查询",-1)])]),_:1},8,["onClick"]),e(f,{onClick:a(C)},{default:l(()=>[...n[6]||(n[6]=[_("重置",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["model"])]),_:1}),e(b,{class:"!border-none mt-4",shadow:"never"},{default:l(()=>[A((m(),w(B,{size:"large",data:a(i).lists},{default:l(()=>[e(s,{label:"用户账号",prop:"account","min-width":"100"}),e(s,{label:"用户昵称","min-width":"160"},{default:l(({row:t})=>[p("div",le,[e(U,{class:"flex-none mr-2",src:t.avatar,width:40,height:40,"preview-teleported":"",fit:"contain"},null,8,["src"]),_(" "+v(t.nickname),1)])]),_:1}),e(s,{label:"手机号码",prop:"mobile","min-width":"100"}),e(s,{label:"变动金额",prop:"change_amount","min-width":"100"},{default:l(({row:t})=>[p("span",{class:J({"text-error":t.action==2})},v(t.change_amount),3)]),_:1}),e(s,{label:"剩余金额",prop:"left_amount","min-width":"100"}),e(s,{label:"变动类型",prop:"change_type_desc","min-width":"120"}),e(s,{label:"来源单号",prop:"source_sn","min-width":"100"}),e(s,{label:"记录时间",prop:"create_time","min-width":"120"})]),_:1},8,["data"])),[[L,a(i).loading]]),p("div",oe,[e(F,{modelValue:a(i),"onUpdate:modelValue":n[4]||(n[4]=t=>Q(i)?i.value=t:null),onChange:a(d)},null,8,["modelValue","onChange"])])]),_:1})])}}});export{be as default};
@@ -1 +1 @@
import{o as T,W as $,ap as j,E as p,aq as z,q as d,r as l,s as r,v as c,D as y,u as S,b5 as I,w as A,F as D,L as M,K as F,M as k,n as C}from"./.pnpm-Dwh6tNxD.js";import{_ as K}from"./footer.vue_vue_type_script_setup_true_lang-BbowIPV2.js";import{u as Q,g as V,P as L,a as X,b as G,_ as H}from"./index-C9ilrjDr.js";import{a as J}from"./wecomOauthPostMessage-C_e6VGrO.js";const Y={class:"bind-wx flex flex-col"},Z={class:"flex-1 flex items-center justify-center"},ee={class:"bind-wx-card bg-body rounded-md px-10 py-10 w-[480px]"},te={key:0,class:"text-center py-10"},oe={key:0,class:"text-center py-10"},ne={key:1,id:"wxwork_bind_qrcode_container",class:"wxwork-qrcode mx-auto"},ae={class:"mt-8 flex justify-center gap-4"},w="bind_wxwork",g="like_admin_wx_bind_oauth",se=T({__name:"bind-work-wechat",setup(re){const u=$(),f=Q(),x=k(!1),_=k(!1),m=k({corp_id:"",agent_id:""}),U=()=>/wxwork/i.test(navigator.userAgent),v=()=>window.location.origin+window.location.pathname+"?bind_wxwork=1";function W(){const o=new URLSearchParams(window.location.search),e=window.location.hash||"",t=e.includes("?")?e.split("?").slice(1).join("?"):"";return t&&new URLSearchParams(t).forEach((n,i)=>{o.has(i)||o.set(i,n)}),o}const E=()=>{f.logout()},q=async()=>{try{await f.getUserInfo()}catch{}if(f.isPaw===0){u.replace("/change-password");return}u.replace(L.INDEX)},b=async o=>{_.value=!0;try{await G({code:o}),window.history.replaceState({},"",window.location.pathname),await q()}catch(e){console.error(e),p.error((e==null?void 0:e.msg)||(e==null?void 0:e.message)||"绑定失败"),_.value=!1}};let s=null;const B=async()=>{if(!m.value.corp_id)return;x.value=!0,window.WwLogin||await new Promise((e,t)=>{const n=document.createElement("script");n.src="https://wwcdn.weixin.qq.com/node/wework/wwopen/js/wwLogin-1.2.7.js",n.onload=()=>e(),n.onerror=()=>t(new Error("加载企业微信 JS 失败")),document.head.appendChild(n)}),await C(),x.value=!1,await C();const o=encodeURIComponent(v());new window.WwLogin({id:"wxwork_bind_qrcode_container",appid:m.value.corp_id,agentid:m.value.agent_id,redirect_uri:o,lang:"zh",state:w,href:"",self_redirect:!1})};return j(async()=>{let o="";try{o=(new URLSearchParams(window.location.search).get("code")||"").trim()}catch{o=""}if(!V()){p.error("请先登录"),u.push(L.LOGIN);return}const e=W(),t=o||(e.get("code")||"").trim(),n=e.get("state"),i=n!=null?String(n).trim():"",P=i===w||i==="admin_bind_wx"||i.toLowerCase()===w.toLowerCase(),R=e.get("bind_wxwork")==="1",N=!!t&&i===""&&u.currentRoute.value.path==="/bind-work-wechat";if(t&&(P||R||N)){if(sessionStorage.getItem(g)===t)return;sessionStorage.setItem(g,t);try{await b(t)}finally{sessionStorage.removeItem(g)}return}try{const a=await X();if(!(a!=null&&a.enabled)||!a.corp_id){p.error("企业微信未配置,请联系管理员");return}if(m.value={corp_id:a.corp_id,agent_id:a.agent_id},U()){_.value=!0;const h=encodeURIComponent(v()),O=`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${a.corp_id}&redirect_uri=${h}&response_type=code&scope=snsapi_privateinfo&agentid=${a.agent_id}&state=${w}#wechat_redirect`;window.location.href=O;return}s==null||s(),s=J({pathIncludes:"bind-work-wechat",lockKey:g,wxBindState:w,onCode:h=>b(h)}),await B()}catch(a){console.error(a),p.error("获取企业微信配置失败")}}),z(()=>{s==null||s(),s=null}),(o,e)=>{const t=A,n=F;return d(),l("div",Y,[r("div",Z,[r("div",ee,[e[4]||(e[4]=r("div",{class:"text-center text-2xl font-medium mb-2"},"绑定企业微信",-1)),e[5]||(e[5]=r("div",{class:"text-center text-gray-500 text-sm mb-8"}," 根据安全策略,需绑定企业微信账号后方可使用管理后台 ",-1)),_.value?(d(),l("div",te,[c(t,{class:"is-loading mb-4",size:40,color:"var(--el-color-primary)"},{default:y(()=>[c(S(I))]),_:1}),e[0]||(e[0]=r("div",{class:"text-gray-500"},"企业微信授权中...",-1))])):(d(),l(D,{key:1},[x.value?(d(),l("div",oe,[c(t,{class:"is-loading",size:32,color:"var(--el-color-primary)"},{default:y(()=>[c(S(I))]),_:1}),e[1]||(e[1]=r("div",{class:"mt-2 text-gray-400 text-sm"},"加载扫码...",-1))])):(d(),l("div",ne)),e[2]||(e[2]=r("div",{class:"text-center text-sm text-gray-400 mt-4"}," 请使用企业微信扫描二维码完成绑定 ",-1))],64)),r("div",ae,[c(n,{onClick:E},{default:y(()=>[...e[3]||(e[3]=[M("退出登录",-1)])]),_:1})])])]),c(K)])}}}),ue=H(se,[["__scopeId","data-v-d4a61522"]]);export{ue as default}; import{o as T,W as $,ap as j,E as p,aq as z,q as d,r as l,s as r,v as c,D as y,u as S,b5 as I,w as A,F as D,L as M,K as F,M as k,n as C}from"./.pnpm-Dwh6tNxD.js";import{_ as K}from"./footer.vue_vue_type_script_setup_true_lang-DlOTnYIa.js";import{u as Q,g as V,P as L,a as X,b as G,_ as H}from"./index-C0Q8AuuZ.js";import{a as J}from"./wecomOauthPostMessage-C_e6VGrO.js";const Y={class:"bind-wx flex flex-col"},Z={class:"flex-1 flex items-center justify-center"},ee={class:"bind-wx-card bg-body rounded-md px-10 py-10 w-[480px]"},te={key:0,class:"text-center py-10"},oe={key:0,class:"text-center py-10"},ne={key:1,id:"wxwork_bind_qrcode_container",class:"wxwork-qrcode mx-auto"},ae={class:"mt-8 flex justify-center gap-4"},w="bind_wxwork",g="like_admin_wx_bind_oauth",se=T({__name:"bind-work-wechat",setup(re){const u=$(),f=Q(),x=k(!1),_=k(!1),m=k({corp_id:"",agent_id:""}),U=()=>/wxwork/i.test(navigator.userAgent),v=()=>window.location.origin+window.location.pathname+"?bind_wxwork=1";function W(){const o=new URLSearchParams(window.location.search),e=window.location.hash||"",t=e.includes("?")?e.split("?").slice(1).join("?"):"";return t&&new URLSearchParams(t).forEach((n,i)=>{o.has(i)||o.set(i,n)}),o}const E=()=>{f.logout()},q=async()=>{try{await f.getUserInfo()}catch{}if(f.isPaw===0){u.replace("/change-password");return}u.replace(L.INDEX)},b=async o=>{_.value=!0;try{await G({code:o}),window.history.replaceState({},"",window.location.pathname),await q()}catch(e){console.error(e),p.error((e==null?void 0:e.msg)||(e==null?void 0:e.message)||"绑定失败"),_.value=!1}};let s=null;const B=async()=>{if(!m.value.corp_id)return;x.value=!0,window.WwLogin||await new Promise((e,t)=>{const n=document.createElement("script");n.src="https://wwcdn.weixin.qq.com/node/wework/wwopen/js/wwLogin-1.2.7.js",n.onload=()=>e(),n.onerror=()=>t(new Error("加载企业微信 JS 失败")),document.head.appendChild(n)}),await C(),x.value=!1,await C();const o=encodeURIComponent(v());new window.WwLogin({id:"wxwork_bind_qrcode_container",appid:m.value.corp_id,agentid:m.value.agent_id,redirect_uri:o,lang:"zh",state:w,href:"",self_redirect:!1})};return j(async()=>{let o="";try{o=(new URLSearchParams(window.location.search).get("code")||"").trim()}catch{o=""}if(!V()){p.error("请先登录"),u.push(L.LOGIN);return}const e=W(),t=o||(e.get("code")||"").trim(),n=e.get("state"),i=n!=null?String(n).trim():"",P=i===w||i==="admin_bind_wx"||i.toLowerCase()===w.toLowerCase(),R=e.get("bind_wxwork")==="1",N=!!t&&i===""&&u.currentRoute.value.path==="/bind-work-wechat";if(t&&(P||R||N)){if(sessionStorage.getItem(g)===t)return;sessionStorage.setItem(g,t);try{await b(t)}finally{sessionStorage.removeItem(g)}return}try{const a=await X();if(!(a!=null&&a.enabled)||!a.corp_id){p.error("企业微信未配置,请联系管理员");return}if(m.value={corp_id:a.corp_id,agent_id:a.agent_id},U()){_.value=!0;const h=encodeURIComponent(v()),O=`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${a.corp_id}&redirect_uri=${h}&response_type=code&scope=snsapi_privateinfo&agentid=${a.agent_id}&state=${w}#wechat_redirect`;window.location.href=O;return}s==null||s(),s=J({pathIncludes:"bind-work-wechat",lockKey:g,wxBindState:w,onCode:h=>b(h)}),await B()}catch(a){console.error(a),p.error("获取企业微信配置失败")}}),z(()=>{s==null||s(),s=null}),(o,e)=>{const t=A,n=F;return d(),l("div",Y,[r("div",Z,[r("div",ee,[e[4]||(e[4]=r("div",{class:"text-center text-2xl font-medium mb-2"},"绑定企业微信",-1)),e[5]||(e[5]=r("div",{class:"text-center text-gray-500 text-sm mb-8"}," 根据安全策略,需绑定企业微信账号后方可使用管理后台 ",-1)),_.value?(d(),l("div",te,[c(t,{class:"is-loading mb-4",size:40,color:"var(--el-color-primary)"},{default:y(()=>[c(S(I))]),_:1}),e[0]||(e[0]=r("div",{class:"text-gray-500"},"企业微信授权中...",-1))])):(d(),l(D,{key:1},[x.value?(d(),l("div",oe,[c(t,{class:"is-loading",size:32,color:"var(--el-color-primary)"},{default:y(()=>[c(S(I))]),_:1}),e[1]||(e[1]=r("div",{class:"mt-2 text-gray-400 text-sm"},"加载扫码...",-1))])):(d(),l("div",ne)),e[2]||(e[2]=r("div",{class:"text-center text-sm text-gray-400 mt-4"}," 请使用企业微信扫描二维码完成绑定 ",-1))],64)),r("div",ae,[c(n,{onClick:E},{default:y(()=>[...e[3]||(e[3]=[M("退出登录",-1)])]),_:1})])])]),c(K)])}}}),ue=H(se,[["__scopeId","data-v-d4a61522"]]);export{ue as default};

Some files were not shown because too many files have changed in this diff Show More