Compare commits

...
8 Commits
Author SHA1 Message Date
gr 5e22d423d4 更新 2026-08-28 14:07:30 +08:00
gr 7f1ed49cc8 更新 2026-08-28 14:06:56 +08:00
Your Name b5b14516a1 更新 2026-08-25 15:05:49 +08:00
Your Name 381fe65367 更新 2026-08-25 14:53:17 +08:00
Your Name af603a4e9a 更新 2026-08-25 14:16:31 +08:00
Your Name f24afa116f 更新 2026-08-25 11:40:58 +08:00
Your Name b8ccbaf567 更新 2026-08-25 10:45:24 +08:00
Your Name af1db59c07 更新 2026-08-25 09:41:26 +08:00
377 changed files with 2781 additions and 924 deletions
+47 -3
View File
@@ -154,6 +154,50 @@ export function firstVisitConversionOverview(params: FirstVisitConversionParams)
)
}
export interface FirstVisitConversionFansDetailParams extends FirstVisitConversionParams {
entity_type: 'dept' | 'member'
entity_id: string | number
admin_id?: number
page_no: number
page_size: number
}
export interface FirstVisitConversionFanDetailRow {
external_userid: string
customer_name: string
wecom_userid: string
wecom_staff_name: string
add_time: string | null
is_deleted: boolean
delete_time: string | null
}
export interface FirstVisitConversionFansDetailResult {
lists?: FirstVisitConversionFanDetailRow[]
rows?: FirstVisitConversionFanDetailRow[]
total?: number
count?: number
page_no?: number
page_size?: number
entity?: {
type?: 'dept' | 'member'
id?: string | number
admin_id?: number
name?: string
add_fans_count?: number
deleted_fans_count?: number
}
date_range?: [string, string]
}
/** 一诊加粉明细:时间、渠道及行实体均由调用方显式透传,服务端仍需按 DataScope 收窄。 */
export function firstVisitConversionFansDetail(params: FirstVisitConversionFansDetailParams) {
return request.get(
{ url: '/firstvisit.conversion/fansDetail', params, timeout: 120000 },
{ ignoreCancelToken: true }
)
}
export interface FirstVisitRegistrationStatsParams {
time_type: 'today' | 'yesterday' | 'week' | 'month'
dept_id?: number
@@ -198,9 +242,9 @@ export function wecomPromotionSaveWidget(params: Record<string, unknown>) {
return request.post({ url: '/firstvisit.wecomPromotion/saveWidget', params })
}
export function wecomPromotionDeletePool(params: { id: number }) {
return request.post({ url: '/firstvisit.wecomPromotion/deletePool', params })
}
export function wecomPromotionDeletePool(params: { id: number }) {
return request.post({ url: '/firstvisit.wecomPromotion/deletePool', params, timeout: 120000 })
}
export function wecomPromotionSaveLink(params: Record<string, unknown>) {
return request.post({ url: '/firstvisit.wecomPromotion/saveLink', params })
@@ -189,7 +189,13 @@
</el-table-column>
<el-table-column prop="add_fans_count" label="加粉" min-width="92" align="right">
<template #default="{ row }">
<span class="fan-count-value">
<button
v-if="hasFans(row.add_fans_count)"
type="button"
class="fan-count-value fan-detail-trigger fan-detail-trigger--table"
title="查看该行加粉客户明细"
@click.stop="openFansDetail(row)"
>
{{ formatNumber(row.add_fans_count) }}
<em
v-if="hasDeletedFans(row.deleted_fans_count)"
@@ -198,7 +204,8 @@
>
-{{ formatNumber(row.deleted_fans_count) }}
</em>
</span>
</button>
<span v-else class="fan-count-value is-empty">0</span>
</template>
</el-table-column>
<el-table-column prop="total_open_count" label="开口" min-width="72" align="right" />
@@ -292,6 +299,75 @@
</div>
</div>
</section>
<el-dialog
v-model="fansDetailVisible"
:title="fansDetailTitle"
width="920px"
top="7vh"
append-to-body
destroy-on-close
class="fans-detail-dialog"
@closed="resetFansDetail"
>
<div class="fans-detail-context">
<span><b>统计区间</b>{{ detailRangeText }}</span>
<span><b>媒体渠道</b>{{ detailChannelText }}</span>
<span><b>数据范围</b>{{ dashboard.meta.scope_label || '当前权限范围' }}</span>
</div>
<el-table
v-loading="fansDetailLoading"
:data="fansDetailRows"
:row-class-name="fansDetailRowClassName"
height="430"
class="fans-detail-table"
>
<el-table-column label="客户" min-width="190">
<template #default="{ row }">
<div class="customer-cell">
<strong class="customer-name" :class="{ 'is-deleted': isDeletedFan(row) }">
{{ fanCustomerName(row) }}
</strong>
<small v-if="row.customer_name && row.external_userid">{{ row.external_userid }}</small>
</div>
</template>
</el-table-column>
<el-table-column label="归属员工" min-width="160">
<template #default="{ row }">{{ fanOwnerName(row) }}</template>
</el-table-column>
<el-table-column label="添加时间" min-width="180">
<template #default="{ row }">{{ fanAddTime(row) }}</template>
</el-table-column>
<el-table-column label="状态" width="150" align="center">
<template #default="{ row }">
<div v-if="isDeletedFan(row)" class="deleted-status">
<el-tag type="danger" effect="dark" size="small">已删除</el-tag>
<small v-if="row.delete_time">{{ row.delete_time }}</small>
</div>
<el-tag v-else type="success" effect="plain" size="small">正常</el-tag>
</template>
</el-table-column>
<template #empty>
<el-empty :image-size="68" description="当前条件下暂无加粉客户明细" />
</template>
</el-table>
<div class="fans-detail-footer">
<span> {{ formatNumber(fansDetailPager.total) }} 位客户</span>
<el-pagination
v-if="fansDetailPager.total > 0"
v-model:current-page="fansDetailPager.page_no"
v-model:page-size="fansDetailPager.page_size"
:page-sizes="[10, 20, 50, 100]"
:total="fansDetailPager.total"
layout="total, sizes, prev, pager, next"
background
@current-change="loadFansDetail"
@size-change="handleFansDetailSizeChange"
/>
</div>
</el-dialog>
</div>
</template>
@@ -300,7 +376,14 @@ import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Lock, Refresh } from '@element-plus/icons-vue'
import vCharts from 'vue-echarts'
import { firstVisitConversionOverview, type FirstVisitConversionParams } from '@/api/first_visit'
import {
firstVisitConversionFansDetail,
firstVisitConversionOverview,
type FirstVisitConversionFanDetailRow,
type FirstVisitConversionFansDetailParams,
type FirstVisitConversionFansDetailResult,
type FirstVisitConversionParams
} from '@/api/first_visit'
type MetricType = 'count' | 'money' | 'ratio'
type MediaChannelOption = {
@@ -346,6 +429,13 @@ const query = reactive<FirstVisitConversionParams>({
media_channel_code: ''
})
const customDateRange = ref<string[]>([])
const fansDetailVisible = ref(false)
const fansDetailLoading = ref(false)
const fansDetailRows = ref<FirstVisitConversionFanDetailRow[]>([])
const fansDetailPager = reactive({ page_no: 1, page_size: 20, total: 0 })
const fansDetailEntity = ref<{ type: 'dept' | 'member'; id: string | number; adminId?: number; name: string }>({
type: 'dept', id: 0, name: '当前行'
})
const deptTreeProps = { value: 'id', label: 'name', children: 'children' }
const timeOptions = [
{ label: '今日', value: 'today' },
@@ -424,6 +514,13 @@ const rankingKind = computed(() => dashboard.meta.ranking_kind || (
))
const showRankings = computed(() => rankingKind.value !== 'hidden')
const rankingSubject = computed(() => rankingKind.value === 'member' ? '组内成员' : '小组')
const fansDetailTitle = computed(() => `${fansDetailEntity.value.name} · 加粉客户明细`)
const detailRangeText = computed(() => {
const startDate = dashboard.meta.start_date || query.start_date || ''
const endDate = dashboard.meta.end_date || query.end_date || ''
return startDate && endDate ? `${startDate}${endDate}` : (dashboard.meta.time_label || '当前区间')
})
const detailChannelText = computed(() => dashboard.meta.selected_media_channel_name || '全部渠道')
const totalOrderValue = computed(() => dashboard.rankings.orders.reduce((total, item) => total + Number(item.value || 0), 0))
const totalAmountValue = computed(() => dashboard.rankings.amounts.reduce((total, item) => total + Number(item.value || 0), 0))
const targetChartOption = computed(() => ({
@@ -440,6 +537,7 @@ const targetChartOption = computed(() => ({
}))
let latestDashboardRequestId = 0
let latestFansDetailRequestId = 0
async function loadDashboard() {
const requestId = ++latestDashboardRequestId
@@ -492,6 +590,89 @@ function handleDeptChange() {
loadDashboard()
}
function openFansDetail(row: any) {
fansDetailEntity.value = {
type: row.type ? 'member' : 'dept',
id: row.id,
adminId: Number(row.admin_id || 0) || undefined,
name: row.name || '当前行'
}
fansDetailPager.page_no = 1
fansDetailRows.value = []
fansDetailPager.total = 0
fansDetailVisible.value = true
loadFansDetail()
}
async function loadFansDetail() {
if (!fansDetailVisible.value) return
const requestId = ++latestFansDetailRequestId
fansDetailLoading.value = true
try {
const params: FirstVisitConversionFansDetailParams = {
...query,
time_type: query.time_type,
start_date: dashboard.meta.start_date || query.start_date,
end_date: dashboard.meta.end_date || query.end_date,
media_channel_code: dashboard.meta.selected_media_channel_code || query.media_channel_code || '',
entity_type: fansDetailEntity.value.type,
entity_id: fansDetailEntity.value.id,
admin_id: fansDetailEntity.value.adminId,
page_no: fansDetailPager.page_no,
page_size: fansDetailPager.page_size
}
const result = await firstVisitConversionFansDetail(params) as FirstVisitConversionFansDetailResult
if (requestId !== latestFansDetailRequestId) return
const rows = Array.isArray(result?.lists) ? result.lists : (Array.isArray(result?.rows) ? result.rows : [])
fansDetailRows.value = rows
fansDetailPager.total = Number(result?.count ?? result?.total ?? rows.length) || 0
} catch (error: any) {
if (requestId !== latestFansDetailRequestId) return
fansDetailRows.value = []
fansDetailPager.total = 0
ElMessage.error(error?.message || '加粉客户明细加载失败')
} finally {
if (requestId === latestFansDetailRequestId) fansDetailLoading.value = false
}
}
function handleFansDetailSizeChange() {
fansDetailPager.page_no = 1
loadFansDetail()
}
function resetFansDetail() {
++latestFansDetailRequestId
fansDetailLoading.value = false
fansDetailRows.value = []
fansDetailPager.page_no = 1
fansDetailPager.total = 0
}
function hasFans(value: any) {
return Math.round(Number(value || 0)) > 0
}
function isDeletedFan(row: FirstVisitConversionFanDetailRow) {
return Boolean(row.is_deleted)
}
function fanCustomerName(row: FirstVisitConversionFanDetailRow) {
return row.customer_name || row.external_userid || '未知客户'
}
function fanOwnerName(row: FirstVisitConversionFanDetailRow) {
return row.wecom_staff_name || row.wecom_userid || '未绑定员工'
}
function fanAddTime(row: FirstVisitConversionFanDetailRow) {
return row.add_time || '-'
}
function fansDetailRowClassName({ row }: { row: FirstVisitConversionFanDetailRow }) {
return isDeletedFan(row) ? 'is-deleted-fan-row' : ''
}
function formatMetric(key: string, type: MetricType) {
const value = dashboard.summary[key]
if (type === 'money') return formatMoney(value)
@@ -658,6 +839,18 @@ onMounted(loadDashboard)
}
.metric-value { display: flex; justify-content: flex-start; }
.fan-detail-trigger {
margin: 0;
padding: 0;
border: 0;
color: inherit;
background: transparent;
font: inherit;
cursor: pointer;
&:focus-visible { outline: 2px solid #2f78df; outline-offset: 3px; border-radius: 3px; }
&:disabled { cursor: default; }
}
.deleted-fans-marker {
color: #c65f26;
font-size: .58em;
@@ -666,6 +859,9 @@ onMounted(loadDashboard)
}
.fan-count-value .deleted-fans-marker { font-size: 11px; }
.fan-detail-trigger--table { color: #1769aa; font-weight: 650; }
.fan-detail-trigger--table:hover { color: #0f4f82; }
.fan-count-value.is-empty { color: #98a2b3; }
.ranking-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.panel { min-width: 0; max-width: 100%; padding: 16px; border-radius: 10px; box-sizing: border-box; }
@@ -729,6 +925,47 @@ onMounted(loadDashboard)
.legend-line.is-target { border-color: #2f78df; border-top-style: dashed; }
.target-chart { width: 100%; height: 260px; }
.fans-detail-context {
display: flex;
flex-wrap: wrap;
gap: 8px 20px;
margin: -4px 0 14px;
padding: 10px 12px;
border: 1px solid #e4e9ef;
border-radius: 7px;
color: #5d6b7e;
background: #f7f9fb;
font-size: 12px;
span { display: inline-flex; gap: 7px; }
b { color: #8a95a5; font-weight: 500; }
}
.fans-detail-table {
width: 100%;
.customer-cell { display: grid; gap: 3px; }
.customer-name { color: #273347; font-weight: 650; }
.customer-name.is-deleted { color: #c24137; text-decoration: line-through; text-decoration-color: #e8a39e; }
.customer-cell small { overflow: hidden; color: #98a2b3; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
:deep(.is-deleted-fan-row > td.el-table__cell) { background: #fff3f2 !important; }
}
.deleted-status {
display: inline-grid;
justify-items: center;
gap: 4px;
small { color: #b14e47; font-size: 10px; }
}
.fans-detail-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-top: 14px;
color: #8491a2;
font-size: 12px;
}
@media (max-width: 1180px) {
.filter-strip { align-items: flex-start; flex-wrap: wrap; }
.range-text { margin-left: 0; }
@@ -744,5 +981,7 @@ onMounted(loadDashboard)
.employee-select, .dept-select, .channel-select { width: 100%; }
.bar-row { grid-template-columns: 100px minmax(70px, 1fr) 96px; }
.target-summary { grid-template-columns: 1fr; }
.fans-detail-footer { align-items: flex-start; flex-direction: column; }
.fans-detail-footer :deep(.el-pagination) { max-width: 100%; flex-wrap: wrap; justify-content: flex-start; }
}
</style>
@@ -5,7 +5,7 @@
<span class="heading-icon"><el-icon><Promotion /></el-icon></span>
<div>
<h1>企业微信获客助手</h1>
<p>一个方案保留一条企业微信官方直链按回调结果自动调度医助权重与数量</p>
<p>一个方案保留一条企业微信官方直链由企业微信在多个可用医助之间原生路由</p>
</div>
</div>
<div class="heading-actions">
@@ -26,7 +26,7 @@
</article>
<article class="metric-card">
<span class="metric-icon is-green"><el-icon><Link /></el-icon></span>
<div><small>官方链接</small><strong>{{ overview.summary.online_links }}</strong><p>链接不变成员范围自动切换</p></div>
<div><small>官方链接</small><strong>{{ overview.summary.online_links }}</strong><p>链接不变成员范围自动更新</p></div>
</article>
<article class="metric-card">
<span class="metric-icon is-orange"><el-icon><Mouse /></el-icon></span>
@@ -91,7 +91,7 @@
<el-button :icon="DocumentCopy" :disabled="!selectedPool.main_url" @click="copyText(selectedPool.main_url, '官方获客链接')">复制链接</el-button>
<el-button :icon="DocumentCopy" @click="copyText(selectedPool.install_code, 'JS 安装代码')">复制 JS</el-button>
<el-button :icon="Edit" @click="openPoolDialog(selectedPool)">编辑方案</el-button>
<el-button type="danger" plain :icon="Delete" @click="removePool(selectedPool)">删除</el-button>
<el-button type="danger" plain :icon="Delete" :loading="deletingPoolId === Number(selectedPool.id)" :disabled="deletingPoolId > 0" @click="removePool(selectedPool)">删除</el-button>
</div>
</div>
@@ -110,8 +110,8 @@
type="info"
show-icon
:closable="false"
title="回调驱动分流"
description="企业微信确认实际承接成员后,系统累计今日数量,并在符合启用状态、有效期和上限的成员中按权重随机抽取下一位;随机结果可能连续命中同一人,并发访问及企微同步存在短暂延迟。"
title="企业微信原生多人路由"
description="当前全部可用医助会同时写入官方链接的成员范围,由企业微信在打开和添加阶段直接进行多人路由。回调只用于统计实际承接结果,并在禁用、过期或达到上限后更新成员范围。"
/>
<el-table :data="selectedMemberRules" class="link-table" stripe>
@@ -125,12 +125,12 @@
</el-table-column>
<el-table-column label="调度" width="105" align="center">
<template #default="{ row }">
<span v-if="row.is_current" class="status-tag is-online">{{ Number(row.sync_status) === 0 ? '当前命中' : '待切换' }}</span>
<span v-else-if="row.is_applied" class="status-tag is-offline">切换中</span>
<span v-else class="muted">候选</span>
<span v-if="row.is_in_remote_range && eligibility(row).className === 'is-ok'" class="status-tag is-online">企微路由中</span>
<span v-else-if="row.is_in_remote_range" class="status-tag is-offline">待移出</span>
<span v-else-if="eligibility(row).className === 'is-ok'" class="status-tag is-offline">待同步</span>
<span v-else class="muted">不参与</span>
</template>
</el-table-column>
<el-table-column label="权重" prop="weight" width="85" align="center" />
<el-table-column label="今日 / 上限" width="125" align="center">
<template #default="{ row }">{{ todayCount(row) }} / {{ Number(row.daily_limit) ? row.daily_limit : '不限' }}</template>
</el-table-column>
@@ -197,7 +197,7 @@
show-icon
:closable="false"
title="消息次数回调尚未配置完成"
description="复用企业微信自建应用现有的“API 接收消息”地址即可,无需新增第二个回调;请确保该地址为 /api/qywx/external-contact/notify,且服务器 Token、AESKey 与企微后台一致。未完成前无法自动累计成员获客数量和切换下一位。"
description="复用企业微信自建应用现有的“API 接收消息”地址即可,无需新增第二个回调;请确保该地址为 /api/qywx/external-contact/notify,且服务器 Token、AESKey 与企微后台一致。未完成前无法自动累计成员获客数量,也无法在成员达到上限后自动更新官方范围。"
/>
<el-alert
@@ -351,19 +351,35 @@
<el-form label-position="top">
<el-form-item label="方案名称" required><el-input v-model="poolForm.name" :disabled="savingPool" maxlength="60" show-word-limit placeholder="例如:官网咨询分流" /></el-form-item>
<el-form-item label="获客医助" required>
<el-select
<el-tree-select
v-model="poolForm.member_admin_ids"
:data="memberDepartmentTree"
:props="memberTreeProps"
:default-expanded-keys="memberTreeDefaultExpandedKeys"
:filter-node-method="filterMemberTreeNode"
:disabled="savingPool"
node-key="value"
multiple
filterable
clearable
show-checkbox
collapse-tags
collapse-tags-tooltip
placeholder="选择后由企业微信生成一个多人分流链接"
:max-collapse-tags="2"
:multiple-limit="500"
:render-after-expand="false"
placeholder="按部门展开,或搜索姓名 / 部门 / 企微 userid"
style="width: 100%"
>
<el-option v-for="member in overview.member_options" :key="member.id" :label="memberOptionLabel(member)" :value="Number(member.id)" />
</el-select>
<span class="form-tip">保存后会为每名医助生成一条成员规则官方链接地址始终只有一个</span>
<template #default="{ data }">
<span class="member-tree-option" :class="`is-${data.kind}`">
<span class="member-tree-option__label">{{ data.label }}</span>
<span v-if="data.kind === 'department'" class="member-tree-option__count">{{ data.member_count }} </span>
<span v-else class="member-tree-option__detail">{{ data.detail }}</span>
</span>
</template>
</el-tree-select>
<span class="form-tip">已选 {{ poolForm.member_admin_ids.length }} 保存后会为每名医助生成一条成员规则官方链接地址始终只有一个</span>
</el-form-item>
<el-form-item label="添加客户时跳过验证">
<el-switch v-model="poolForm.skip_verify" :disabled="savingPool" :active-value="1" :inactive-value="0" active-text="跳过验证" inactive-text="需要验证" />
@@ -382,16 +398,10 @@
<el-form-item label="获客医助">
<el-input :model-value="memberForm.name ? `${memberForm.name} · ${memberForm.userid}` : memberForm.userid" readonly />
</el-form-item>
<div class="rule-form-grid">
<el-form-item label="权重" required>
<el-input-number v-model="memberForm.weight" :min="1" :max="100" :disabled="savingMember" style="width: 100%" />
<span class="form-tip">权重越高在后续回调调度中被选中的频率越高</span>
</el-form-item>
<el-form-item label="每日获客上限">
<el-input-number v-model="memberForm.daily_limit" :min="0" :max="1000000" :disabled="savingMember" style="width: 100%" />
<span class="form-tip">0 表示不限数量按实际获客回调累计</span>
</el-form-item>
</div>
<el-form-item label="每日获客上限">
<el-input-number v-model="memberForm.daily_limit" :min="0" :max="1000000" :disabled="savingMember" style="width: 100%" />
<span class="form-tip">0 表示不限数量按实际获客回调累计达到上限后会从企微官方成员范围移除</span>
</el-form-item>
<el-form-item label="有效时间">
<el-date-picker v-model="memberForm.active_range" type="datetimerange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" :disabled="savingMember" style="width: 100%" />
</el-form-item>
@@ -427,13 +437,44 @@ import type { WecomPromotionCustomerChatStatus } from '@/api/first_visit'
import WecomFloatingWidgetBuilder from './components/WecomFloatingWidgetBuilder.vue'
type TabName = 'links' | 'customer-stats' | 'configuration' | 'install'
const emptyOverview = () => ({
meta: { scope_label: '', generated_at: '' },
config: { mode: 'internal', configured: false, ready: false, missing: [] as string[], corp_id_masked: '', agent_id: '', secret_configured: false, callback_ready: false, callback_url: '', official_doc: '' },
summary: { configured_apps: 0, pool_count: 0, online_links: 0, today_clicks: 0 },
pools: [] as any[], links: [] as any[], member_options: [] as any[], customer_acquisition_link_example: 'https://work.weixin.qq.com/ca/xxxxxxxx'
})
type TabName = 'links' | 'customer-stats' | 'configuration' | 'install'
interface PromotionDepartmentOption {
id: number | string
name: string
children?: PromotionDepartmentOption[]
}
interface PromotionMemberOption {
id: number
name: string
userid: string
display_dept_id?: number
dept_ids: number[]
dept_names: string[]
}
interface PromotionMemberTreeNode {
value: number | string
label: string
kind: 'department' | 'member'
disabled?: boolean
member_count?: number
detail?: string
search_text: string
children?: PromotionMemberTreeNode[]
}
const emptyOverview = () => ({
meta: { scope_label: '', generated_at: '' },
config: { mode: 'internal', configured: false, ready: false, missing: [] as string[], corp_id_masked: '', agent_id: '', secret_configured: false, callback_ready: false, callback_url: '', official_doc: '' },
summary: { configured_apps: 0, pool_count: 0, online_links: 0, today_clicks: 0 },
pools: [] as any[],
links: [] as any[],
member_options: [] as PromotionMemberOption[],
department_options: [] as PromotionDepartmentOption[],
customer_acquisition_link_example: 'https://work.weixin.qq.com/ca/xxxxxxxx'
})
const overview = reactive(emptyOverview())
const loading = ref(false)
@@ -446,8 +487,9 @@ const checkingApi = ref(false)
const memberDialogVisible = ref(false)
const savingMember = ref(false)
const togglingMemberId = ref(0)
const deletingPoolId = ref(0)
const poolForm = reactive({ id: 0, name: '', fallback_url: '', status: 1, member_admin_ids: [] as number[], skip_verify: 0, main_url: '' })
const memberForm = reactive({ id: 0, name: '', userid: '', weight: 1, daily_limit: 0, status: 1, active_range: [] as string[], remark: '' })
const memberForm = reactive({ id: 0, name: '', userid: '', daily_limit: 0, status: 1, active_range: [] as string[], remark: '' })
const customerStatsLoading = ref(false)
const customerStatsLoaded = ref(false)
const syncingCustomers = ref(false)
@@ -467,8 +509,11 @@ const customerStats = reactive({
const selectedPool = computed(() => overview.pools.find((item: any) => Number(item.id) === selectedPoolId.value))
const selectedMemberRules = computed(() => Array.isArray(selectedPool.value?.member_rules) ? selectedPool.value.member_rules : [])
const selectedInstallPool = computed(() => overview.pools.find((item: any) => Number(item.id) === selectedInstallPoolId.value))
const customerLinkOptions = computed(() => customerStats.link_options.length
const selectedInstallPool = computed(() => overview.pools.find((item: any) => Number(item.id) === selectedInstallPoolId.value))
const memberTreeProps = { value: 'value', label: 'label', children: 'children', disabled: 'disabled' }
const memberDepartmentTree = computed(() => buildMemberDepartmentTree(overview.department_options, overview.member_options))
const memberTreeDefaultExpandedKeys = computed(() => memberDepartmentTree.value.map((node) => node.value))
const customerLinkOptions = computed(() => customerStats.link_options.length
? customerStats.link_options
: overview.links
.filter((item: any) => item.id)
@@ -505,12 +550,15 @@ async function loadOverview() {
}
function openPoolDialog(pool?: any) {
const selectableMemberIds = new Set(overview.member_options.map((member) => Number(member.id)))
Object.assign(poolForm, pool ? {
id: Number(pool.id),
name: pool.name,
fallback_url: pool.fallback_url || '',
status: Number(pool.status),
member_admin_ids: Array.isArray(pool.member_admin_ids) ? pool.member_admin_ids.map(Number) : [],
member_admin_ids: Array.isArray(pool.member_admin_ids)
? pool.member_admin_ids.map(Number).filter((memberId: number) => selectableMemberIds.has(memberId))
: [],
skip_verify: Number(pool.skip_verify) === 1 ? 1 : 0,
main_url: pool.main_url || ''
} : {
@@ -534,7 +582,9 @@ async function savePool() {
poolDialogVisible.value = false
await loadOverview()
if (result?.id) selectedPoolId.value = Number(result.id)
ElMessage.success(poolForm.id ? '分流方案已保存' : '分流方案和官方获客链接已创建')
result?.sync_error
? ElMessage.warning('方案已保存,企业微信多人范围将在后台自动重试同步')
: ElMessage.success(poolForm.id ? '分流方案已保存' : '分流方案和官方获客链接已创建')
} catch (error: any) {
ElMessage.error(error?.message || '分流方案保存失败')
} finally {
@@ -543,14 +593,25 @@ async function savePool() {
}
async function removePool(pool: any) {
await ElMessageBox.confirm(`删除“${pool.name}”后,本站将停止使用该链接,但不会永久删除企业微信后台中的官方链接。确认继续?`, '删除分流方案', { type: 'warning' })
try {
await wecomPromotionDeletePool({ id: Number(pool.id) })
ElMessage.success('分流方案已删除')
await loadOverview()
} catch (error: any) {
ElMessage.error(error?.message || '删除失败')
}
try {
await ElMessageBox.confirm(
`删除“${pool.name}”将同时永久删除企业微信后台中的官方获客链接,已投放的链接会失效且无法恢复。确认继续?`,
'永久删除分流方案',
{ type: 'warning', confirmButtonText: '永久删除', cancelButtonText: '取消' }
)
} catch {
return
}
deletingPoolId.value = Number(pool.id)
try {
await wecomPromotionDeletePool({ id: Number(pool.id) })
ElMessage.success('分流方案及企业微信官方获客链接已永久删除')
await loadOverview()
} catch (error: any) {
ElMessage.error(error?.message || '删除失败')
} finally {
deletingPoolId.value = 0
}
}
function openMemberDialog(row: any) {
@@ -561,7 +622,6 @@ function openMemberDialog(row: any) {
id: Number(row.id),
name: row.name || '',
userid: row.userid || '',
weight: Number(row.weight) || 1,
daily_limit: Number(row.daily_limit) || 0,
status: Number(row.enabled) === 1 ? 1 : 0,
active_range: range,
@@ -597,7 +657,7 @@ async function handleMemberToggle(row: any, value: unknown) {
await loadOverview()
result?.sync_error
? ElMessage.warning('成员状态已保存,企业微信成员范围将在后台自动重试同步')
: ElMessage.success(value ? '医助已启用' : '医助已禁用并退出后续调度')
: ElMessage.success('成员规则已保存,企微多人路由范围已重新计算')
} catch (error: any) {
ElMessage.error(error?.message || '成员状态更新失败')
await loadOverview()
@@ -727,22 +787,127 @@ function eligibility(row: any) {
if (Number(row.active_start) > 0 && Number(row.active_start) > now) return { label: '尚未生效', className: 'is-waiting' }
if (Number(row.active_end) > 0 && Number(row.active_end) < now) return { label: '已过期', className: 'is-error' }
if (Number(row.daily_limit) > 0 && todayCount(row) >= Number(row.daily_limit)) return { label: '今日已达上限', className: 'is-waiting' }
const isSyncTarget = Boolean(row.is_current || row.is_applied)
if (isSyncTarget && Number(row.sync_status) === 3) return { label: '企微同步重试中', className: 'is-waiting' }
if (isSyncTarget && Number(row.sync_status) === 4) return { label: '暂无可用调度', className: 'is-error' }
if (Number(row.sync_status) === 3) return { label: '企微同步重试中', className: 'is-waiting' }
if (Number(row.sync_status) === 4) return { label: '暂无可用成员', className: 'is-error' }
return { label: '可参与分流', className: 'is-ok' }
}
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())
return row.today_date === today ? Number(row.today_count || 0) : 0
}
function memberOptionLabel(member: any) {
const departments = Array.isArray(member.dept_names) && member.dept_names.length
? member.dept_names.join(' / ')
: '未分部门'
return `${member.name} · ${departments} · ${member.userid}`
}
function buildMemberDepartmentTree(
departmentOptions: PromotionDepartmentOption[],
memberOptions: PromotionMemberOption[]
): PromotionMemberTreeNode[] {
const availableDeptIds = new Set<number>()
const collectDeptIds = (departments: PromotionDepartmentOption[]) => {
departments.forEach((department) => {
const departmentId = Number(department.id)
if (departmentId > 0) availableDeptIds.add(departmentId)
if (Array.isArray(department.children)) collectDeptIds(department.children)
})
}
collectDeptIds(Array.isArray(departmentOptions) ? departmentOptions : [])
const members: PromotionMemberOption[] = []
const seenMemberIds = new Set<number>()
for (const rawMember of Array.isArray(memberOptions) ? memberOptions : []) {
const memberId = Number(rawMember.id)
if (memberId <= 0 || seenMemberIds.has(memberId)) continue
seenMemberIds.add(memberId)
members.push({
id: memberId,
name: String(rawMember.name || rawMember.userid || `成员 ${memberId}`),
userid: String(rawMember.userid || ''),
display_dept_id: Number(rawMember.display_dept_id || 0),
dept_ids: Array.isArray(rawMember.dept_ids)
? Array.from(new Set(rawMember.dept_ids.map(Number).filter((id) => id > 0)))
: [],
dept_names: Array.isArray(rawMember.dept_names)
? rawMember.dept_names.map(String).filter(Boolean)
: []
})
}
const membersByDept = new Map<number, PromotionMemberOption[]>()
members.forEach((member) => {
const explicitDeptId = Number(member.display_dept_id || 0)
const displayDeptId = availableDeptIds.has(explicitDeptId)
? explicitDeptId
: (member.dept_ids.find((id) => availableDeptIds.has(id)) || 0)
if (!membersByDept.has(displayDeptId)) membersByDept.set(displayDeptId, [])
membersByDept.get(displayDeptId)?.push(member)
})
const placedMemberIds = new Set<number>()
const toMemberNode = (member: PromotionMemberOption): PromotionMemberTreeNode => {
const departments = member.dept_names.length ? member.dept_names.join(' / ') : '未分部门'
const detail = member.userid ? `${departments} · ${member.userid}` : departments
return {
value: member.id,
label: member.name,
kind: 'member',
detail,
search_text: `${member.name} ${member.userid} ${member.dept_names.join(' ')}`.toLocaleLowerCase()
}
}
const byMemberName = (left: PromotionMemberOption, right: PromotionMemberOption) => left.name.localeCompare(right.name, 'zh-CN')
const buildDepartments = (departments: PromotionDepartmentOption[], parentPath: string[]): PromotionMemberTreeNode[] => {
const result: PromotionMemberTreeNode[] = []
for (const department of Array.isArray(departments) ? departments : []) {
const departmentId = Number(department.id)
if (departmentId <= 0) continue
const departmentName = String(department.name || `部门 ${departmentId}`)
const path = [...parentPath, departmentName]
const departmentChildren = buildDepartments(
Array.isArray(department.children) ? department.children : [],
path
)
const directMembers = [...(membersByDept.get(departmentId) || [])].sort(byMemberName)
directMembers.forEach((member) => placedMemberIds.add(member.id))
const memberChildren = directMembers.map(toMemberNode)
const children = [...departmentChildren, ...memberChildren]
if (!children.length) continue
const memberCount = children.reduce(
(count, child) => count + (child.kind === 'member' ? 1 : Number(child.member_count || 0)),
0
)
result.push({
value: `dept:${departmentId}`,
label: departmentName,
kind: 'department',
disabled: true,
member_count: memberCount,
search_text: `${path.join(' ')} ${children.map((child) => child.search_text).join(' ')}`.toLocaleLowerCase(),
children
})
}
return result
}
const tree = buildDepartments(Array.isArray(departmentOptions) ? departmentOptions : [], [])
const unassigned = members.filter((member) => !placedMemberIds.has(member.id)).sort(byMemberName)
if (unassigned.length) {
const children = unassigned.map(toMemberNode)
tree.push({
value: 'dept:unassigned',
label: '未分部门',
kind: 'department',
disabled: true,
member_count: children.length,
search_text: `未分部门 ${children.map((child) => child.search_text).join(' ')}`.toLocaleLowerCase(),
children
})
}
return tree
}
function filterMemberTreeNode(keyword: string, data: any) {
const query = String(keyword || '').trim().toLocaleLowerCase()
return query === '' || data.search_text.includes(query)
}
function memberSummary(pool: any) {
@@ -940,8 +1105,13 @@ h1, h2, h3, p { margin: 0; }
.configuration-state { display: flex; align-items: center; gap: 12px; }.configuration-state > span { display: grid; width: 36px; height: 36px; place-items: center; border-radius: 9px; color: #fff; background: var(--teal); font-size: 18px; }.is-pending .configuration-state > span { background: #e69a43; }.configuration-state strong { font-size: 14px; }.configuration-state p { margin-top: 4px; color: #718094; font-size: 11px; }
.missing-fields { margin-top: 12px; color: #9c651f; font-size: 11px; }.missing-fields code { margin-left: 6px; padding: 3px 6px; border-radius: 4px; background: rgba(224,153,63,.11); }
.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; }
.install-pool-select { width: 220px; }
.configuration-actions { display: flex; align-items: center; gap: 14px; margin-top: 16px; }
.install-pool-select { width: 220px; }
.member-tree-option { display: inline-flex; min-width: 0; flex: 1; align-items: center; gap: 10px; }
.member-tree-option__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member-tree-option.is-department .member-tree-option__label { color: #344054; font-weight: 600; }
.member-tree-option__count { flex: 0 0 auto; color: #98a2b3; font-size: 11px; }
.member-tree-option__detail { overflow: hidden; color: #8b97a6; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
.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: 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; } }
+59 -35
View File
@@ -1775,9 +1775,31 @@ const appointmentCellClasses = (row: any) => {
}
}
/** 仅已预约(1)可进视频/小程序码 */
const isAppointmentActiveForVideo = (row: any) =>
row.has_appointment && Number(row.appointment_status) === 1
/**
* 找到当前可用的已预约挂号。
* 同一诊单当天可能先完成一条挂号、随后又新增一条预约,此时行级 appointment_* 仍可能指向旧记录。
*/
const activeAppointment = (row: any) =>
appointmentRows(row).find((apt: any) => Number(apt?.status) === 1) ?? null
/** 二维码必须使用已预约挂号对应的医生和时间,不能继续沿用行级旧挂号字段。 */
const activeAppointmentRow = (row: any) => {
const apt = activeAppointment(row)
if (!apt) return null
return {
...row,
has_appointment: 1,
appointment_id: apt.id,
appointment_status: apt.status,
appointment_doctor_id: apt.doctor_id,
appointment_doctor_name: apt.doctor_name,
appointment_time_text: apt.time_text
}
}
/** 任一挂号记录处于已预约(1)即可进视频/小程序码。 */
const isAppointmentActiveForVideo = (row: any) => !!activeAppointment(row)
/** 已预约、已过号可取消(后端同步限制),针对行上主字段 */
const canCancelAppointmentRow = (row: any) => {
@@ -1962,17 +1984,18 @@ const submitFillIdCard = async () => {
}
// 生成视频二维码(跳转登录页)- 仅已预约(1)可生成
const handleVideoQRCode = async (row: any) => {
if (!isAppointmentActiveForVideo(row)) {
feedback.msgWarning('仅「已预约」状态可生成视频二维码')
return
}
if (!row.patient_id) {
feedback.msgWarning('患者信息不完整')
return
}
lastQRCodeType.value = 'video'
currentQRCodePatient.value = row
const handleVideoQRCode = async (row: any) => {
const qrcodeRow = activeAppointmentRow(row)
if (!qrcodeRow) {
feedback.msgWarning('仅「已预约」状态可生成视频二维码')
return
}
if (!qrcodeRow.patient_id) {
feedback.msgWarning('患者信息不完整')
return
}
lastQRCodeType.value = 'video'
currentQRCodePatient.value = qrcodeRow
qrcodeDialogVisible.value = true
qrcodeLoading.value = true
qrcodeUrl.value = ''
@@ -1983,11 +2006,11 @@ const handleVideoQRCode = async (row: any) => {
feedback.msgError('小程序未配置,请先配置小程序信息')
qrcodeDialogVisible.value = false
return
}
const result = await generateMiniProgramQrcode({
doctor_id: row.appointment_doctor_id,
diagnosis_id: row.appointment_doctor_id,
patient_id: row.patient_id,
}
const result = await generateMiniProgramQrcode({
doctor_id: qrcodeRow.appointment_doctor_id,
diagnosis_id: qrcodeRow.appointment_doctor_id,
patient_id: qrcodeRow.patient_id,
share_user_id: userStore.userInfo?.id || '',
mini_program_path: 'pages/login/login'
})
@@ -2004,17 +2027,18 @@ const handleVideoQRCode = async (row: any) => {
}
// 生成确认诊单二维码
const handleMiniProgramQRCode = async (row: any) => {
if (!isAppointmentActiveForVideo(row)) {
feedback.msgWarning('仅「已预约」状态可使用诊单二维码')
return
}
if (!row.patient_id) {
feedback.msgWarning('患者信息不完整')
return
}
lastQRCodeType.value = 'confirm'
currentQRCodePatient.value = row
const handleMiniProgramQRCode = async (row: any) => {
const qrcodeRow = activeAppointmentRow(row)
if (!qrcodeRow) {
feedback.msgWarning('仅「已预约」状态可使用诊单二维码')
return
}
if (!qrcodeRow.patient_id) {
feedback.msgWarning('患者信息不完整')
return
}
lastQRCodeType.value = 'confirm'
currentQRCodePatient.value = qrcodeRow
qrcodeDialogVisible.value = true
qrcodeLoading.value = true
qrcodeUrl.value = ''
@@ -2033,11 +2057,11 @@ const handleMiniProgramQRCode = async (row: any) => {
// 获取当前登录用户信息
const currentUser = userStore.userInfo
// 调用生成二维码接口
const result = await generateMiniProgramQrcode({
diagnosis_id: row.id,
doctor_id: row.appointment_doctor_id,
patient_id: row.patient_id,
// 调用生成二维码接口
const result = await generateMiniProgramQrcode({
diagnosis_id: qrcodeRow.id,
doctor_id: qrcodeRow.appointment_doctor_id,
patient_id: qrcodeRow.patient_id,
share_user_id: currentUser?.id || ''
})
+48 -26
View File
@@ -946,8 +946,28 @@ const appointmentRowClass = (row: any) => {
}
}
const isAppointmentActiveForVideo = (row: any) =>
row.has_appointment && Number(row.appointment_status) === 1
/**
* 同一诊单可能同时有已完成的旧挂号和已预约的新挂号,二维码应使用明细里的有效预约。
*/
const activeAppointment = (row: any) =>
appointmentRows(row).find((apt: any) => Number(apt?.status) === 1) ?? null
const activeAppointmentRow = (row: any) => {
const apt = activeAppointment(row)
if (!apt) return null
return {
...row,
has_appointment: 1,
appointment_id: apt.id,
appointment_status: apt.status,
appointment_doctor_id: apt.doctor_id,
appointment_doctor_name: apt.doctor_name,
appointment_time_text: apt.time_text
}
}
const isAppointmentActiveForVideo = (row: any) => !!activeAppointment(row)
const canCancelAppointmentRow = (row: any) => {
const s = Number(row.appointment_status)
@@ -1241,13 +1261,14 @@ const qrcodeAppointmentTimeText = computed(() => {
return p.appointment_time_text || '—'
})
const handleVideoQRCode = async (row: any) => {
if (!isAppointmentActiveForVideo(row)) {
feedback.msgWarning('仅「已预约」状态可生成视频二维码'); return
}
if (!row.patient_id) { feedback.msgWarning('患者信息不完整'); return }
lastQRCodeType.value = 'video'
currentQRCodePatient.value = row
const handleVideoQRCode = async (row: any) => {
const qrcodeRow = activeAppointmentRow(row)
if (!qrcodeRow) {
feedback.msgWarning('仅「已预约」状态可生成视频二维码'); return
}
if (!qrcodeRow.patient_id) { feedback.msgWarning('患者信息不完整'); return }
lastQRCodeType.value = 'video'
currentQRCodePatient.value = qrcodeRow
qrcodeDialogVisible.value = true
qrcodeLoading.value = true
qrcodeUrl.value = ''
@@ -1258,11 +1279,11 @@ const handleVideoQRCode = async (row: any) => {
feedback.msgError('小程序未配置')
qrcodeDialogVisible.value = false
return
}
const result = await generateMiniProgramQrcode({
doctor_id: row.appointment_doctor_id,
diagnosis_id: row.appointment_doctor_id,
patient_id: row.patient_id,
}
const result = await generateMiniProgramQrcode({
doctor_id: qrcodeRow.appointment_doctor_id,
diagnosis_id: qrcodeRow.appointment_doctor_id,
patient_id: qrcodeRow.patient_id,
share_user_id: userStore.userInfo?.id || '',
mini_program_path: 'pages/login/login'
})
@@ -1275,13 +1296,14 @@ const handleVideoQRCode = async (row: any) => {
}
}
const handleMiniProgramQRCode = async (row: any) => {
if (!isAppointmentActiveForVideo(row)) {
feedback.msgWarning('仅「已预约」状态可使用诊单二维码'); return
}
if (!row.patient_id) { feedback.msgWarning('患者信息不完整'); return }
lastQRCodeType.value = 'confirm'
currentQRCodePatient.value = row
const handleMiniProgramQRCode = async (row: any) => {
const qrcodeRow = activeAppointmentRow(row)
if (!qrcodeRow) {
feedback.msgWarning('仅「已预约」状态可使用诊单二维码'); return
}
if (!qrcodeRow.patient_id) { feedback.msgWarning('患者信息不完整'); return }
lastQRCodeType.value = 'confirm'
currentQRCodePatient.value = qrcodeRow
qrcodeDialogVisible.value = true
qrcodeLoading.value = true
qrcodeUrl.value = ''
@@ -1292,11 +1314,11 @@ const handleMiniProgramQRCode = async (row: any) => {
feedback.msgError('小程序未配置')
qrcodeDialogVisible.value = false
return
}
const result = await generateMiniProgramQrcode({
diagnosis_id: row.id,
doctor_id: row.appointment_doctor_id,
patient_id: row.patient_id,
}
const result = await generateMiniProgramQrcode({
diagnosis_id: qrcodeRow.id,
doctor_id: qrcodeRow.appointment_doctor_id,
patient_id: qrcodeRow.patient_id,
share_user_id: userStore.userInfo?.id || ''
})
if (result?.qrcode_url) qrcodeUrl.value = result.qrcode_url
@@ -27,6 +27,21 @@ class ConversionController extends BaseAdminController
));
}
public function fansDetail()
{
if (!$this->hasPagePermission()) {
return $this->fail('权限不足,无法查看加粉明细');
}
@set_time_limit(120);
return $this->data(FirstVisitConversionLogic::fansDetail(
$this->request->get(),
$this->adminId,
$this->adminInfo
));
}
private function hasPagePermission(): bool
{
if ((int) ($this->adminInfo['root'] ?? 0) === 1) {
@@ -62,7 +62,7 @@ class WecomPromotionController extends BaseAdminController
return $this->run(function () use ($id) {
WecomPromotionLogic::deletePool($id, $this->adminId, $this->adminInfo);
return $this->success('分流方案删除');
return $this->success('分流方案及企业微信官方获客链接已永久删除');
});
}
@@ -31,49 +31,22 @@ class FirstVisitConversionLogic
/** @return array<string,mixed> */
public static function overview(array $params, int $adminId, array $adminInfo): array
{
[$startDate, $endDate, $timeType, $timeLabel] = self::resolveTimeRange($params);
$baseVisibleAdminIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
$allowedDeptSet = DataScopeService::getAllowedDeptIdSet($adminId, $adminInfo);
$scopeValue = DataScopeService::getEffectiveScope($adminInfo);
$selectedDeptId = max(0, (int) ($params['dept_id'] ?? 0));
$selectedAssistantId = max(0, (int) ($params['assistant_id'] ?? 0));
$requestedMediaChannelCode = trim((string)($params['media_channel_code'] ?? ''));
$selectedMediaChannel = $requestedMediaChannelCode !== ''
? MediaChannelService::getCurrentTagChannelByCode($requestedMediaChannelCode)
: null;
$selectedMediaChannelCode = $selectedMediaChannel !== null ? $requestedMediaChannelCode : '';
$deptSelectionValid = $selectedDeptId <= 0
|| $allowedDeptSet === null
|| isset($allowedDeptSet[$selectedDeptId]);
$selectedDeptIds = [];
if ($selectedDeptId > 0 && $deptSelectionValid) {
$selectedDeptIds = array_values(array_unique(array_filter(array_map(
'intval',
DeptLogic::getSelfAndDescendantIds($selectedDeptId)
), static fn (int $id): bool => $id > 0)));
if ($allowedDeptSet !== null) {
$selectedDeptIds = array_values(array_filter(
$selectedDeptIds,
static fn (int $id): bool => isset($allowedDeptSet[$id])
));
}
}
$effectiveAdminIds = $deptSelectionValid ? $baseVisibleAdminIds : [];
if ($selectedDeptId > 0 && $deptSelectionValid) {
$deptAdminIds = $selectedDeptIds === []
? []
: self::normalizeIds(AdminDept::whereIn('dept_id', $selectedDeptIds)->column('admin_id'));
$effectiveAdminIds = self::intersectVisibleIds($effectiveAdminIds, $deptAdminIds);
}
$selectedAssistantValid = $selectedAssistantId <= 0;
if ($selectedAssistantId > 0) {
$selectedAssistantValid = self::isActiveAssistant($selectedAssistantId)
&& ($effectiveAdminIds === null || in_array($selectedAssistantId, $effectiveAdminIds, true));
$effectiveAdminIds = $selectedAssistantValid ? [$selectedAssistantId] : [];
}
$context = self::resolveScopeContext($params, $adminId, $adminInfo);
$startDate = $context['start_date'];
$endDate = $context['end_date'];
$timeType = $context['time_type'];
$timeLabel = $context['time_label'];
$allowedDeptSet = $context['allowed_dept_set'];
$baseVisibleAdminIds = $context['base_visible_admin_ids'];
$scopeValue = $context['scope_value'];
$selectedDeptId = $context['selected_dept_id'];
$selectedAssistantId = $context['selected_assistant_id'];
$selectedMediaChannel = $context['selected_media_channel'];
$selectedMediaChannelCode = $context['selected_media_channel_code'];
$deptSelectionValid = $context['dept_selection_valid'];
$selectedDeptIds = $context['selected_dept_ids'];
$effectiveAdminIds = $context['effective_admin_ids'];
$selectedAssistantValid = $context['selected_assistant_valid'];
$costAllocationAdminIds = self::costAllocationAdminIds(
$effectiveAdminIds,
$scopeValue === DataScopeService::SCOPE_SELF || $selectedAssistantId > 0
@@ -208,6 +181,337 @@ class FirstVisitConversionLogic
];
}
/** @return array<string,mixed> */
public static function fansDetail(array $params, int $adminId, array $adminInfo): array
{
$context = self::resolveScopeContext($params, $adminId, $adminInfo);
$context['channel_dept_ids'] = ConversionLogic::fanDetailChannelDeptIds(
$context['selected_media_channel']
);
$pageNo = max(1, (int) ($params['page_no'] ?? 1));
$pageSize = max(1, min(100, (int) ($params['page_size'] ?? 20)));
$empty = [
'lists' => [],
'count' => 0,
'page_no' => $pageNo,
'page_size' => $pageSize,
'date_range' => [$context['start_date'], $context['end_date']],
'entity' => null,
];
$entityType = strtolower(trim((string) ($params['entity_type'] ?? '')));
if (!in_array($entityType, ['dept', 'member'], true)) {
return $empty;
}
$entityId = trim((string) ($params['entity_id'] ?? ''));
$requestedAdminId = max(0, (int) ($params['admin_id'] ?? 0));
$resolved = self::resolveFanDetailEntity(
$entityType,
$entityId,
$requestedAdminId,
$context
);
if ($resolved === null) {
return $empty;
}
$target = $resolved['target'];
$conversionParams = [
'dimension' => 'dept',
'time_type' => 'custom',
'start_date' => $context['start_date'],
'end_date' => $context['end_date'],
'page_no' => $pageNo,
'page_size' => $pageSize,
];
if ((int) $context['selected_dept_id'] > 0 && !empty($context['dept_selection_valid'])) {
$conversionParams['dept_id'] = (int) $context['selected_dept_id'];
}
if ((string) $context['selected_media_channel_code'] !== '') {
$conversionParams['media_channel_code'] = (string) $context['selected_media_channel_code'];
}
$result = ConversionLogic::fanDetails(
$conversionParams,
$target,
$adminId,
$adminInfo,
$context['effective_admin_ids'],
$context['selected_media_channel']
);
if ((int) ($result['count'] ?? 0) <= 0) {
return $empty;
}
$result['entity'] = [
'type' => $entityType,
'id' => $resolved['id'],
'admin_id' => (int) $resolved['admin_id'],
'name' => (string) $resolved['name'],
'add_fans_count' => (int) $result['count'],
'deleted_fans_count' => (int) ($result['deleted_count'] ?? 0),
];
unset($result['deleted_count']);
return $result;
}
/**
* Resolve only the clicked entity and its authorized target range. This is
* deliberately structural: it avoids recomputing all overview metrics,
* while still rejecting ids that do not exist in the current DataScope.
*
* @param array<string,mixed> $context
* @return array{id:string|int,admin_id:int,name:string,target:array<string,mixed>}|null
*/
private static function resolveFanDetailEntity(
string $entityType,
string $entityId,
int $requestedAdminId,
array $context
): ?array {
$visibleAdminIds = $context['effective_admin_ids'];
$selectedDeptId = (int) $context['selected_dept_id'];
$channelDeptIds = $context['channel_dept_ids'] ?? null;
if ($entityType === 'member' && str_starts_with($entityId, 'U_')) {
$wecomUserId = trim(substr($entityId, 2));
if ($wecomUserId === '' || $requestedAdminId > 0 || $visibleAdminIds !== null || $selectedDeptId > 0) {
return null;
}
$isBound = Db::name('admin')
->whereNull('delete_time')
->where('work_wechat_userid', $wecomUserId)
->count() > 0;
if ($isBound) {
return null;
}
$historicalName = trim((string) (Db::name('admin')
->where('work_wechat_userid', $wecomUserId)
->order('id', 'desc')
->value('name') ?? ''));
return [
'id' => $entityId,
'admin_id' => 0,
'name' => $historicalName !== ''
? $historicalName . '' . $wecomUserId . ''
: $wecomUserId,
'target' => ['type' => 'wecom_user', 'wecom_userid' => $wecomUserId],
];
}
if ($entityType === 'member') {
if (!preg_match('/^M([1-9]\d*)_(-?\d+)$/', $entityId, $matches)) {
return null;
}
$targetAdminId = (int) $matches[1];
$rowDeptId = (int) $matches[2];
if (($requestedAdminId > 0 && $requestedAdminId !== $targetAdminId)
|| ($visibleAdminIds !== null && !in_array($targetAdminId, $visibleAdminIds, true))
) {
return null;
}
$admin = Db::name('admin')
->where('id', $targetAdminId)
->whereNull('delete_time')
->where('work_wechat_userid', '<>', '')
->field('id,name')
->find();
$resolvedDeptId = self::resolveFanDetailMemberDeptId($targetAdminId, $context);
if (!$admin
|| $resolvedDeptId !== $rowDeptId
|| ($resolvedDeptId === -2 && $selectedDeptId > 0)
) {
return null;
}
return [
'id' => $entityId,
'admin_id' => $targetAdminId,
'name' => trim((string) ($admin['name'] ?? '')),
'target' => ['type' => 'member', 'admin_id' => $targetAdminId],
];
}
if ($entityType !== 'dept' || !preg_match('/^-?\d+$/', $entityId)) {
return null;
}
$deptId = (int) $entityId;
if (in_array($deptId, [-1, -2], true)) {
if ($selectedDeptId > 0 || $visibleAdminIds !== null) {
return null;
}
$name = $deptId === -1 ? '未绑定后台账号' : '未分配部门';
return [
'id' => $deptId,
'admin_id' => 0,
'name' => $name,
'target' => ['type' => 'dept', 'dept_ids' => [$deptId]],
];
}
if ($deptId <= 0) {
return null;
}
$dept = Db::name('dept')->where('id', $deptId)->whereNull('delete_time')->field('id,name')->find();
if (!$dept) {
return null;
}
$universeDeptIds = $selectedDeptId > 0
? self::normalizeIds(DeptLogic::getSelfAndDescendantIds($selectedDeptId))
: self::normalizeIds(Db::name('dept')->whereNull('delete_time')->column('id'));
if ($channelDeptIds !== null) {
$universeDeptIds = array_values(array_intersect($universeDeptIds, $channelDeptIds));
}
$rowDeptIds = self::visibleRowDeptIds($visibleAdminIds);
if (!in_array($deptId, $universeDeptIds, true)
|| ($rowDeptIds !== null && !in_array($deptId, $rowDeptIds, true))
) {
return null;
}
$targetDeptIds = array_values(array_intersect(
self::normalizeIds(DeptLogic::getSelfAndDescendantIds($deptId)),
$universeDeptIds,
$rowDeptIds ?? $universeDeptIds
));
if ($targetDeptIds === []) {
return null;
}
return [
'id' => $deptId,
'admin_id' => 0,
'name' => trim((string) ($dept['name'] ?? '')),
'target' => ['type' => 'dept', 'dept_ids' => $targetDeptIds],
];
}
/** @param array<string,mixed> $context */
private static function resolveFanDetailMemberDeptId(int $adminId, array $context): int
{
// Other roles are rendered by ConversionLogic in the unassigned bucket.
$hasMedicalRole = Db::name('admin_role')
->where('admin_id', $adminId)
->whereIn('role_id', [1, self::ASSISTANT_ROLE_ID])
->count() > 0;
if (!$hasMedicalRole) {
return -2;
}
$selectedDeptId = (int) $context['selected_dept_id'];
$validDeptIds = $selectedDeptId > 0
? self::normalizeIds(DeptLogic::getSelfAndDescendantIds($selectedDeptId))
: self::normalizeIds(Db::name('dept')->whereNull('delete_time')->column('id'));
if (($context['channel_dept_ids'] ?? null) !== null) {
$validDeptIds = array_values(array_intersect($validDeptIds, $context['channel_dept_ids']));
}
$validDeptSet = array_fill_keys($validDeptIds, true);
$deptRows = Db::name('dept')->whereNull('delete_time')->field('id,pid,sort')->select()->toArray();
$deptMeta = [];
foreach ($deptRows as $deptRow) {
$id = (int) ($deptRow['id'] ?? 0);
if ($id > 0) {
$deptMeta[$id] = ['pid' => (int) ($deptRow['pid'] ?? 0), 'sort' => (int) ($deptRow['sort'] ?? 0)];
}
}
$depthCache = [];
$depthOf = static function (int $id) use (&$depthOf, &$depthCache, $deptMeta): int {
if ($id <= 0 || !isset($deptMeta[$id])) {
return 0;
}
if (isset($depthCache[$id])) {
return $depthCache[$id];
}
$pid = (int) ($deptMeta[$id]['pid'] ?? 0);
if ($pid <= 0 || $pid === $id || !isset($deptMeta[$pid])) {
return $depthCache[$id] = 0;
}
return $depthCache[$id] = $depthOf($pid) + 1;
};
$adminDeptIds = self::normalizeIds(AdminDept::where('admin_id', $adminId)->column('dept_id'));
usort($adminDeptIds, static function (int $left, int $right) use ($depthOf, $deptMeta): int {
return ($depthOf($right) <=> $depthOf($left))
?: ((int) ($deptMeta[$right]['sort'] ?? 0) <=> (int) ($deptMeta[$left]['sort'] ?? 0))
?: ($left <=> $right);
});
foreach ($adminDeptIds as $adminDeptId) {
if (isset($validDeptSet[$adminDeptId])) {
return $adminDeptId;
}
}
return -2;
}
/** @return array<string,mixed> */
private static function resolveScopeContext(array $params, int $adminId, array $adminInfo): array
{
[$startDate, $endDate, $timeType, $timeLabel] = self::resolveTimeRange($params);
$baseVisibleAdminIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
$allowedDeptSet = DataScopeService::getAllowedDeptIdSet($adminId, $adminInfo);
$scopeValue = DataScopeService::getEffectiveScope($adminInfo);
$selectedDeptId = max(0, (int) ($params['dept_id'] ?? 0));
$selectedAssistantId = max(0, (int) ($params['assistant_id'] ?? 0));
$requestedMediaChannelCode = trim((string) ($params['media_channel_code'] ?? ''));
$selectedMediaChannel = $requestedMediaChannelCode !== ''
? MediaChannelService::getCurrentTagChannelByCode($requestedMediaChannelCode)
: null;
$selectedMediaChannelCode = $selectedMediaChannel !== null ? $requestedMediaChannelCode : '';
$deptSelectionValid = $selectedDeptId <= 0
|| $allowedDeptSet === null
|| isset($allowedDeptSet[$selectedDeptId]);
$selectedDeptIds = [];
if ($selectedDeptId > 0 && $deptSelectionValid) {
$selectedDeptIds = array_values(array_unique(array_filter(array_map(
'intval',
DeptLogic::getSelfAndDescendantIds($selectedDeptId)
), static fn (int $id): bool => $id > 0)));
if ($allowedDeptSet !== null) {
$selectedDeptIds = array_values(array_filter(
$selectedDeptIds,
static fn (int $id): bool => isset($allowedDeptSet[$id])
));
}
}
$effectiveAdminIds = $deptSelectionValid ? $baseVisibleAdminIds : [];
if ($selectedDeptId > 0 && $deptSelectionValid) {
$deptAdminIds = $selectedDeptIds === []
? []
: self::normalizeIds(AdminDept::whereIn('dept_id', $selectedDeptIds)->column('admin_id'));
$effectiveAdminIds = self::intersectVisibleIds($effectiveAdminIds, $deptAdminIds);
}
$selectedAssistantValid = $selectedAssistantId <= 0;
if ($selectedAssistantId > 0) {
$selectedAssistantValid = self::isActiveAssistant($selectedAssistantId)
&& ($effectiveAdminIds === null || in_array($selectedAssistantId, $effectiveAdminIds, true));
$effectiveAdminIds = $selectedAssistantValid ? [$selectedAssistantId] : [];
}
return [
'start_date' => $startDate,
'end_date' => $endDate,
'time_type' => $timeType,
'time_label' => $timeLabel,
'base_visible_admin_ids' => $baseVisibleAdminIds,
'allowed_dept_set' => $allowedDeptSet,
'scope_value' => $scopeValue,
'selected_dept_id' => $selectedDeptId,
'selected_assistant_id' => $selectedAssistantId,
'selected_media_channel' => $selectedMediaChannel,
'selected_media_channel_code' => $selectedMediaChannelCode,
'dept_selection_valid' => $deptSelectionValid,
'selected_dept_ids' => $selectedDeptIds,
'effective_admin_ids' => $effectiveAdminIds,
'selected_assistant_valid' => $selectedAssistantValid,
];
}
/** @return array{0:string,1:string,2:string,3:string} */
private static function resolveTimeRange(array $params): array
{
@@ -4,12 +4,13 @@ declare(strict_types=1);
namespace app\adminapi\logic\firstvisit;
use app\adminapi\logic\dept\DeptLogic;
use app\common\service\DataScope\DataScopeService;
use app\common\service\qywx\QywxCustomerAcquisitionApiService;
use app\common\service\qywx\QywxCustomerAcquisitionLinkService;
use app\common\service\qywx\QywxPromotionMemberRange;
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 RuntimeException;
use think\facade\Db;
@@ -126,11 +127,11 @@ class WecomPromotionLogic
$officialLink = $officialLinks[0] ?? null;
$memberRules = $memberRulesByPool[(int) $pool['id']] ?? [];
$sync = $syncByPool[(int) $pool['id']] ?? [];
$remoteUserIds = array_fill_keys((array) ($officialLink['range_userids'] ?? []), true);
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['is_current'] = false;
$memberRule['is_applied'] = false;
$memberRule['is_in_remote_range'] = isset($remoteUserIds[(string) ($memberRule['userid'] ?? '')]);
$memberRule['sync_status'] = (int) ($sync['status'] ?? 0);
$memberRule['sync_error'] = (string) ($sync['last_error'] ?? '');
}
@@ -183,6 +184,7 @@ class WecomPromotionLogic
'pools' => $pools,
'links' => $links,
'member_options' => $memberOptions,
'department_options' => DeptLogic::getAllDataScoped($adminId, $adminInfo),
'customer_acquisition_link_example' => QywxCustomerAcquisitionLinkService::example(),
];
}
@@ -204,7 +206,7 @@ class WecomPromotionLogic
}
$members = self::resolveMembers((array) ($params['member_admin_ids'] ?? []), $adminId, $adminInfo);
$userIds = array_values(array_column($members, 'userid'));
$selectedUserId = self::preferredDispatchUserId($id, $members);
$eligibleUserIds = self::eligibleSelectedUserIds($id, $members);
$skipVerify = (int) ($params['skip_verify'] ?? 0) === 1 ? 1 : 0;
$status = (int) ($params['status'] ?? 1) === 1 ? 1 : 0;
$now = time();
@@ -227,38 +229,38 @@ class WecomPromotionLogic
}
$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 {
$remote = null;
if ($existingLink === null) {
$payload = [
'link_name' => mb_substr($name, 0, 30),
// 一个官方直链同时绑定全部当前可用医助,由企业微信原生多人路由直接分配。
'range' => ['user_list' => $eligibleUserIds],
'skip_verify' => $skipVerify === 1,
];
$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 {
$remote = self::normaliseRemoteLink($api->getLink($remoteLinkId), $remoteLinkId);
if (!QywxPromotionMemberRange::same($remote['range_userids'], $eligibleUserIds)) {
throw new RuntimeException('企业微信返回的多人路由成员范围与方案可用医助不一致');
}
} catch (\Throwable $e) {
try {
$api->deleteLink($remoteLinkId);
} catch (\Throwable) {
// 保留详情读取的原始异常;孤立链接仍可通过企业微信同步找回。
}
throw $e;
}
throw $e;
} else {
$remoteLinkId = trim((string) ($existingLink['remote_link_id'] ?? ''));
}
$remoteData = self::remoteColumns($remote, $now);
$remoteData = $remote !== null ? self::remoteColumns($remote, $now) : [];
$linkId = (int) ($existingLink['id'] ?? 0);
try {
@@ -270,7 +272,8 @@ class WecomPromotionLogic
$status,
$existingPool,
$members,
$selectedUserId,
$skipVerify,
$createdRemote,
$now,
$adminId
): void {
@@ -288,8 +291,10 @@ class WecomPromotionLogic
$linkCommon = [
'pool_id' => $id,
'name' => mb_substr((string) $poolData['name'], 0, 30),
'group_name' => '方案官方链接',
'status' => $status,
'skip_verify' => $skipVerify,
'update_time' => $now,
];
if ($linkId > 0) {
@@ -312,7 +317,9 @@ class WecomPromotionLogic
]);
}
self::persistPoolMembers($id, $members, $now);
QywxPromotionMemberSchedulerService::initialisePool($id, $linkId, $selectedUserId);
if ($createdRemote) {
QywxPromotionMemberSchedulerService::initialisePool($id, $linkId);
}
// 旧多链接只在本地下线,企业微信远端与历史客户归因继续保留。
Db::name('qywx_promotion_link')
@@ -332,16 +339,30 @@ class WecomPromotionLogic
throw $e;
}
$syncError = '';
if (!$createdRemote) {
QywxPromotionMemberSchedulerService::requestPoolSync($id, $linkId);
try {
(new QywxPromotionRangeSyncService())->syncPool($id);
} catch (\Throwable $e) {
// 本地方案和成员规则已保存;后台分钟任务会继续重试最新完整范围。
$syncError = $e->getMessage();
}
}
$savedLink = Db::name('qywx_promotion_link')->where('id', $linkId)->find() ?: [];
$savedUrl = (string) ($savedLink['wecom_url'] ?? $remote['url'] ?? '');
return [
'id' => $id,
'remote_link_id' => (string) ($remote['link_id'] ?? ''),
'wecom_url' => (string) ($remote['url'] ?? ''),
'remote_link_id' => (string) ($savedLink['remote_link_id'] ?? $remoteLinkId),
'wecom_url' => $savedUrl,
'main_url' => QywxCustomerAcquisitionLinkService::withCustomerChannel(
(string) ($remote['url'] ?? ''),
$savedUrl,
'zyt_pool:' . $id
),
'member_userids' => $userIds,
'current_userid' => $selectedUserId,
'range_userids' => $eligibleUserIds,
'sync_error' => $syncError,
];
}
@@ -360,13 +381,99 @@ class WecomPromotionLogic
return ['id' => $id, 'widget_config' => $config];
}
public static function deletePool(int $id, int $adminId, array $adminInfo): void
public static function deletePool(
int $id,
int $adminId,
array $adminInfo,
?QywxCustomerAcquisitionApiService $api = null
): void
{
self::assertScopedRow('qywx_promotion_pool', $id, $adminId, $adminInfo);
$links = Db::name('qywx_promotion_link')
->where('pool_id', $id)
->order('id', 'asc')
->select()->toArray();
$now = time();
$remoteLinks = array_values(array_filter($links, static fn (array $link): bool =>
trim((string) ($link['remote_link_id'] ?? '')) !== ''
&& (int) ($link['remote_status'] ?? 0) !== 2
));
if ($remoteLinks !== []) {
// 使用方案级删除租约阻止双击、重复请求以及回调/分钟任务继续更新远端范围。
$deleteToken = bin2hex(random_bytes(16));
Db::transaction(function () use ($id, $remoteLinks, $deleteToken, $now): void {
$sync = Db::name('qywx_promotion_range_sync')->where('pool_id', $id)->lock(true)->find();
if ($sync && (int) ($sync['status'] ?? 0) === 5 && (int) ($sync['lock_until'] ?? 0) > $now) {
throw new RuntimeException('该分流方案正在删除,请勿重复提交');
}
$data = [
'promotion_link_id' => (int) ($sync['promotion_link_id'] ?? $remoteLinks[0]['id'] ?? 0),
'desired_member_id' => 0,
'desired_version' => max(1, (int) ($sync['desired_version'] ?? 0) + 1),
'status' => 5,
'next_retry' => 0,
'lock_token' => $deleteToken,
'lock_until' => $now + 300,
'last_error' => '分流方案正在删除企业微信官方链接',
'update_time' => $now,
];
if ($sync) {
Db::name('qywx_promotion_range_sync')->where('pool_id', $id)->update($data);
} else {
Db::name('qywx_promotion_range_sync')->insert($data + [
'pool_id' => $id,
'applied_member_id' => 0,
'applied_version' => 0,
'attempts' => 0,
'create_time' => $now,
]);
}
});
$api ??= new QywxCustomerAcquisitionApiService();
foreach ($remoteLinks as $link) {
$linkId = (int) ($link['id'] ?? 0);
$remoteLinkId = trim((string) ($link['remote_link_id'] ?? ''));
try {
$api->deleteLink($remoteLinkId);
} catch (\Throwable $e) {
// 删除是幂等操作:企业微信明确返回 invalid link_id 时,远端目标已不存在
// 或已不再属于当前应用,继续完成本地清理;其他错误仍保留方案以便重试。
if (!self::isRemoteLinkAlreadyMissing($e)) {
$message = '企业微信官方获客链接删除失败,本地方案已保留:' . $e->getMessage();
Db::name('qywx_promotion_link')->where('id', $linkId)->update([
'sync_error' => mb_substr($message, 0, 500),
'update_time' => time(),
]);
Db::name('qywx_promotion_range_sync')->where('pool_id', $id)->where('lock_token', $deleteToken)->update([
'status' => 4,
'next_retry' => 0,
'lock_token' => '',
'lock_until' => 0,
'last_error' => mb_substr($message, 0, 500),
'update_time' => time(),
]);
throw new RuntimeException($message, 0, $e);
}
}
// 多个历史官方链接部分成功时也保存进度,用户重试删除不会再次请求已删除链接。
Db::name('qywx_promotion_link')->where('id', $linkId)->update([
'status' => 0,
'remote_status' => 2,
'last_sync_time' => time(),
'sync_error' => '',
'update_time' => time(),
]);
}
}
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_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([
'status' => 0,
'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,
@@ -375,12 +482,24 @@ class WecomPromotionLogic
Db::name('qywx_promotion_range_sync')->where('pool_id', $id)->update([
'status' => 4,
'next_retry' => 0,
'last_error' => '分流方案已删除',
'lock_token' => '',
'lock_until' => 0,
'last_error' => '分流方案及企业微信官方链接已删除',
'update_time' => $now,
]);
});
}
private static function isRemoteLinkAlreadyMissing(\Throwable $error): bool
{
$message = strtolower($error->getMessage());
$isInvalidLinkId = str_contains($message, 'invalid link_id');
$isInvalidParameter = (int) $error->getCode() === 40058
|| str_contains($message, '[40058]');
return $isInvalidParameter && $isInvalidLinkId;
}
public static function saveMember(array $params, int $adminId, array $adminInfo): array
{
self::assertMemberDispatchSchema();
@@ -412,7 +531,8 @@ class WecomPromotionLogic
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))),
// 企业微信原生多人路由不支持逐成员权重;字段固定为 1,仅兼容已部署表结构。
'weight' => 1,
'daily_limit' => min(1000000, max(0, (int) ($params['daily_limit'] ?? 0))),
'active_start' => $startAt,
'active_end' => $endAt,
@@ -445,7 +565,6 @@ class WecomPromotionLogic
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),
@@ -457,18 +576,10 @@ class WecomPromotionLogic
{
$id = max(0, (int) ($params['id'] ?? 0));
$poolId = max(0, (int) ($params['pool_id'] ?? 0));
$pool = self::assertScopedRow('qywx_promotion_pool', $poolId, $adminId, $adminInfo);
self::assertScopedRow('qywx_promotion_pool', $poolId, $adminId, $adminInfo);
$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('一个分流方案只能有一个官方获客链接,请直接编辑分流方案中的获客成员');
}
if ($existing === null || trim((string) ($existing['remote_link_id'] ?? '')) !== '') {
throw new RuntimeException('官方获客链接由分流方案统一维护,请直接编辑方案中的获客医助');
}
$name = trim((string) ($params['name'] ?? ''));
if ($name === '' || mb_strlen($name) > 80) {
@@ -494,66 +605,15 @@ class WecomPromotionLogic
'update_time' => $now,
];
// 历史手工链接只维护本地分流规则,不会在企业微信端创建重复链接
if ($existing !== null && trim((string) ($existing['remote_link_id'] ?? '')) === '') {
$url = trim((string) ($params['wecom_url'] ?? $existing['wecom_url'] ?? ''));
if (!QywxCustomerAcquisitionLinkService::isAllowed($url)) {
throw new RuntimeException('历史链接必须是 https://work.weixin.qq.com/ca/... 格式');
}
$data['wecom_url'] = $url;
Db::name('qywx_promotion_link')->where('id', $id)->update($data);
return ['id' => $id, 'mode' => 'legacy'];
// 仅保留历史手工链接的兼容编辑;官方链接只能经 savePool 和版本化同步服务修改
$url = trim((string) ($params['wecom_url'] ?? $existing['wecom_url'] ?? ''));
if (!QywxCustomerAcquisitionLinkService::isAllowed($url)) {
throw new RuntimeException('历史链接必须是 https://work.weixin.qq.com/ca/... 格式');
}
$data['wecom_url'] = $url;
Db::name('qywx_promotion_link')->where('id', $id)->update($data);
$userIds = self::resolveMemberUserIds((array) ($params['member_admin_ids'] ?? []), $adminId, $adminInfo);
$skipVerify = (int) ($params['skip_verify'] ?? 0) === 1 ? 1 : 0;
$payload = [
'link_name' => $name,
'range' => ['user_list' => $userIds],
'skip_verify' => $skipVerify === 1,
];
$api = new QywxCustomerAcquisitionApiService();
if ($existing !== null) {
$remoteLinkId = trim((string) ($existing['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,请先执行“同步企业微信”确认结果');
}
}
$remote = self::normaliseRemoteLink($api->getLink($remoteLinkId), $remoteLinkId);
$data += self::remoteColumns($remote, $now);
if ($existing !== null) {
Db::name('qywx_promotion_link')->where('id', $id)->update($data);
} else {
$data += [
'owner_admin_id' => (int) ($pool['owner_admin_id'] ?? 0) ?: $adminId,
'dept_id' => (int) ($pool['dept_id'] ?? 0) ?: self::primaryDeptId($adminId),
'click_count' => 0,
'today_count' => 0,
'today_date' => null,
'last_click_time' => 0,
'create_time' => $now,
];
try {
$id = (int) Db::name('qywx_promotion_link')->insertGetId($data);
} catch (\Throwable $e) {
try {
$api->deleteLink($remoteLinkId);
} catch (\Throwable) {
// 远端补偿失败时保留原始异常,管理员可通过“同步企业微信”找回链接。
}
throw $e;
}
}
return ['id' => $id, 'remote_link_id' => $remoteLinkId, 'mode' => 'official'];
return ['id' => $id, 'mode' => 'legacy'];
}
/** 验证 CorpID、应用 Secret、可信 IP 与获客助手接口权限。 */
@@ -653,7 +713,7 @@ class WecomPromotionLogic
return self::remotePublicPayload($remote);
}
/** 永久删除企业微信端获客链接,本地保留审计记录并停止分流。 */
/** 官方链接由方案统一删除,避免绕过方案删除租约和同步状态机。 */
public static function deleteRemoteLink(int $id, int $adminId, array $adminInfo): void
{
$row = self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo);
@@ -661,14 +721,8 @@ class WecomPromotionLogic
if ($remoteLinkId === '') {
throw new RuntimeException('历史手工链接只能从本地移除');
}
(new QywxCustomerAcquisitionApiService())->deleteLink($remoteLinkId);
Db::name('qywx_promotion_link')->where('id', $id)->update([
'status' => 0,
'remote_status' => 2,
'last_sync_time' => time(),
'sync_error' => '',
'update_time' => time(),
]);
throw new RuntimeException('官方获客链接由分流方案统一维护,请使用“删除分流方案”同时删除企业微信链接');
}
public static function toggleLink(int $id, int $status, int $adminId, array $adminInfo): void
@@ -685,14 +739,17 @@ class WecomPromotionLogic
public static function deleteLink(int $id, int $adminId, array $adminInfo): void
{
self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo);
$row = self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo);
if (trim((string) ($row['remote_link_id'] ?? '')) !== '') {
throw new RuntimeException('官方获客链接不能仅从本地移除,请使用“删除分流方案”同步删除企业微信链接');
}
Db::name('qywx_promotion_link')->where('id', $id)->update([
'delete_time' => time(),
'update_time' => time(),
]);
}
/** @return list<array{id:int,name:string,userid:string,dept_ids:list<int>,dept_names:list<string>}> */
/** @return list<array{id:int,name:string,userid:string,display_dept_id:int,dept_ids:list<int>,dept_names:list<string>}> */
private static function memberOptions(int $adminId, array $adminInfo): array
{
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
@@ -701,6 +758,7 @@ class WecomPromotionLogic
}
$query = Db::name('admin')->alias('a')
->whereNull('a.delete_time')
->where('a.disable', 0)
->where('a.work_wechat_userid', '<>', '');
if ($visibleIds !== null) {
$query->whereIn('a.id', $visibleIds);
@@ -733,11 +791,15 @@ class WecomPromotionLogic
}
$seenUserIds[$userId] = true;
$aid = (int) $admin['id'];
$deptIds = array_values(array_unique(array_filter($departments[$aid]['ids'] ?? [])));
$result[] = [
'id' => $aid,
'name' => (string) ($admin['name'] ?? $userId),
'userid' => $userId,
'dept_ids' => array_values(array_unique(array_filter($departments[$aid]['ids'] ?? []))),
// admin_dept 没有主部门字段;与现有获客归属、复诊统计口径一致,
// 取最小 dept_id 作为树形下拉的唯一展示归属,避免多部门成员重复选择。
'display_dept_id' => (int) ($deptIds[0] ?? 0),
'dept_ids' => $deptIds,
'dept_names' => array_values(array_unique($departments[$aid]['names'] ?? [])),
];
}
@@ -745,7 +807,7 @@ class WecomPromotionLogic
return $result;
}
/** @return list<array{id:int,name:string,userid:string,dept_ids:list<int>,dept_names:list<string>}> */
/** @return list<array{id:int,name:string,userid:string,display_dept_id:int,dept_ids:list<int>,dept_names:list<string>}> */
private static function resolveMembers(array $adminIds, int $adminId, array $adminInfo): array
{
$requested = array_values(array_unique(array_filter(array_map('intval', $adminIds))));
@@ -776,76 +838,41 @@ class WecomPromotionLogic
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
/** @param list<array{id:int,userid:string}> $members @return list<string> */
private static function eligibleSelectedUserIds(int $poolId, array $members): array
{
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) {
$candidates = [];
foreach ($members as $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;
$rule['userid'] = $userId;
$candidates[] = $rule;
}
$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'];
}
$range = QywxPromotionMemberRange::evaluate($candidates, $today, $now);
if ($range['userids'] === []) {
throw new RuntimeException('至少需要一名已启用、已生效且未达到今日上限的获客医助');
}
throw new RuntimeException('至少需要启用一名获客医助');
return $range['userids'];
}
/** @param list<array{id:int,userid:string}> $members */
@@ -918,16 +945,32 @@ class WecomPromotionLogic
if ($officialLink === null) {
continue;
}
$firstUserId = '';
foreach ((array) ($officialLink['range_userids'] ?? []) as $userIdValue) {
$userId = trim((string) $userIdValue);
$rangeUserIds = array_values(array_unique(array_filter(array_map(
static fn (mixed $value): string => trim((string) $value),
(array) ($officialLink['range_userids'] ?? [])
), static fn (string $value): bool => $value !== '')));
if ($rangeUserIds === []) {
continue;
}
$mappedMembers = [];
foreach ($rangeUserIds as $userId) {
$mappedAdminId = (int) ($adminIdByUserId[$userId] ?? 0);
if ($mappedAdminId <= 0) {
// 受限账号或本地缺少映射时禁止部分回填,避免定时任务误删企微中的不可见成员。
$mappedMembers = [];
break;
}
$mappedMembers[] = ['userid' => $userId, 'admin_id' => $mappedAdminId];
}
if (count($mappedMembers) !== count($rangeUserIds)) {
continue;
}
foreach ($mappedMembers as $mappedMember) {
$userId = (string) $mappedMember['userid'];
$mappedAdminId = (int) $mappedMember['admin_id'];
if ($userId === '' || $mappedAdminId <= 0) {
continue;
}
if ($firstUserId === '') {
$firstUserId = $userId;
}
try {
Db::name('qywx_promotion_pool_member')->insert([
'pool_id' => $poolId,
@@ -951,20 +994,8 @@ class WecomPromotionLogic
// 并发打开 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,
]);
if ($mappedMembers !== []) {
QywxPromotionMemberSchedulerService::initialisePool($poolId, (int) $officialLink['id']);
}
}
}
@@ -340,6 +340,304 @@ class ConversionLogic
return $result;
}
/**
* Return the distinct external contacts behind an add_fans_count row.
*
* The caller must pass the already-authorized admin range and the exact
* department ids represented by the clicked tree node. This keeps the
* detail endpoint on the same data-scope and department-primary-mapping
* rules as overview(), including descendant departments and virtual
* buckets.
*
* @param array<string,mixed> $params
* @param array<string,mixed> $target
* @param int[]|null $trustedVisibleAdminIdsOverride
* @param array<string,mixed>|null $trustedMediaChannelOverride
* @return array<string,mixed>
*/
public static function fanDetails(
array $params,
array $target,
int $adminId = 0,
?array $adminInfo = null,
?array $trustedVisibleAdminIdsOverride = null,
?array $trustedMediaChannelOverride = null
): array {
self::$requestRowsCache = [];
[$startTimestamp, $endTimestamp, $startDate, $endDate] = self::resolveTimeRange($params);
$pageNo = max(1, (int) ($params['page_no'] ?? 1));
$pageSize = max(1, min(100, (int) ($params['page_size'] ?? 20)));
$visibleAdminIds = $trustedVisibleAdminIdsOverride;
if ($trustedVisibleAdminIdsOverride === null && $adminInfo !== null && $adminId > 0) {
$visibleAdminIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
}
if ($visibleAdminIds !== null) {
$visibleAdminIds = array_values(array_unique(array_filter(
array_map('intval', $visibleAdminIds),
static fn (int $id): bool => $id > 0
)));
}
$empty = [
'lists' => [],
'count' => 0,
'page_no' => $pageNo,
'page_size' => $pageSize,
'date_range' => [$startDate, $endDate],
];
if ($visibleAdminIds === []) {
return $empty;
}
$mediaChannel = $trustedMediaChannelOverride;
$requestedMediaChannelCode = trim((string) ($params['media_channel_code'] ?? ''));
if ($mediaChannel === null && $requestedMediaChannelCode !== '') {
$mediaChannel = MediaChannelService::getChannelByCode($requestedMediaChannelCode);
}
$targetType = (string) ($target['type'] ?? '');
$targetAdminId = max(0, (int) ($target['admin_id'] ?? 0));
$targetWecomUserId = trim((string) ($target['wecom_userid'] ?? ''));
$targetDeptIds = array_values(array_unique(array_map('intval', (array) ($target['dept_ids'] ?? []))));
$targetDeptSet = array_fill_keys($targetDeptIds, true);
if (($targetType === 'member' && $targetAdminId <= 0)
|| ($targetType === 'wecom_user' && $targetWecomUserId === '')
|| ($targetType === 'dept' && $targetDeptSet === [])
) {
return $empty;
}
$allEntities = [];
$entityIds = [];
$adminToDeptIds = [];
$targetWorkWechatUserIds = [];
$unboundOnly = false;
if ($targetType === 'dept') {
$allEntities = self::loadEntities('dept', $params, $visibleAdminIds);
$adminToDeptIds = self::loadAdminDeptMap();
$channelDeptIds = self::fanDetailChannelDeptIds($mediaChannel);
if ($channelDeptIds !== null) {
$allEntities = self::filterEntitiesByChannelDeptScope(
$allEntities,
'dept',
$channelDeptIds,
$adminToDeptIds
);
}
$entityIds = array_keys($allEntities);
foreach ($targetDeptIds as $targetDeptId) {
if (!isset($allEntities[$targetDeptId])) {
return $empty;
}
}
if (isset($targetDeptSet[self::VIRTUAL_DEPT_UNBOUND_ADMIN_ID])) {
// The unbound bucket exists only under SCOPE_ALL. Keep the
// exclusion in SQL so this bucket does not first load every
// fan pair in the installation.
if ($visibleAdminIds !== null || count($targetDeptSet) !== 1) {
return $empty;
}
$unboundOnly = true;
} else {
$candidateAdminQuery = Db::name('admin')
->whereNull('delete_time')
->where('work_wechat_userid', '<>', '')
->field('id,work_wechat_userid');
if ($visibleAdminIds !== null) {
$candidateAdminQuery->whereIn('id', $visibleAdminIds);
}
foreach ($candidateAdminQuery->select()->toArray() as $candidateAdmin) {
$candidateAdminId = (int) ($candidateAdmin['id'] ?? 0);
$mappedIds = self::mapEntityIds(
'dept',
$candidateAdminId,
$entityIds,
$adminToDeptIds,
$visibleAdminIds
);
$mappedDeptId = $mappedIds !== []
? (int) $mappedIds[0]
: self::VIRTUAL_DEPT_UNASSIGNED_ID;
if (isset($targetDeptSet[$mappedDeptId])) {
$workWechatUserId = trim((string) ($candidateAdmin['work_wechat_userid'] ?? ''));
if ($workWechatUserId !== '') {
$targetWorkWechatUserIds[$workWechatUserId] = $workWechatUserId;
}
}
}
}
} elseif ($targetType === 'member') {
if ($visibleAdminIds !== null && !in_array($targetAdminId, $visibleAdminIds, true)) {
return $empty;
}
$targetAdmin = Db::name('admin')
->where('id', $targetAdminId)
->whereNull('delete_time')
->where('work_wechat_userid', '<>', '')
->field('id,work_wechat_userid')
->find();
if (!$targetAdmin) {
return $empty;
}
$targetWorkWechatUserIds[] = trim((string) $targetAdmin['work_wechat_userid']);
} else {
// Unbound WeCom rows are never visible in a restricted scope.
if ($visibleAdminIds !== null) {
return $empty;
}
$boundAdminExists = Db::name('admin')
->whereNull('delete_time')
->where('work_wechat_userid', $targetWecomUserId)
->count() > 0;
if ($boundAdminExists) {
return $empty;
}
$targetWorkWechatUserIds[] = $targetWecomUserId;
}
$targetWorkWechatUserIds = array_values(array_unique(array_filter(
array_map('strval', $targetWorkWechatUserIds),
static fn (string $userId): bool => $userId !== ''
)));
if (!$unboundOnly && $targetWorkWechatUserIds === []) {
return $empty;
}
$pairs = self::loadFanDetailRows(
$startTimestamp,
$endTimestamp,
$mediaChannel,
null,
$unboundOnly ? null : $targetWorkWechatUserIds,
$unboundOnly
);
// Target membership has already been resolved before the event query,
// so sorting and pagination operate on the clicked row's small set.
$matched = $pairs;
usort($matched, static function (array $left, array $right): int {
$timeCompare = ((int) ($right['add_time'] ?? 0)) <=> ((int) ($left['add_time'] ?? 0));
if ($timeCompare !== 0) {
return $timeCompare;
}
return strcmp(
(string) ($left['external_userid'] ?? ''),
(string) ($right['external_userid'] ?? '')
) ?: strcmp(
(string) ($left['user_id'] ?? ''),
(string) ($right['user_id'] ?? '')
);
});
$count = count($matched);
$deletedCount = count(array_filter(
$matched,
static fn (array $row): bool => !empty($row['is_deleted'])
));
$offset = ($pageNo - 1) * $pageSize;
$pageRows = array_slice($matched, $offset, $pageSize);
$externalUserIds = array_values(array_unique(array_filter(array_map(
static fn (array $row): string => trim((string) ($row['external_userid'] ?? '')),
$pageRows
))));
$customerNames = $externalUserIds === []
? []
: Db::name('qywx_external_contact')
->whereIn('external_userid', $externalUserIds)
->column('name', 'external_userid');
$deletedPageRows = array_values(array_filter(
$pageRows,
static fn (array $row): bool => !empty($row['is_deleted'])
));
$deleteTimesByPair = [];
if ($deletedPageRows !== []) {
$deletedUserIds = array_values(array_unique(array_filter(array_map(
static fn (array $row): string => trim((string) ($row['user_id'] ?? '')),
$deletedPageRows
))));
$deletedExternalUserIds = array_values(array_unique(array_filter(array_map(
static fn (array $row): string => trim((string) ($row['external_userid'] ?? '')),
$deletedPageRows
))));
$deletedEvents = Db::name('qywx_external_contact_event')
->where('change_type', 'del_external_contact')
->where('event_time', '<=', $endTimestamp)
->whereIn('user_id', $deletedUserIds)
->whereIn('external_userid', $deletedExternalUserIds)
->field('user_id,external_userid,MAX(event_time) AS delete_time')
->group('user_id,external_userid')
->select()
->toArray();
foreach ($deletedEvents as $deletedEvent) {
$key = trim((string) ($deletedEvent['user_id'] ?? ''))
. "\0"
. trim((string) ($deletedEvent['external_userid'] ?? ''));
$deleteTimesByPair[$key] = max(0, (int) ($deletedEvent['delete_time'] ?? 0));
}
}
$staffNames = self::resolveQywxUserNames(array_values(array_unique(array_filter(array_map(
static fn (array $row): string => trim((string) ($row['user_id'] ?? '')),
$pageRows
)))));
$lists = array_map(static function (array $row) use ($customerNames, $staffNames, $deleteTimesByPair): array {
$externalUserId = trim((string) ($row['external_userid'] ?? ''));
$wecomUserId = trim((string) ($row['user_id'] ?? ''));
$addTime = max(0, (int) ($row['add_time'] ?? 0));
$deleted = !empty($row['is_deleted']);
$deleteTime = $deleted
? max(0, (int) ($deleteTimesByPair[$wecomUserId . "\0" . $externalUserId] ?? 0))
: 0;
if ($deleteTime < $addTime) {
$deleteTime = 0;
}
return [
'external_userid' => $externalUserId,
'customer_name' => trim((string) ($customerNames[$externalUserId] ?? '')),
'wecom_userid' => $wecomUserId,
'wecom_staff_name' => trim((string) ($staffNames[$wecomUserId] ?? '')),
'add_time' => $addTime > 0 ? date('Y-m-d H:i:s', $addTime) : null,
'is_deleted' => $deleted,
'delete_time' => $deleted && $deleteTime > 0 ? date('Y-m-d H:i:s', $deleteTime) : null,
];
}, $pageRows);
return [
'lists' => $lists,
'count' => $count,
'deleted_count' => $deletedCount,
'page_no' => $pageNo,
'page_size' => $pageSize,
'date_range' => [$startDate, $endDate],
];
}
/**
* Department scope imposed by an actively maintained media-channel cost
* binding. null means the channel has no binding and therefore does not
* restrict business statistics.
*
* @param array<string,mixed>|null $mediaChannel
* @return int[]|null
*/
public static function fanDetailChannelDeptIds(?array $mediaChannel): ?array
{
if ($mediaChannel === null) {
return null;
}
$mediaChannelCodes = MediaChannelService::getChannelCodesForStats($mediaChannel);
if ($mediaChannelCodes === []) {
return null;
}
$boundDeptIds = self::loadChannelBoundDeptIds($mediaChannelCodes);
return $boundDeptIds === [] ? null : self::expandDeptIdsWithDescendants($boundDeptIds);
}
/**
* @param int[]|null $visibleAdminIds 可见 admin 集合(null = SCOPE_ALL
* @param int[] $eligibleDeptIds 当媒体渠道筛选生效时收窄过的部门作用域
@@ -987,6 +1285,33 @@ class ConversionLogic
int $endTimestamp,
?array $mediaChannel,
?array $adminIds = null
): array {
$detailRows = self::loadFanDetailRows($startTimestamp, $endTimestamp, $mediaChannel, $adminIds);
$effectivePairs = array_values(array_filter(
$detailRows,
static fn (array $row): bool => empty($row['is_deleted'])
));
return self::buildFanCountRows($detailRows, $effectivePairs);
}
/**
* Load the exact distinct (user_id, external_userid) pairs represented by
* add_fans_count. Both aggregate statistics and the detail endpoint consume
* this method, so channel, add-way, prior-add and deletion rules cannot
* drift apart.
*
* @param array<string, mixed>|null $mediaChannel
* @param int[]|null $adminIds
* @return array<int, array{user_id:string,external_userid:string,add_time:int,is_deleted:bool,delete_time:int}>
*/
private static function loadFanDetailRows(
int $startTimestamp,
int $endTimestamp,
?array $mediaChannel,
?array $adminIds = null,
?array $exactWorkWechatUserIds = null,
bool $unboundOnly = false
): array {
if ($adminIds !== null) {
$adminIds = array_values(array_unique(array_filter(
@@ -999,21 +1324,38 @@ class ConversionLogic
}
}
$baseKey = self::requestRowsCacheKey('fans-effective-v9', [
if ($exactWorkWechatUserIds !== null) {
$exactWorkWechatUserIds = array_values(array_unique(array_filter(
array_map('strval', $exactWorkWechatUserIds),
static fn (string $userId): bool => $userId !== ''
)));
sort($exactWorkWechatUserIds);
if ($exactWorkWechatUserIds === [] && !$unboundOnly) {
return [];
}
}
$baseKey = self::requestRowsCacheKey('fans-detail-v1', [
$startTimestamp,
$endTimestamp,
self::mediaChannelCacheKey($mediaChannel),
]);
$allKey = $baseKey . ':all';
$cacheKey = $adminIds === null
? $allKey
: $baseKey . ':admins:' . implode(',', $adminIds);
if ($unboundOnly) {
$cacheKey = $baseKey . ':unbound';
} elseif ($exactWorkWechatUserIds !== null) {
$cacheKey = $baseKey . ':wecom:' . implode(',', $exactWorkWechatUserIds);
} else {
$cacheKey = $adminIds === null
? $allKey
: $baseKey . ':admins:' . implode(',', $adminIds);
}
if (isset(self::$requestRowsCache[$cacheKey])) {
return self::$requestRowsCache[$cacheKey];
}
$workWechatUserIds = null;
if ($adminIds !== null) {
$workWechatUserIds = $exactWorkWechatUserIds;
if ($workWechatUserIds === null && $adminIds !== null) {
$workWechatUserIds = Db::name('admin')
->whereIn('id', $adminIds)
->whereNull('delete_time')
@@ -1052,10 +1394,17 @@ class ConversionLogic
. ' AND prev_e.event_time < ?)',
['add_external_contact', $startTimestamp]
)
->field(['e.user_id', 'e.external_userid'])
->field('e.user_id,e.external_userid,MIN(e.event_time) AS add_time')
->group('e.user_id, e.external_userid');
if ($workWechatUserIds !== null) {
$query->whereIn('e.user_id', $workWechatUserIds);
} elseif ($unboundOnly) {
$adminTable = config('database.connections.mysql.prefix') . 'admin';
$query->whereRaw(
'NOT EXISTS (SELECT 1 FROM `' . $adminTable . '` bound_a'
. ' WHERE bound_a.work_wechat_userid = e.user_id'
. ' AND bound_a.delete_time IS NULL)'
);
}
$effectiveQuery = clone $query;
if ($mediaChannel !== null) {
@@ -1064,13 +1413,19 @@ class ConversionLogic
}
$effectivePairs = $effectiveQuery
->whereRaw(
'NOT EXISTS (SELECT 1 FROM `' . $eventTable . '` del_e'
. ' WHERE del_e.user_id = e.user_id'
. ' AND del_e.external_userid = e.external_userid'
. ' AND del_e.change_type = ?'
. ' AND del_e.event_time >= e.event_time'
. ' AND del_e.event_time <= ?)',
['del_external_contact', $endTimestamp]
'EXISTS (SELECT 1 FROM `' . $eventTable . '` surviving_e'
. ' WHERE surviving_e.user_id = e.user_id'
. ' AND surviving_e.external_userid = e.external_userid'
. ' AND surviving_e.change_type = ?'
. ' AND surviving_e.event_time >= ?'
. ' AND surviving_e.event_time <= ?'
. ' AND NOT EXISTS (SELECT 1 FROM `' . $eventTable . '` surviving_del'
. ' WHERE surviving_del.user_id = surviving_e.user_id'
. ' AND surviving_del.external_userid = surviving_e.external_userid'
. ' AND surviving_del.change_type = ?'
. ' AND surviving_del.event_time >= surviving_e.event_time'
. ' AND surviving_del.event_time <= ?))',
['add_external_contact', $startTimestamp, $endTimestamp, 'del_external_contact', $endTimestamp]
)
->select()
->toArray();
@@ -1102,13 +1457,59 @@ class ConversionLogic
// add_way 筛选对有效与已删粉丝使用同一口径。
$candidatePairs = array_merge($effectivePairs, $deletedPairs);
$candidatePairs = self::excludeUncountedFanPairs($candidatePairs);
$rows = self::buildFanCountRows($candidatePairs, $effectivePairs);
$rows = self::buildFanDetailRows($candidatePairs, $effectivePairs);
self::$requestRowsCache[$cacheKey] = $rows;
return self::$requestRowsCache[$cacheKey];
}
/**
* @param array<int,array<string,mixed>> $candidatePairs
* @param array<int,array<string,mixed>> $effectivePairs
* @return array<int, array{user_id:string,external_userid:string,add_time:int,is_deleted:bool,delete_time:int}>
*/
private static function buildFanDetailRows(
array $candidatePairs,
array $effectivePairs
): array {
$effectiveKeys = [];
foreach ($effectivePairs as $pair) {
$userId = trim((string) ($pair['user_id'] ?? ''));
$externalUserId = trim((string) ($pair['external_userid'] ?? ''));
if ($userId !== '' && $externalUserId !== '') {
$effectiveKeys[$userId . "\0" . $externalUserId] = true;
}
}
$detailsByKey = [];
foreach ($candidatePairs as $pair) {
$userId = trim((string) ($pair['user_id'] ?? ''));
$externalUserId = trim((string) ($pair['external_userid'] ?? ''));
if ($userId === '' || $externalUserId === '') {
continue;
}
$key = $userId . "\0" . $externalUserId;
$addTime = max(0, (int) ($pair['add_time'] ?? 0));
if (isset($detailsByKey[$key])) {
$existingTime = (int) ($detailsByKey[$key]['add_time'] ?? 0);
if ($addTime > 0 && ($existingTime <= 0 || $addTime < $existingTime)) {
$detailsByKey[$key]['add_time'] = $addTime;
}
continue;
}
$detailsByKey[$key] = [
'user_id' => $userId,
'external_userid' => $externalUserId,
'add_time' => $addTime,
'is_deleted' => !isset($effectiveKeys[$key]),
'delete_time' => 0,
];
}
return array_values($detailsByKey);
}
/**
* 按员工聚合全部候选新增,并标记其中期末已删除的子集。
*
@@ -0,0 +1,44 @@
<?php
// +----------------------------------------------------------------------
// | 企业微信客服桌面端认证接口
// +----------------------------------------------------------------------
namespace app\api\controller;
use app\common\enum\user\UserTerminalEnum;
use app\common\enum\YesNoEnum;
use app\common\model\user\User;
/**
* 桌面端只通过这里确认一个 token 是否属于企业微信客服终端。
*
* 登录仍复用 LoginController::account;这个接口只返回建立外部账号映射所需的
* 最小身份信息,避免客服后台依赖面向普通 PC 页面的完整个人资料结构。
*/
class DesktopController extends BaseApiController
{
public function session()
{
if ((int)($this->userInfo['terminal'] ?? 0) !== UserTerminalEnum::WECOM_RPA) {
return $this->fail('当前登录令牌不属于企业微信客服桌面端');
}
$user = User::where(['id' => $this->userId])
->field('id,sn,nickname,mobile,avatar,is_disable')
->findOrEmpty();
if ($user->isEmpty() || (int)$user['is_disable'] === YesNoEnum::YES) {
return $this->fail('用户不存在或已禁用');
}
return $this->data([
'user_id' => (int)$user['id'],
'sn' => (string)$user['sn'],
'nickname' => (string)$user['nickname'],
'mobile' => (string)$user['mobile'],
'avatar' => (string)$user['avatar'],
'terminal' => UserTerminalEnum::WECOM_RPA,
'expire_time' => (int)($this->userInfo['expire_time'] ?? 0),
'status' => 'active',
]);
}
}
@@ -181,7 +181,7 @@ class QywxExternalContactCallbackController extends BaseApiController
}
if ($changeType === 'add_external_contact' && $state !== '' && $userId !== '') {
// customer_channel=zyt_pool:{id} 会原样进入 State;以实际 UserID 幂等记账并规划下一位
// customer_channel=zyt_pool:{id} 会原样进入 State;以实际 UserID 幂等记账并更新可用成员范围
try {
$dispatch = QywxPromotionMemberSchedulerService::recordFromState(
$state,
@@ -192,7 +192,7 @@ class QywxExternalContactCallbackController extends BaseApiController
);
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());
@@ -35,8 +35,8 @@ class LoginAccountValidate extends BaseValidate
protected $rule = [
'terminal' => 'require|in:' . UserTerminalEnum::WECHAT_MMP . ',' . UserTerminalEnum::WECHAT_OA . ','
. UserTerminalEnum::H5 . ',' . UserTerminalEnum::PC . ',' . UserTerminalEnum::IOS .
',' . UserTerminalEnum::ANDROID,
. UserTerminalEnum::H5 . ',' . UserTerminalEnum::PC . ',' . UserTerminalEnum::IOS
. ',' . UserTerminalEnum::ANDROID . ',' . UserTerminalEnum::WECOM_RPA,
'scene' => 'require|in:' . LoginEnum::ACCOUNT_PASSWORD . ',' . LoginEnum::MOBILE_CAPTCHA . '|checkConfig',
'account' => 'require',
];
@@ -158,4 +158,4 @@ class LoginAccountValidate extends BaseValidate
}
return '验证码错误';
}
}
}
@@ -9,13 +9,13 @@ 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('同步企业微信官方获客链接的下一名承接成员');
->setDescription('同步企业微信官方获客链接的多人路由成员范围');
}
protected function execute(Input $input, Output $output): int
@@ -28,6 +28,7 @@ class UserTerminalEnum
const PC = 4;//电脑PC
const IOS = 5;//苹果app
const ANDROID = 6;//安卓app
const WECOM_RPA = 7;//企业微信客服桌面端
const ALL_TERMINAL = [
@@ -37,6 +38,7 @@ class UserTerminalEnum
self::PC,
self::IOS,
self::ANDROID,
self::WECOM_RPA,
];
/**
@@ -55,10 +57,11 @@ class UserTerminalEnum
self::PC => '电脑PC',
self::IOS => '苹果APP',
self::ANDROID => '安卓APP',
self::WECOM_RPA => '企业微信客服桌面端',
];
if(true === $from){
return $desc;
}
return $desc[$from] ?? '';
}
}
}
@@ -187,7 +187,8 @@ class QywxCustomerAcquisitionApiService
if ($errcode === 60111) {
throw new RuntimeException(
'所选医助的企业微信 userid 不存在,或不在获客助手可调用应用的可见范围;'
. '请检查后台账号绑定和企业微信应用可见范围。企业微信返回:' . $errorMessage
. '请检查后台账号绑定和企业微信应用可见范围。企业微信返回:' . $errorMessage,
$errcode
);
}
@@ -195,7 +196,7 @@ class QywxCustomerAcquisitionApiService
'企业微信获客助手接口失败[%d]%s',
$errcode,
$errorMessage
));
), $errcode);
}
private function accessToken(): string
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace app\common\service\qywx;
/** 计算应交给企业微信官方多人路由的成员范围。 */
class QywxPromotionMemberRange
{
/**
* @param list<array<string,mixed>> $members
* @return array{userids:list<string>,members:list<array<string,mixed>>,eligible_count:int}
*/
public static function evaluate(array $members, string $today, int $now): array
{
$userIds = [];
foreach ($members as &$member) {
if ((string) ($member['today_date'] ?? '') !== $today) {
$member['today_date'] = $today;
$member['today_count'] = 0;
}
$member['current_weight'] = 0;
if (!self::eligible($member, $now)) {
continue;
}
$userId = trim((string) ($member['userid'] ?? ''));
if ($userId !== '') {
$userIds[$userId] = true;
}
}
unset($member);
return [
'userids' => array_keys($userIds),
'members' => array_values($members),
'eligible_count' => count($userIds),
];
}
/** @param array<string,mixed> $member */
public static function eligible(array $member, int $now): bool
{
if ((int) ($member['enabled'] ?? 0) !== 1) {
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;
}
/** @param list<string> $left @param list<string> $right */
public static function same(array $left, array $right): bool
{
$normalise = static function (array $values): array {
$result = [];
foreach ($values as $value) {
$userId = trim((string) $value);
if ($userId !== '') {
$result[$userId] = true;
}
}
$result = array_keys($result);
sort($result, SORT_STRING);
return $result;
};
return $normalise($left) === $normalise($right);
}
}
@@ -6,7 +6,7 @@ namespace app\common\service\qywx;
use think\facade\Db;
/** 根据实际获客回调记账,并同一个官方链接规划下一名承接成员。 */
/** 根据实际获客回调记账,并维护同一个官方链接的可用成员范围。 */
class QywxPromotionMemberSchedulerService
{
public static function poolIdFromState(string $state): int
@@ -54,7 +54,7 @@ class QywxPromotionMemberSchedulerService
}
/**
* 管理端变更成员开关、权重或上限后,确保当前远端成员仍可用;必要时立即排队切换
* 管理端变更成员开关、上限或有效期后,重新计算企业微信官方多人路由范围
*
* @return array{pool_id:int,next_member_id:int,queued:bool,blocked:bool}
*/
@@ -66,55 +66,30 @@ class QywxPromotionMemberSchedulerService
$linkId = self::promotionLinkId($poolId);
$sync = self::lockedSyncRow($poolId);
$members = self::lockedMembers($poolId);
$syncStatus = (int) ($sync['status'] ?? 0);
$syncError = (string) ($sync['last_error'] ?? '');
if ($syncStatus === 5 || ($syncStatus === 4 && str_starts_with($syncError, '企业微信官方获客链接删除失败'))) {
return ['pool_id' => $poolId, 'next_member_id' => 0, 'queued' => false, 'blocked' => true];
}
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);
return self::queueEligibleRangeLocked($poolId, $linkId, $sync, $members, $today, $now);
});
}
public static function initialisePool(int $poolId, int $promotionLinkId, string $selectedUserId): int
public static function initialisePool(int $poolId, int $promotionLinkId): 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) {
if ($poolId <= 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,
'desired_member_id' => 0,
'applied_member_id' => 0,
'status' => 0,
'attempts' => 0,
'next_retry' => 0,
@@ -133,7 +108,55 @@ class QywxPromotionMemberSchedulerService
Db::name('qywx_promotion_range_sync')->insert($data);
}
return $memberId;
return $promotionLinkId;
}
/** 强制排队一次成员范围同步,也用于方案名称或免验证配置变化。 */
public static function requestPoolSync(int $poolId, int $promotionLinkId): void
{
if ($poolId <= 0 || $promotionLinkId <= 0) {
return;
}
Db::transaction(function () use ($poolId, $promotionLinkId): void {
$now = time();
$existing = self::lockedSyncRow($poolId);
if ($existing === null) {
Db::name('qywx_promotion_range_sync')->insert([
'pool_id' => $poolId,
'promotion_link_id' => $promotionLinkId,
'desired_member_id' => 0,
'desired_version' => 1,
'applied_member_id' => 0,
'applied_version' => 0,
'status' => 1,
'attempts' => 0,
'next_retry' => $now,
'lock_token' => '',
'lock_until' => 0,
'last_error' => '',
'create_time' => $now,
'update_time' => $now,
]);
return;
}
$existingStatus = (int) ($existing['status'] ?? 0);
$leaseActive = in_array($existingStatus, [2, 5], true)
&& (int) ($existing['lock_until'] ?? 0) > $now;
$data = [
'promotion_link_id' => $promotionLinkId,
'desired_member_id' => 0,
'desired_version' => max(1, (int) ($existing['desired_version'] ?? 0) + 1),
'applied_member_id' => 0,
'status' => $leaseActive ? $existingStatus : 1,
'next_retry' => $leaseActive ? (int) ($existing['next_retry'] ?? 0) : $now,
'update_time' => $now,
];
if (!$leaseActive) {
$data += ['attempts' => 0, 'last_error' => ''];
}
Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->update($data);
});
}
/** @return array{status:string,pool_id:int,member_id:int,next_member_id:int} */
@@ -167,7 +190,8 @@ class QywxPromotionMemberSchedulerService
return ['status' => 'ignored_member', 'pool_id' => $poolId, 'member_id' => 0, 'next_member_id' => 0];
}
$actualMemberId = (int) $actualMember['id'];
$eventKey = hash('sha256', $poolId . '|' . $userId . '|' . $externalUserId);
// 同一方案中的同一客户只计一次;后续更换跟进成员不能重复占用医助额度。
$eventKey = hash('sha256', $poolId . '|' . $externalUserId);
try {
Db::name('qywx_promotion_dispatch_event')->insert([
'event_key' => $eventKey,
@@ -205,12 +229,11 @@ class QywxPromotionMemberSchedulerService
$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];
if ($linkId <= 0) {
return ['status' => 'counted_stale', 'pool_id' => $poolId, 'member_id' => $actualMemberId, 'next_member_id' => 0];
}
$planned = self::selectAndQueueLocked($poolId, $linkId, $sync, $members, $today, $now);
$planned = self::queueEligibleRangeLocked($poolId, $linkId, $sync, $members, $today, $now);
return [
'status' => $planned['blocked'] ? 'counted_blocked' : 'counted',
@@ -222,7 +245,7 @@ class QywxPromotionMemberSchedulerService
}
/** @return array{pool_id:int,next_member_id:int,queued:bool,blocked:bool} */
private static function selectAndQueueLocked(
private static function queueEligibleRangeLocked(
int $poolId,
int $linkId,
?array $sync,
@@ -230,40 +253,51 @@ class QywxPromotionMemberSchedulerService
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, '所有成员均已禁用、未生效或达到今日上限');
$range = QywxPromotionMemberRange::evaluate($members, $today, $now);
self::persistMemberCursors($range['members'], $now);
if ($range['userids'] === []) {
self::upsertSync($poolId, $linkId, 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);
$appliedUserIds = self::linkRangeUserIds($linkId);
$changed = !QywxPromotionMemberRange::same($range['userids'], $appliedUserIds);
$alreadyPending = in_array((int) ($sync['status'] ?? 0), [1, 3], true);
$needsSync = $changed || $alreadyPending;
self::upsertSync($poolId, $linkId, $needsSync, $sync, $now);
return ['pool_id' => $poolId, 'next_member_id' => $selectedId, 'queued' => $changed, 'blocked' => false];
return ['pool_id' => $poolId, 'next_member_id' => 0, 'queued' => $needsSync, '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));
$desiredChanged = $pending || ($error !== '' && (int) ($existing['status'] ?? 0) !== 4);
$version = max(1, (int) ($existing['desired_version'] ?? 0) + ($desiredChanged ? 1 : 0));
$existingStatus = (int) ($existing['status'] ?? 0);
$leaseActive = in_array($existingStatus, [2, 5], true)
&& (int) ($existing['lock_until'] ?? 0) > $now;
$data = [
'promotion_link_id' => $linkId,
'desired_member_id' => $desiredMemberId,
'desired_member_id' => 0,
'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),
// 活跃租约不能被回调或分钟重算抢占;版本变化会让当前工作完成后继续同步。
'status' => $leaseActive ? $existingStatus : ($error !== '' ? 4 : ($pending ? 1 : 0)),
'next_retry' => $leaseActive
? (int) ($existing['next_retry'] ?? 0)
: ($error !== '' ? strtotime('tomorrow', $now) : ($pending ? $now : 0)),
'last_error' => $leaseActive
? (string) ($existing['last_error'] ?? '')
: mb_substr($error, 0, 500),
'applied_member_id' => 0,
'update_time' => $now,
];
if ($pending) {
if (!$leaseActive) {
$data['attempts'] = 0;
}
if ($existing) {
@@ -318,6 +352,18 @@ class QywxPromotionMemberSchedulerService
->value('id') ?? 0);
}
/** @return list<string> */
private static function linkRangeUserIds(int $linkId): array
{
$json = (string) (Db::name('qywx_promotion_link')->where('id', $linkId)->value('range_user_json') ?? '[]');
$decoded = json_decode($json, true);
return array_values(array_filter(array_map(
static fn (mixed $value): string => trim((string) $value),
is_array($decoded) ? $decoded : []
), static fn (string $value): bool => $value !== ''));
}
/** @param list<array<string,mixed>> $members */
private static function persistMemberCursors(array $members, int $now): void
{
@@ -7,7 +7,7 @@ namespace app\common\service\qywx;
use RuntimeException;
use think\facade\Db;
/** 把回调调度出的单个目标成员同步到同一条企业微信官方获客链接。 */
/** 把当前全部可用医助同步到同一条企业微信官方获客链接,由企微执行原生多人路由。 */
class QywxPromotionRangeSyncService
{
private QywxCustomerAcquisitionApiService $api;
@@ -27,7 +27,7 @@ class QywxPromotionRangeSyncService
}
$status = (int) ($row['status'] ?? 0);
$now = time();
if ($status === 0 || $status === 4 || ($status === 2 && (int) ($row['lock_until'] ?? 0) > $now)) {
if ($status === 0 || $status === 4 || $status === 5 || ($status === 2 && (int) ($row['lock_until'] ?? 0) > $now)) {
return null;
}
if ($status === 3 && (int) ($row['next_retry'] ?? 0) > $now) {
@@ -52,34 +52,55 @@ class QywxPromotionRangeSyncService
$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('分流方案、官方链接或目标成员已失效');
$members = Db::name('qywx_promotion_pool_member')
->where('pool_id', $poolId)->whereNull('delete_time')->order('id', 'asc')->select()->toArray();
if (!$pool || !$link || $members === []) {
throw new RuntimeException('分流方案、官方链接或方案成员已失效');
}
$remoteLinkId = trim((string) ($link['remote_link_id'] ?? ''));
$userId = trim((string) ($member['userid'] ?? ''));
if ($remoteLinkId === '' || $userId === '') {
throw new RuntimeException('官方链接 ID 或目标成员 userid 为空');
if ($remoteLinkId === '') {
throw new RuntimeException('官方链接 ID 为空');
}
$range = QywxPromotionMemberRange::evaluate($members, date('Y-m-d'), time());
$desiredUserIds = $range['userids'];
if ($desiredUserIds === []) {
$message = '所有成员均已禁用、未生效或达到今日上限;企业微信官方链接至少需要保留一名成员';
Db::name('qywx_promotion_range_sync')
->where('pool_id', $poolId)->where('lock_token', $token)->update([
'status' => 4,
'desired_member_id' => 0,
'applied_member_id' => 0,
'next_retry' => strtotime('tomorrow'),
'attempts' => 0,
'lock_token' => '',
'lock_until' => 0,
'last_error' => $message,
'update_time' => time(),
]);
Db::name('qywx_promotion_link')->where('id', (int) $link['id'])->update([
'sync_error' => $message,
'update_time' => time(),
]);
return ['status' => 'blocked', 'pool_id' => $poolId, 'member_id' => 0];
}
$this->api->updateLink([
'link_id' => $remoteLinkId,
'link_name' => mb_substr((string) ($pool['name'] ?? '获客分流方案'), 0, 30),
'range' => ['user_list' => [$userId]],
'range' => ['user_list' => $desiredUserIds],
'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('企业微信返回的成员范围与待同步成员不一致');
if (!QywxPromotionMemberRange::same($actualUserIds, $desiredUserIds)) {
throw new RuntimeException('企业微信返回的多人路由成员范围与方案可用医助不一致');
}
$url = $remote['url'];
$snapshot = json_encode($remote['snapshot'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@@ -101,7 +122,8 @@ class QywxPromotionRangeSyncService
->where('lock_token', $token)
->update([
'status' => $freshVersion === $desiredVersion ? 0 : 1,
'applied_member_id' => $memberId,
'desired_member_id' => 0,
'applied_member_id' => 0,
'applied_version' => $desiredVersion,
'next_retry' => $freshVersion === $desiredVersion ? 0 : time(),
'attempts' => 0,
@@ -111,7 +133,7 @@ class QywxPromotionRangeSyncService
'update_time' => time(),
]);
return ['status' => 'synced', 'pool_id' => $poolId, 'member_id' => $memberId];
return ['status' => 'synced', 'pool_id' => $poolId, 'member_id' => 0];
} catch (\Throwable $e) {
$attempts = max(1, (int) ($claim['attempts'] ?? 0) + 1);
Db::name('qywx_promotion_range_sync')
@@ -136,16 +158,21 @@ class QywxPromotionRangeSyncService
/** @return array{selected:int,synced:int,failed:int} */
public function syncPending(int $limit = 100): array
{
// 每分钟分页重算全部方案,避免有效期、跨日上限变化漏同步,也避免方案数超过批次后饥饿。
$lastPoolId = 0;
do {
$reconcilePoolIds = Db::name('qywx_promotion_range_sync')
->where('pool_id', '>', $lastPoolId)
->order('pool_id', 'asc')
->limit(500)
->column('pool_id');
foreach ($reconcilePoolIds as $reconcilePoolId) {
$lastPoolId = (int) $reconcilePoolId;
QywxPromotionMemberSchedulerService::reconcilePool($lastPoolId);
}
} while (count($reconcilePoolIds) === 500);
$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)
@@ -1,83 +0,0 @@
<?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;
}
}
+8 -8
View File
@@ -30,15 +30,15 @@ HOST = "https://公开访问域名"
- 更新获客链接 `update_link`
- 删除获客链接 `delete_link`
当前管理端按“一个分流方案对应一个官方获客链接”管理。删除分流方案只软删除本地记录,不调用企业微信 `delete_link`,因此不会破坏已有客户归因;官方永久删除接口仅保留给兼容接口使用,调用后无法恢复
当前管理端按“一个分流方案对应一个官方获客链接”管理。删除方案时先调用企业微信 `delete_link` 永久删除关联的官方链接,成功后再软删除本地方案、成员和链接记录;如果企业微信删除失败,本地方案会保留并返回错误,避免两端状态不一致。官方链接删除后无法恢复,已投放 URL 会失效,历史本地客户归因记录仍保留
获客成员来自后台管理员的 `work_wechat_userid`。管理员可管理全量;组长、医助等账号只返回 `DataScopeService` 当前角色与部门范围内的成员。同步远端链接时,非全量账号只导入 `range.user_list` 与其可见成员有交集的数据;企业微信部门 ID 尚未建立本地映射时按安全原则隐藏,不会越权放行。
`list_link` 只返回当前获客助手可调用应用通过 API 创建的官方链接。后台历史手工粘贴的 `work.weixin.qq.com/ca/...` 链接,以及其他应用创建的链接,不会出现在当前应用的同步列表中,也无法仅凭 URL 反查为官方 `link_id`。需要官方客户、统计和消息归因时,应在本页面创建分流方案并选择获客成员。
创建分流方案时必须选择一名或多名医助。所有医助分别保存启用状态、权重、每日上限、有效时间和实际获客计数,但一个方案只创建一条企业微信官方链接。官方链接当前`range.user_list` 只放调度器选中的一名成员,回调确认实际承接结果后再按权重随机抽取下一名并更新同一个 `link_id`,因此对外 URL 始终不变
创建分流方案时必须选择一名或多名医助。一个方案只创建一条企业微信官方链接,当前全部可用医助会同时写入该链接`range.user_list`,由企业微信在打开、添加阶段执行官方多人路由。成员可配置启用状态、每日上限和有效时间,系统按实际获客回调累计数量
新建方案时的首名成员也会从所选医助中随机产生;编辑已有方案时,如果当前成员仍可用则保持不变,避免无获客事件时无故切换
企业微信 `range.user_list` 只接受成员 userid 列表,不提供逐成员权重字段,因此本站不再展示或执行 2:1:1 一类权重规则。官方多人路由的实际承接还会受到成员可服务状态、客户已有好友关系等企业微信规则影响,不能承诺每次刷新严格随机或短期样本绝对平均
系统只使用企业微信获客助手生成的链接:
@@ -56,7 +56,7 @@ https://work.weixin.qq.com/ca/xxxxxxxx?customer_channel=zyt_pool:123
其中 `customer_channel` 是本站写入的自定义渠道值,格式为 `zyt_pool:分流方案ID`;它与示例中的 `qywx_ca:...` 作用相同,但命名空间和数值由各系统自行定义。
## 回调驱动成员调度
## 回调统计与成员范围维护
部署时必须执行:
@@ -70,11 +70,11 @@ server/sql/1.9.20260824/upgrade_qywx_promotion_member_dispatch.sql
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` 作为补偿。方案、成员和客户组合使用唯一幂等键,同一实际获客不会因重复回调重复计数。
调优先使用 `change_external_contact/add_external_contact` 事件中的 `State``UserID``ExternalUserID``State` 来自主链接的 `customer_channel=zyt_pool:方案ID`,因此可以定位方案及实际承接医助;获客会话回调会通过 `ChatKey → get_chat_info` 作为补偿。方案和客户组合使用唯一幂等键,同一实际获客不会因重复回调或后续更换跟进成员而重复计数。
每次确认实际承接后,系统执行加权随机抽取:权重越大,被抽中的概率越高;禁用、尚未生效、已过期或达到今日上限的成员不会进入随机池。随机允许连续抽中同一成员。待同步范围由 `qywx:sync-promotion-ranges` 每分钟重试,管理端主动禁用当前成员时也会立即尝试同步。
创建或编辑方案时,系统把所有已启用、已生效且未达到今日上限的成员一次写入同一个官方链接。禁用、尚未生效、已过期或达到今日上限的成员会从官方范围移出;跨日或重新进入有效期后会自动加入。`qywx:sync-promotion-ranges` 每分钟重算全部方案并重试失败同步,管理端修改成员规则时也会立即尝试同步。
权重和数量属于回调驱动的近实时控制,并非点击前的强事务:多个客户在企微回调或 `update_link` 生效前并发访问时,可能仍由同一成员承接;所有成员都达到上限时,企业微信不允许把 `range.user_list` 更新为空,系统会标记“无可用成员”并保留最后一次有效范围。因此数量上限用于自动退出后续调度,不承诺并发场景下绝对零超量。
每日数量属于回调驱动的近实时软上限,并非点击前的强事务:多个客户在企微回调或 `update_link` 生效前并发访问时,可能出现少量超量;所有成员都达到上限时,企业微信不允许把 `range.user_list` 更新为空,系统会标记“无可用成员”并保留最后一次有效范围。因此上限用于自动退出后续官方路由,不承诺并发场景下绝对零超量。
如果需要为点击 IP 生成不可逆服务端哈希,可在 `[qywx_promotion]` 下额外设置独立的 `CREDENTIAL_KEY`
@@ -124,4 +124,4 @@ window.WecomPromotion['分流方案KEY'].destroy()
`/go` 兼容入口记录点击来源时,只保存页面的 origin 与 pathname,不包含查询参数或 fragment。推广页路径中也不应放置手机号、患者 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-BRBhqisw.js";import{o,q as r,r as a,v as n,D as c,s}from"./.pnpm-Dwh6tNxD.js";import"./index-CLwe6ftX.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-BRBhqisw.js";import{o,q as r,r as t,v as s}from"./.pnpm-Dwh6tNxD.js";import"./index-CLwe6ftX.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-CD6XYjKU.js";import{m as A,_ as M}from"./index-CLwe6ftX.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-BzzBMfBw.js";import{_ as q}from"./index-CLwe6ftX.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-BzzBMfBw.js";import{_ as q}from"./index-CLwe6ftX.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-CMHUdU8f.js";import{U as k}from"./index-BWeecgSM.js";import{i as c,_ as q}from"./index-CLwe6ftX.js";import{aj as K,ak as x,al as A}from"./tcm-BzzBMfBw.js";import"./RecordingVideoPlayer-CDqcv1v9.js";import"./file-CAEOOa9_.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-BzzBMfBw.js";import{_ as H}from"./index-CLwe6ftX.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
@@ -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};
@@ -0,0 +1 @@
import{_ as o}from"./GancaoSubmissionReconcileButton.vue_vue_type_script_setup_true_lang-hLNJ9AIP.js";import"./.pnpm-Dwh6tNxD.js";import"./tcm-BzzBMfBw.js";import"./index-CLwe6ftX.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-BzzBMfBw.js";import{i as C}from"./index-CLwe6ftX.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-BzzBMfBw.js";import{p as re}from"./im-business-message-parse-CVnz1EnV.js";import{_ as le}from"./index-CLwe6ftX.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-CLwe6ftX.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-Dr1-lH_q.js";import{e as ve,c as _e,i as f,_ as ye}from"./index-CLwe6ftX.js";import{a as T,d as he}from"./patient-4QJ3YgMJ.js";import{h as ke}from"./perm-CHalL7pT.js";import"./index-De13kk0O.js";import"./index-BhX1mXHA.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-DUJcOhOZ.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.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};
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-CLwe6ftX.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-CDqcv1v9.js";import{e as I,_ as P}from"./index-CLwe6ftX.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]);
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-CLwe6ftX.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-BzzBMfBw.js";import{i as M,_ as S}from"./index-CLwe6ftX.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};
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
@@ -0,0 +1 @@
import{_ as o}from"./account-adjust.vue_vue_type_script_setup_true_lang-D8V2sQ39.js";import"./.pnpm-Dwh6tNxD.js";import"./index-De13kk0O.js";import"./index-CLwe6ftX.js";export{o as default};
@@ -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};
@@ -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-De13kk0O.js";import{i as V}from"./index-CLwe6ftX.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-Cd2ygMrI.js";import"./.pnpm-Dwh6tNxD.js";import"./index-DUJcOhOZ.js";import"./index-CLwe6ftX.js";import"./picker-DYZuEtRo.js";import"./index-De13kk0O.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-Cg76y5pa.js";import"./usePaging-DOuAwzL9.js";import"./picker-Dr1-lH_q.js";import"./index-BhX1mXHA.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.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-DUJcOhOZ.js";import{_ as F}from"./picker-DYZuEtRo.js";import{_ as K}from"./picker-Dr1-lH_q.js";import{c as O,i as r}from"./index-CLwe6ftX.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-CLwe6ftX.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-CLwe6ftX.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-CLwe6ftX.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-CVCkEkKY.js";import"./.pnpm-Dwh6tNxD.js";import"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";import"./picker-Dr1-lH_q.js";import"./index-De13kk0O.js";import"./index-CLwe6ftX.js";import"./index-BhX1mXHA.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-DUJcOhOZ.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.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-DiYI-N-j.js";import"./.pnpm-Dwh6tNxD.js";import"./picker-Dr1-lH_q.js";import"./index-De13kk0O.js";import"./index-CLwe6ftX.js";import"./index-BhX1mXHA.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./index-DUJcOhOZ.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./usePaging-DOuAwzL9.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-SwwwDk4U.js";import"./.pnpm-Dwh6tNxD.js";import"./add-nav.vue_vue_type_script_setup_true_lang-Cd2ygMrI.js";import"./index-DUJcOhOZ.js";import"./index-CLwe6ftX.js";import"./picker-DYZuEtRo.js";import"./index-De13kk0O.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-Cg76y5pa.js";import"./usePaging-DOuAwzL9.js";import"./picker-Dr1-lH_q.js";import"./index-BhX1mXHA.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.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-C2wYb6Py.js";import"./.pnpm-Dwh6tNxD.js";import"./index-DUJcOhOZ.js";import"./index-CLwe6ftX.js";import"./picker-DYZuEtRo.js";import"./index-De13kk0O.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-Cg76y5pa.js";import"./usePaging-DOuAwzL9.js";import"./picker-Dr1-lH_q.js";import"./index-BhX1mXHA.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.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};
@@ -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-D91XaciT.js";import"./.pnpm-Dwh6tNxD.js";import"./index-DUJcOhOZ.js";import"./index-CLwe6ftX.js";import"./picker-DYZuEtRo.js";import"./index-De13kk0O.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-Cg76y5pa.js";import"./usePaging-DOuAwzL9.js";import"./picker-Dr1-lH_q.js";import"./index-BhX1mXHA.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.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-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};
@@ -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-CbLdZhC4.js";import"./index-DUJcOhOZ.js";import"./index-CLwe6ftX.js";import"./picker-DYZuEtRo.js";import"./index-De13kk0O.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-Cg76y5pa.js";import"./usePaging-DOuAwzL9.js";import"./picker-Dr1-lH_q.js";import"./index-BhX1mXHA.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.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};
@@ -0,0 +1 @@
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-C54j8yNY.js";import"./.pnpm-Dwh6tNxD.js";import"./add-nav.vue_vue_type_script_setup_true_lang-Cd2ygMrI.js";import"./index-DUJcOhOZ.js";import"./index-CLwe6ftX.js";import"./picker-DYZuEtRo.js";import"./index-De13kk0O.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-Cg76y5pa.js";import"./usePaging-DOuAwzL9.js";import"./picker-Dr1-lH_q.js";import"./index-BhX1mXHA.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.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.vue_vue_type_script_setup_true_lang-P81rWJz8.js";import"./.pnpm-Dwh6tNxD.js";import"./index-DUJcOhOZ.js";import"./index-CLwe6ftX.js";import"./picker-DYZuEtRo.js";import"./index-De13kk0O.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-Cg76y5pa.js";import"./usePaging-DOuAwzL9.js";import"./picker-Dr1-lH_q.js";import"./index-BhX1mXHA.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.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};
@@ -0,0 +1 @@
import{_ as o}from"./attr-setting.vue_vue_type_script_setup_true_lang-LzhUJdSI.js";import"./.pnpm-Dwh6tNxD.js";import"./index-CBMqSINZ.js";import"./attr-CbkM9kCH.js";import"./index-DUJcOhOZ.js";import"./index-CLwe6ftX.js";import"./picker-DYZuEtRo.js";import"./index-De13kk0O.js";import"./index.vue_vue_type_script_setup_true_lang-D0KAQf1t.js";import"./article-Cg76y5pa.js";import"./usePaging-DOuAwzL9.js";import"./picker-Dr1-lH_q.js";import"./index-BhX1mXHA.js";import"./index-BWeecgSM.js";import"./file-CAEOOa9_.js";import"./index.vue_vue_type_script_setup_true_lang-Cxx3Fcm2.js";import"./content.vue_vue_type_script_setup_true_lang-DsMojqSz.js";import"./decoration-img-CreeGOM7.js";import"./attr.vue_vue_type_script_setup_true_lang-DiYI-N-j.js";import"./content-CEh186sK.js";import"./attr.vue_vue_type_script_setup_true_lang-P81rWJz8.js";import"./content.vue_vue_type_script_setup_true_lang-BH3MxCg3.js";import"./attr.vue_vue_type_script_setup_true_lang-C54j8yNY.js";import"./add-nav.vue_vue_type_script_setup_true_lang-Cd2ygMrI.js";import"./content-CKBOYLUz.js";import"./attr.vue_vue_type_script_setup_true_lang-SwwwDk4U.js";import"./content.vue_vue_type_script_setup_true_lang-HA8Rdikn.js";import"./attr.vue_vue_type_script_setup_true_lang--WwiPUCL.js";import"./content-CqqgaESf.js";import"./decoration-CUkBWWKw.js";import"./attr.vue_vue_type_script_setup_true_lang-CVCkEkKY.js";import"./index.vue_vue_type_script_setup_true_lang-Cq5qrGbU.js";import"./content-DMHMygLe.js";import"./content.vue_vue_type_script_setup_true_lang-DOCZLuaJ.js";import"./attr.vue_vue_type_script_setup_true_lang-B4PfotVc.js";import"./content-C1DSVUoW.js";import"./attr.vue_vue_type_script_setup_true_lang-D91XaciT.js";import"./content.vue_vue_type_script_setup_true_lang-ANIdMeLo.js";import"./attr.vue_vue_type_script_setup_true_lang-BJHLs4e8.js";import"./content-DQ0iz8i7.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-CBMqSINZ.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 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-DUJcOhOZ.js";import{_ as X}from"./picker-DYZuEtRo.js";import{_ as Y}from"./picker-Dr1-lH_q.js";import{c as Z,i as b}from"./index-CLwe6ftX.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-Cd2ygMrI.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 +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-Dr1-lH_q.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-DUJcOhOZ.js";import{c as T,i as v}from"./index-CLwe6ftX.js";import{_ as G}from"./picker-DYZuEtRo.js";import{_ as H}from"./picker-Dr1-lH_q.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 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-Dr1-lH_q.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 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-DUJcOhOZ.js";import{c as T,i as k}from"./index-CLwe6ftX.js";import{_ as G}from"./picker-DYZuEtRo.js";import{_ as H}from"./picker-Dr1-lH_q.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-Cd2ygMrI.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 +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-DKmWACZL.js";import"./.pnpm-Dwh6tNxD.js";import"./menu-B3ojZv5U.js";import"./index-CLwe6ftX.js";import"./role-CSB0a6yH.js";import"./index-De13kk0O.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-B3ojZv5U.js";import{a as Z}from"./role-CSB0a6yH.js";import{_ as $}from"./index-De13kk0O.js";import{x as ee}from"./index-CLwe6ftX.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-CLwe6ftX.js";import{_ as Z}from"./index.vue_vue_type_script_setup_true_lang-C9hPqHM6.js";import{g as ee,h as te}from"./finance-CZySKYK6.js";import{u as ae}from"./useDictOptions-pgjHnp9X.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};

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