227 lines
7.8 KiB
Vue
227 lines
7.8 KiB
Vue
<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>
|
||
<view class="action-btn primary" @click="goCommission(item)">分成与结算</view>
|
||
<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>
|