2026-06-09 17:02:13 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<page-layout :is-platform="ctx.isPlatform" title="推广员管理" :show-back="true">
|
|
|
|
|
|
<view class="page">
|
|
|
|
|
|
<view v-if="ctx.isPlatform" class="filter-panel">
|
|
|
|
|
|
<input class="filter-input" v-model="filters.store_keyword" placeholder="诊所名称" />
|
|
|
|
|
|
<input class="filter-input" v-model="filters.nick_name" placeholder="推广员名称" />
|
|
|
|
|
|
<input class="filter-input" v-model="filters.phone" placeholder="手机号" />
|
|
|
|
|
|
<view class="filter-btn" @click="reload">查询</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view v-if="!ctx.isPlatform" class="see-price-row">
|
|
|
|
|
|
<text>推广员可见价格</text>
|
|
|
|
|
|
<switch :checked="seePrice === 1" @change="onToggleSeePrice" color="#6ACDBB" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="add-bar">
|
|
|
|
|
|
<view class="add-btn" @click="goCreate">+ 新增推广员</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view v-if="loading" class="loading-tip">加载中...</view>
|
|
|
|
|
|
<view v-else-if="!list.length" class="empty">暂无推广员</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view v-for="item in list" :key="item.id" class="card">
|
|
|
|
|
|
<view class="card-head">
|
|
|
|
|
|
<view>
|
|
|
|
|
|
<text class="name">{{ item.nick_name || '未知' }}</text>
|
|
|
|
|
|
<text v-if="item.store_name" class="store">{{ item.store_name }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="tags">
|
|
|
|
|
|
<text class="tag">{{ splitLabel(item.split_type) }}</text>
|
|
|
|
|
|
<text v-if="Number(item.tcm_split) > 0" class="tag tcm">中药 {{ item.tcm_split }}%</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="card-body">
|
|
|
|
|
|
<text class="phone">{{ item.phone }}</text>
|
|
|
|
|
|
<view class="stat-row">
|
|
|
|
|
|
<text>累计获客</text>
|
|
|
|
|
|
<text class="num">{{ (item.count && item.count.user_number) || 0 }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="stat-row">
|
|
|
|
|
|
<text>待结算</text>
|
|
|
|
|
|
<text class="num pending">¥{{ (item.count && item.count.pending_amount) || '0.00' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="stat-row">
|
|
|
|
|
|
<text>后台账号</text>
|
|
|
|
|
|
<text :class="item.has_admin_account ? 'ok' : 'warn'">{{ item.has_admin_account ? '已开通' : '未开通' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="actions">
|
|
|
|
|
|
<view class="action-btn" @click="goEdit(item)">编辑</view>
|
2026-07-13 12:55:07 +08:00
|
|
|
|
<view class="action-btn primary" @click="goCommission(item)">服务费与结算</view>
|
2026-06-09 17:02:13 +08:00
|
|
|
|
<view v-if="item.qr_code && item.qr_code.qr_code" class="action-btn" @click="openQr(item)">二维码</view>
|
|
|
|
|
|
<view class="action-btn" @click="goLeads(item)">获客</view>
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-if="!item.has_admin_account"
|
|
|
|
|
|
class="action-btn warn"
|
|
|
|
|
|
@click="openAdminAccount(item)"
|
|
|
|
|
|
>开通后台</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</page-layout>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import PageLayout from './components/PageLayout.vue';
|
|
|
|
|
|
import { getSalespersonManageContext, resolveManageMode } from './common/context.js';
|
|
|
|
|
|
import { splitTypeLabel } from './common/labels.js';
|
|
|
|
|
|
import { openQrCodePreview } from '@/utils/qrCodePreview.js';
|
|
|
|
|
|
|
|
|
|
|
|
const ROOT = '/subPackages/sub_salesperson_manage';
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
components: { PageLayout },
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
ctx: getSalespersonManageContext('clinic'),
|
|
|
|
|
|
filters: { store_keyword: '', nick_name: '', phone: '' },
|
|
|
|
|
|
presetStoreId: 0,
|
|
|
|
|
|
list: [],
|
|
|
|
|
|
loading: false,
|
|
|
|
|
|
seePrice: 0,
|
|
|
|
|
|
openingId: 0,
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
onLoad(options) {
|
|
|
|
|
|
this.ctx = getSalespersonManageContext(resolveManageMode(options));
|
|
|
|
|
|
this.presetStoreId = Number(options.store_id || 0);
|
|
|
|
|
|
if (this.presetStoreId > 0) {
|
|
|
|
|
|
this.filters.store_keyword = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
onShow() {
|
|
|
|
|
|
this.reload();
|
|
|
|
|
|
if (!this.ctx.isPlatform) this.loadSeePrice();
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
splitLabel: splitTypeLabel,
|
|
|
|
|
|
async reload() {
|
|
|
|
|
|
this.loading = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
pageSize: 50,
|
|
|
|
|
|
...this.filters,
|
|
|
|
|
|
};
|
|
|
|
|
|
if (this.presetStoreId > 0) params.store_id = this.presetStoreId;
|
|
|
|
|
|
const wrap = await this.ctx.api.getList(params);
|
|
|
|
|
|
this.list = (wrap && wrap.ok && wrap.data && wrap.data.items) ? wrap.data.items : [];
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
async loadSeePrice() {
|
|
|
|
|
|
const wrap = await this.ctx.api.getSeePriceDetail();
|
|
|
|
|
|
if (wrap && wrap.ok && wrap.data) {
|
|
|
|
|
|
this.seePrice = Number(wrap.data.see_price) || 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
async onToggleSeePrice(e) {
|
|
|
|
|
|
const wrap = await this.ctx.api.toggleSeePrice();
|
|
|
|
|
|
if (wrap && wrap.ok && wrap.data) {
|
|
|
|
|
|
this.seePrice = Number(wrap.data.see_price) || 0;
|
|
|
|
|
|
uni.showToast({ title: this.seePrice === 1 ? '已开启可见价格' : '已关闭可见价格', icon: 'none' });
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
goCreate() {
|
|
|
|
|
|
uni.navigateTo({ url: `${ROOT}/form?mode=${this.ctx.mode}` });
|
|
|
|
|
|
},
|
|
|
|
|
|
goEdit(item) {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `${ROOT}/form?mode=${this.ctx.mode}&id=${item.id}&store_id=${(item.qr_code && item.qr_code.store_id) || ''}`,
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
goCommission(item) {
|
|
|
|
|
|
const storeId = (item.qr_code && item.qr_code.store_id) || '';
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `${ROOT}/commission/index?mode=${this.ctx.mode}&salesperson_id=${item.id}&nick_name=${encodeURIComponent(item.nick_name || '')}&store_id=${storeId}`,
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
goLeads(item) {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `${ROOT}/leads/index?mode=${this.ctx.mode}&salesperson_id=${item.id}&nick_name=${encodeURIComponent(item.nick_name || '')}`,
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
openQr(item) {
|
|
|
|
|
|
openQrCodePreview({
|
|
|
|
|
|
type: 'salesperson',
|
|
|
|
|
|
title: item.nick_name || '推广员二维码',
|
|
|
|
|
|
payload: {
|
|
|
|
|
|
nick_name: item.nick_name || '',
|
|
|
|
|
|
qr_code: {
|
|
|
|
|
|
qr_code: (item.qr_code && item.qr_code.qr_code) || '',
|
|
|
|
|
|
store: { name: item.store_name || '' },
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
async openAdminAccount(item) {
|
|
|
|
|
|
if (this.openingId) return;
|
|
|
|
|
|
this.openingId = item.id;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const wrap = await this.ctx.api.openAdminAccount({
|
|
|
|
|
|
salesperson_id: item.id,
|
|
|
|
|
|
store_id: item.qr_code && item.qr_code.store_id,
|
|
|
|
|
|
});
|
|
|
|
|
|
if (wrap && wrap.ok) {
|
|
|
|
|
|
uni.showModal({
|
|
|
|
|
|
title: '开通成功',
|
|
|
|
|
|
content: '默认密码:Xk123456@',
|
|
|
|
|
|
showCancel: false,
|
|
|
|
|
|
});
|
|
|
|
|
|
this.reload();
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.openingId = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.page { padding: 24rpx; padding-bottom: 48rpx; }
|
|
|
|
|
|
.filter-panel { display: flex; flex-wrap: wrap; gap: 12rpx; margin-bottom: 20rpx; }
|
|
|
|
|
|
.filter-input {
|
|
|
|
|
|
flex: 1; min-width: 200rpx; background: #fff; border-radius: 12rpx;
|
|
|
|
|
|
padding: 16rpx 20rpx; font-size: 26rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-btn {
|
|
|
|
|
|
background: #6ACDBB; color: #fff; padding: 16rpx 32rpx; border-radius: 12rpx; font-size: 26rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.see-price-row {
|
|
|
|
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
|
|
|
|
background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 20rpx; font-size: 28rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.add-bar { margin-bottom: 20rpx; }
|
|
|
|
|
|
.add-btn {
|
|
|
|
|
|
background: linear-gradient(90deg, #00BFA6, #6ACDBB); color: #fff;
|
|
|
|
|
|
text-align: center; padding: 20rpx; border-radius: 16rpx; font-size: 28rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.loading-tip, .empty { text-align: center; color: #999; padding: 80rpx 0; }
|
|
|
|
|
|
.card {
|
|
|
|
|
|
background: #fff; border-radius: 20rpx; padding: 28rpx; margin-bottom: 20rpx;
|
|
|
|
|
|
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
.card-head { display: flex; justify-content: space-between; margin-bottom: 16rpx; }
|
|
|
|
|
|
.name { display: block; font-size: 32rpx; font-weight: 600; }
|
|
|
|
|
|
.store { font-size: 24rpx; color: #999; }
|
|
|
|
|
|
.tags { display: flex; flex-direction: column; gap: 8rpx; align-items: flex-end; }
|
|
|
|
|
|
.tag { font-size: 22rpx; background: #EFF6FF; color: #3B82F6; padding: 4rpx 12rpx; border-radius: 8rpx; }
|
|
|
|
|
|
.tag.tcm { background: #F0FDF4; color: #22C55E; }
|
|
|
|
|
|
.phone { font-size: 26rpx; color: #666; display: block; margin-bottom: 12rpx; }
|
|
|
|
|
|
.stat-row { display: flex; justify-content: space-between; font-size: 26rpx; margin-bottom: 8rpx; }
|
|
|
|
|
|
.num { font-weight: 600; }
|
|
|
|
|
|
.pending { color: #F97316; }
|
|
|
|
|
|
.ok { color: #22C55E; }
|
|
|
|
|
|
.warn { color: #EF4444; }
|
|
|
|
|
|
.actions { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 20rpx; }
|
|
|
|
|
|
.action-btn {
|
|
|
|
|
|
font-size: 24rpx; padding: 10rpx 20rpx; border-radius: 10rpx;
|
|
|
|
|
|
background: #f5f5f5; color: #333;
|
|
|
|
|
|
}
|
|
|
|
|
|
.action-btn.primary { background: #6ACDBB; color: #fff; }
|
|
|
|
|
|
.action-btn.warn { background: #FEF3C7; color: #D97706; }
|
|
|
|
|
|
</style>
|