1. 特色方功能迭代
This commit is contained in:
@@ -6,4 +6,5 @@ alwaysApply: true
|
||||
1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则
|
||||
2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于2行
|
||||
3. 有封装好的方法、组件需要复用,不要重复造轮子
|
||||
4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面
|
||||
4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show)
|
||||
5. 数据库的(created_at、updated_at、deleted_at)统一使用时间戳,不要使用字符串,并且我在查询器中一级格式化成字符串了,无需再次格式化
|
||||
|
||||
@@ -69,3 +69,8 @@ export function getClinicSalespersonTransferPrescriptionList(params) {
|
||||
export function getClinicSalespersonTransferPrescriptionDetail(id) {
|
||||
return clinicSalespersonRequest({ url: `${PREFIX}/transfer-prescription-detail`, method: 'GET', data: { id } });
|
||||
}
|
||||
|
||||
/** 我邀请的下级推广员列表 */
|
||||
export function getClinicSalespersonInvitedList(params) {
|
||||
return clinicSalespersonRequest({ url: `${PREFIX}/invited-salesperson-list`, method: 'GET', data: params });
|
||||
}
|
||||
|
||||
@@ -107,6 +107,14 @@ export function createSalespersonManageApi(mode) {
|
||||
return request({ url: `${prefix}/user-bind-list`, method: 'GET', data: params });
|
||||
},
|
||||
|
||||
getSalespersonOptions(params) {
|
||||
return request({ url: `${prefix}/salesperson-options`, method: 'GET', data: params });
|
||||
},
|
||||
|
||||
getInvitedSalespersonList(params) {
|
||||
return request({ url: `${prefix}/invited-salesperson-list`, method: 'GET', data: params });
|
||||
},
|
||||
|
||||
toggleSeePrice(storeId) {
|
||||
const configPrefix = isPlatform ? PLATFORM_CONFIG_PREFIX : CLINIC_CONFIG_PREFIX;
|
||||
return configRequest({
|
||||
|
||||
22
pages.json
22
pages.json
@@ -517,11 +517,25 @@
|
||||
}, {
|
||||
"root": "subPackages/sub_clinic_salesperson",
|
||||
"pages": [
|
||||
{ "path": "home/index", "style": { "navigationBarTitleText": "工作台", "navigationStyle": "custom" } },
|
||||
{
|
||||
"path": "home/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "工作台",
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {
|
||||
"transfer-patient-drawer": "/subPackages/sub_workbench/prescription_v2/components/modals/TransferPatientDrawer"
|
||||
},
|
||||
"componentPlaceholder": {
|
||||
"transfer-patient-drawer": "view"
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "path": "my/index", "style": { "navigationBarTitleText": "我的", "navigationStyle": "custom" } },
|
||||
{ "path": "earnings/index", "style": { "navigationBarTitleText": "我的收益", "navigationStyle": "custom" } },
|
||||
{ "path": "leads/index", "style": { "navigationBarTitleText": "获客记录", "navigationStyle": "custom" } },
|
||||
{ "path": "commission/index", "style": { "navigationBarTitleText": "分成记录", "navigationStyle": "custom" } },
|
||||
{ "path": "invitees/index", "style": { "navigationBarTitleText": "我邀请的推广员", "navigationStyle": "custom", "enablePullDownRefresh": true } },
|
||||
{ "path": "invitees/detail", "style": { "navigationBarTitleText": "收益明细", "navigationStyle": "custom" } },
|
||||
{ "path": "settlement/index", "style": { "navigationBarTitleText": "结算记录", "navigationStyle": "custom" } },
|
||||
{ "path": "transfer-prescription/form", "style": { "navigationBarTitleText": "传方", "navigationStyle": "custom" } },
|
||||
{ "path": "transfer-prescription/list", "style": { "navigationBarTitleText": "传方记录", "navigationStyle": "custom" } }
|
||||
@@ -589,5 +603,11 @@
|
||||
"navigationBarBackgroundColor": "#fff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f7f8fa"
|
||||
},
|
||||
"preloadRule": {
|
||||
"subPackages/sub_clinic_salesperson/home/index": {
|
||||
"network": "all",
|
||||
"packages": ["subPackages/sub_workbench"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<view v-for="item in list" :key="item.id" class="row">
|
||||
<view>
|
||||
<text class="order">{{ item.order_no }} <text class="status" style="margin-left: 20rpx">{{ item.settlement_id > 0 ? '已结算' : '待结算' }}</text></text>
|
||||
<text v-if="item.patient_name || item.patient_mobile" class="patient">{{ item.patient_name }} {{ item.patient_mobile }}</text>
|
||||
<text class="drug">{{ item.drug_name }} ×{{ item.qty }}</text>
|
||||
</view>
|
||||
<view class="right">
|
||||
@@ -42,6 +43,7 @@ export default {
|
||||
.empty { text-align: center; color: #999; padding: 80rpx 0; }
|
||||
.row { background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 16rpx; display: flex; justify-content: space-between; }
|
||||
.order { display: block; font-size: 26rpx; font-weight: 600; }
|
||||
.patient { display: block; font-size: 24rpx; color: #666; margin-top: 6rpx; }
|
||||
.drug { font-size: 24rpx; color: #666; }
|
||||
.right { text-align: right; }
|
||||
.amount { display: block; font-size: 28rpx; color: #4db6a8; font-weight: 600; }
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<page-container
|
||||
v-if="visible"
|
||||
:show="visible"
|
||||
:overlay="false"
|
||||
@leave="close"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<u-popup
|
||||
v-model="visible"
|
||||
mode="bottom"
|
||||
border-radius="24"
|
||||
height="80%"
|
||||
z-index="10080"
|
||||
@close="close"
|
||||
>
|
||||
<view class="drawer">
|
||||
<view class="header">
|
||||
<text class="title">传方详情</text>
|
||||
<u-icon name="close" size="36" @click="close"></u-icon>
|
||||
</view>
|
||||
<scroll-view scroll-y class="body">
|
||||
<view v-if="loading" class="tip">加载中...</view>
|
||||
<template v-else-if="detail">
|
||||
<view class="section">
|
||||
<view class="row"><text class="label">状态</text><u-tag :text="detail.status_text" size="mini" :type="statusType" /></view>
|
||||
<view class="row"><text class="label">方式</text><text>{{ detail.transfer_mode_text }}</text></view>
|
||||
<view class="row"><text class="label">收货人</text><text>{{ detail.patient_name }} {{ detail.patient_mobile }}</text></view>
|
||||
<view class="row"><text class="label">地址</text><text>{{ detail.patient_address || '—' }}</text></view>
|
||||
<view v-if="detail.remark" class="row"><text class="label">备注</text><text>{{ detail.remark }}</text></view>
|
||||
<view v-if="detail.status === 3 && detail.exception_message" class="row exception">
|
||||
<text class="label">异常</text><text>{{ detail.exception_message }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="detail.drug_text_lines && detail.drug_text_lines.length" class="section">
|
||||
<text class="section-title">药方</text>
|
||||
<text v-for="(line, i) in detail.drug_text_lines" :key="i" class="drug-line">{{ line }}</text>
|
||||
</view>
|
||||
<view v-if="detail.prescription_images && detail.prescription_images.length" class="section">
|
||||
<text class="section-title">处方图片</text>
|
||||
<view class="img-row">
|
||||
<image
|
||||
v-for="(img, idx) in detail.prescription_images"
|
||||
:key="idx"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
class="thumb"
|
||||
@click="preview(img)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="section">
|
||||
<text class="time">{{ detail.transfer_time_text }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getClinicSalespersonTransferPrescriptionDetail } from '@/api/clinicSalesperson.js'
|
||||
|
||||
/**
|
||||
* 传方详情抽屉
|
||||
*/
|
||||
export default {
|
||||
name: 'TransferDetailDrawer',
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
loading: false,
|
||||
detail: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
statusType() {
|
||||
const map = { 0: 'warning', 1: 'primary', 2: 'success', 3: 'error' }
|
||||
return map[Number(this.detail?.status)] || 'info'
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async open(id) {
|
||||
this.visible = true
|
||||
this.loading = true
|
||||
this.detail = null
|
||||
try {
|
||||
const wrap = await getClinicSalespersonTransferPrescriptionDetail(id)
|
||||
if (wrap && wrap.ok) {
|
||||
this.detail = wrap.data
|
||||
}
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.visible = false
|
||||
},
|
||||
preview(url) {
|
||||
const urls = this.detail?.prescription_images || []
|
||||
uni.previewImage({ urls, current: url })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drawer { height: 100%; display: flex; flex-direction: column; background: #f5f5f5; }
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 28rpx 32rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
.title { font-size: 32rpx; font-weight: 600; }
|
||||
.body { flex: 1; height: 0; padding: 24rpx; }
|
||||
.tip { text-align: center; color: #999; padding: 40rpx; }
|
||||
.section {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.section-title { font-size: 28rpx; font-weight: 600; display: block; margin-bottom: 12rpx; }
|
||||
.row {
|
||||
display: flex;
|
||||
font-size: 26rpx;
|
||||
margin-bottom: 12rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.label { color: #86909c; width: 120rpx; flex-shrink: 0; }
|
||||
.exception .label, .exception text:last-child { color: #ef4444; }
|
||||
.drug-line { display: block; font-size: 26rpx; color: #008771; margin-bottom: 8rpx; }
|
||||
.img-row { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.thumb { width: 160rpx; height: 160rpx; border-radius: 8rpx; }
|
||||
.time { font-size: 24rpx; color: #999; }
|
||||
</style>
|
||||
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<u-popup
|
||||
v-model="visible"
|
||||
mode="center"
|
||||
border-radius="24"
|
||||
width="640rpx"
|
||||
:mask-close-able="true"
|
||||
@close="handleClose"
|
||||
>
|
||||
<view class="mode-modal">
|
||||
<view class="mode-header">
|
||||
<text class="mode-title">选择传方方式</text>
|
||||
<text class="mode-sub">请选择适合您的传方方式</text>
|
||||
</view>
|
||||
<view class="mode-cards">
|
||||
<view class="mode-card photo" hover-class="mode-card-hover" @click="handleSelect('photo')">
|
||||
<view class="mode-icon-wrap photo-bg">
|
||||
<u-icon name="camera-fill" size="48" color="#10B981"></u-icon>
|
||||
</view>
|
||||
<text class="mode-name">拍照一键传方</text>
|
||||
<text class="mode-desc">拍摄处方照片,快速提交</text>
|
||||
</view>
|
||||
<view class="mode-card manual" hover-class="mode-card-hover" @click="handleSelect('manual')">
|
||||
<view class="mode-icon-wrap manual-bg">
|
||||
<u-icon name="edit-pen-fill" size="48" color="#0EA5E9"></u-icon>
|
||||
</view>
|
||||
<text class="mode-name">输入药方</text>
|
||||
<text class="mode-desc">手动选择中药并填写信息</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mode-cancel" @click="handleClose">取消</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TransferModeModal',
|
||||
props: {
|
||||
value: { type: Boolean, default: false },
|
||||
},
|
||||
computed: {
|
||||
visible: {
|
||||
get() { return this.value },
|
||||
set(v) { this.$emit('input', v) },
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.visible = false
|
||||
this.$emit('close')
|
||||
},
|
||||
/** 选择传方方式:photo=拍照 manual=手动输入药方 */
|
||||
handleSelect(mode) {
|
||||
this.$emit('select', mode)
|
||||
this.visible = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mode-modal {
|
||||
padding: 48rpx 40rpx 32rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.mode-header {
|
||||
text-align: center;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
.mode-title {
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #1d2129;
|
||||
}
|
||||
.mode-sub {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #86909c;
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
.mode-cards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
.mode-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 36rpx 32rpx;
|
||||
border-radius: 20rpx;
|
||||
border: 2rpx solid #f0f0f0;
|
||||
background: #fafafa;
|
||||
}
|
||||
.mode-card-hover {
|
||||
opacity: 0.85;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.mode-icon-wrap {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.photo-bg { background: linear-gradient(135deg, #ecfdf5, #d1fae5); }
|
||||
.manual-bg { background: linear-gradient(135deg, #f0f9ff, #e0f2fe); }
|
||||
.mode-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
}
|
||||
.mode-desc {
|
||||
font-size: 24rpx;
|
||||
color: #86909c;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
.mode-cancel {
|
||||
text-align: center;
|
||||
padding: 28rpx;
|
||||
margin-top: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: #86909c;
|
||||
}
|
||||
</style>
|
||||
@@ -88,7 +88,7 @@
|
||||
:key="item.key"
|
||||
class="service-card"
|
||||
hover-class="service-card-hover"
|
||||
@click="go(item.path)"
|
||||
@click="onMenuClick(item)"
|
||||
>
|
||||
<view class="service-icon-box" :style="{ background: item.theme.bg }">
|
||||
<!-- 替换为 uView 动态图标 -->
|
||||
@@ -103,18 +103,28 @@
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<transfer-mode-modal v-model="showTransferModeModal" @select="onTransferModeSelect" />
|
||||
<transfer-patient-drawer
|
||||
ref="photoTransferDrawer"
|
||||
v-model="showPhotoTransferDrawer"
|
||||
:photo-mode="true"
|
||||
:submitting="photoSubmitting"
|
||||
@confirm="handlePhotoTransferConfirm"
|
||||
/>
|
||||
</clinic-salesperson-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||
import { getClinicSalespersonMyInfo } from '@/api/clinicSalesperson.js'
|
||||
import TransferModeModal from '@/subPackages/sub_clinic_salesperson/components/TransferModeModal.vue'
|
||||
import { getClinicSalespersonMyInfo, createClinicSalespersonTransferPrescription } from '@/api/clinicSalesperson.js'
|
||||
import { openQrCodePreview, buildSalespersonQrPayload } from '@/utils/qrCodePreview.js'
|
||||
|
||||
const ROOT = '/subPackages/sub_clinic_salesperson'
|
||||
|
||||
export default {
|
||||
components: { ClinicSalespersonPageLayout },
|
||||
// transfer-patient-drawer 跨分包引用,在 pages.json 通过 usingComponents + componentPlaceholder 注册
|
||||
components: { ClinicSalespersonPageLayout, TransferModeModal },
|
||||
data() {
|
||||
return {
|
||||
displayName: '',
|
||||
@@ -122,11 +132,15 @@ export default {
|
||||
qrCode: '',
|
||||
profileInfo: null,
|
||||
stats: {},
|
||||
showTransferModeModal: false,
|
||||
showPhotoTransferDrawer: false,
|
||||
photoSubmitting: false,
|
||||
menuItems: [
|
||||
// { key: 'transfer', label: '传方', path: '/subPackages/sub_workbench/prescription_v2/index?salesperson_transfer=1&initial_category=1' },
|
||||
// { key: 'transfer-list', label: '传方记录', path: `${ROOT}/transfer-prescription/list` },
|
||||
{ key: 'transfer', label: '传方', action: 'transfer' },
|
||||
{ key: 'transfer-list', label: '传方记录', path: `${ROOT}/transfer-prescription/list` },
|
||||
// { key: 'earnings', label: '我的收益', path: `${ROOT}/earnings/index` },
|
||||
{ key: 'leads', label: '获客记录', path: `${ROOT}/leads/index` },
|
||||
{ key: 'invitees', label: '我邀请的推广员', path: `${ROOT}/invitees/index` },
|
||||
{ key: 'commission', label: '分成记录', path: `${ROOT}/commission/index` },
|
||||
{ key: 'settlement', label: '结算记录', path: `${ROOT}/settlement/index` },
|
||||
],
|
||||
@@ -185,6 +199,55 @@ export default {
|
||||
go(path) {
|
||||
uni.navigateTo({ url: path })
|
||||
},
|
||||
/** 菜单点击:传方走方式选择,其余跳转 */
|
||||
onMenuClick(item) {
|
||||
if (item.action === 'transfer') {
|
||||
this.showTransferModeModal = true
|
||||
return
|
||||
}
|
||||
if (item.path) {
|
||||
this.go(item.path)
|
||||
}
|
||||
},
|
||||
/** 选择传方方式 */
|
||||
onTransferModeSelect(mode) {
|
||||
if (mode === 'photo') {
|
||||
this.showPhotoTransferDrawer = true
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/subPackages/sub_workbench/prescription_v2/index?salesperson_transfer=1&initial_category=1',
|
||||
})
|
||||
},
|
||||
/** 拍照传方:抽屉确认后直接提交 */
|
||||
async handlePhotoTransferConfirm(patientData) {
|
||||
this.photoSubmitting = true
|
||||
try {
|
||||
const wrap = await createClinicSalespersonTransferPrescription({
|
||||
...patientData,
|
||||
transfer_mode: 2,
|
||||
drugs: [],
|
||||
clinical_diagnose: '',
|
||||
doctor_order: '',
|
||||
})
|
||||
if (wrap && wrap.ok) {
|
||||
this.showPhotoTransferDrawer = false
|
||||
if (this.$refs.photoTransferDrawer && this.$refs.photoTransferDrawer.resetForm) {
|
||||
this.$refs.photoTransferDrawer.resetForm()
|
||||
}
|
||||
uni.showToast({ title: '提交成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({ url: '/subPackages/sub_clinic_salesperson/transfer-prescription/list' })
|
||||
}, 500)
|
||||
} else {
|
||||
uni.showToast({ title: wrap?.res?.msg || wrap?.res?.message || '提交失败', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '提交失败,请重试', icon: 'none' })
|
||||
} finally {
|
||||
this.photoSubmitting = false
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
113
subPackages/sub_clinic_salesperson/invitees/detail.vue
Normal file
113
subPackages/sub_clinic_salesperson/invitees/detail.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<clinic-salesperson-page-layout :title="pageTitle" :show-back="true">
|
||||
<view class="page">
|
||||
<view class="summary">
|
||||
<view class="sum-row">
|
||||
<text class="label">邀请分成比例</text>
|
||||
<text class="val">{{ summary.inviter_split }}%</text>
|
||||
</view>
|
||||
<view class="sum-row">
|
||||
<text class="label">累计带来收益</text>
|
||||
<text class="val amount">¥{{ summary.invite_total_amount }}</text>
|
||||
</view>
|
||||
<view class="sum-row">
|
||||
<text class="label">待结算 / 已结算</text>
|
||||
<text class="val">¥{{ summary.invite_pending_amount }} / ¥{{ summary.invite_settled_amount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!list.length && !loading" class="empty">暂无收益明细</view>
|
||||
<view v-for="item in list" :key="item.id" class="row">
|
||||
<view class="top">
|
||||
<text class="order">{{ item.order_no }}</text>
|
||||
<text class="status">{{ item.settlement_id > 0 ? '已结算' : '待结算' }}</text>
|
||||
</view>
|
||||
<text class="patient">{{ item.patient_name }} {{ item.patient_mobile }}</text>
|
||||
<text class="drug">{{ item.drug_name }} ×{{ item.qty }}</text>
|
||||
<view class="bottom">
|
||||
<text class="amount">¥{{ item.commission_amount }}</text>
|
||||
<text class="time">{{ item.created_at }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</clinic-salesperson-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||
import { getClinicSalespersonCommissionList } from '@/api/clinicSalesperson.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicSalespersonPageLayout },
|
||||
data() {
|
||||
return {
|
||||
sourceSalespersonId: 0,
|
||||
nickName: '',
|
||||
summary: {
|
||||
inviter_split: '0.00',
|
||||
invite_total_amount: '0.00',
|
||||
invite_pending_amount: '0.00',
|
||||
invite_settled_amount: '0.00',
|
||||
},
|
||||
list: [],
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pageTitle() {
|
||||
return this.nickName ? `${this.nickName} 的收益明细` : '收益明细'
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
this.sourceSalespersonId = Number(options.source_salesperson_id || 0)
|
||||
this.nickName = decodeURIComponent(options.nick_name || '')
|
||||
this.summary = {
|
||||
inviter_split: options.inviter_split || '0.00',
|
||||
invite_total_amount: options.invite_total_amount || '0.00',
|
||||
invite_pending_amount: options.invite_pending_amount || '0.00',
|
||||
invite_settled_amount: options.invite_settled_amount || '0.00',
|
||||
}
|
||||
this.loadList()
|
||||
},
|
||||
methods: {
|
||||
async loadList() {
|
||||
if (this.sourceSalespersonId <= 0) return
|
||||
this.loading = true
|
||||
try {
|
||||
const wrap = await getClinicSalespersonCommissionList({
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
commission_source: 1,
|
||||
source_salesperson_id: this.sourceSalespersonId,
|
||||
})
|
||||
if (wrap && wrap.ok) this.list = wrap.data?.items || []
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page { padding: 24rpx; }
|
||||
.summary {
|
||||
background: linear-gradient(135deg, #f0fdf4, #ecfdf5); border-radius: 12rpx;
|
||||
padding: 24rpx; margin-bottom: 20rpx;
|
||||
}
|
||||
.sum-row { display: flex; justify-content: space-between; margin-bottom: 12rpx; font-size: 26rpx; }
|
||||
.sum-row:last-child { margin-bottom: 0; }
|
||||
.label { color: #666; }
|
||||
.val { color: #333; font-weight: 500; }
|
||||
.val.amount { color: #4db6a8; font-weight: 600; }
|
||||
.empty { text-align: center; color: #999; padding: 80rpx 0; }
|
||||
.row { background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.top { display: flex; justify-content: space-between; align-items: center; }
|
||||
.order { font-size: 26rpx; font-weight: 600; }
|
||||
.status { font-size: 22rpx; color: #999; }
|
||||
.patient { display: block; font-size: 24rpx; color: #666; margin-top: 8rpx; }
|
||||
.drug { display: block; font-size: 24rpx; color: #666; margin-top: 4rpx; }
|
||||
.bottom { display: flex; justify-content: space-between; align-items: center; margin-top: 12rpx; }
|
||||
.amount { font-size: 28rpx; color: #4db6a8; font-weight: 600; }
|
||||
.time { font-size: 22rpx; color: #999; }
|
||||
</style>
|
||||
85
subPackages/sub_clinic_salesperson/invitees/index.vue
Normal file
85
subPackages/sub_clinic_salesperson/invitees/index.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<clinic-salesperson-page-layout title="我邀请的推广员" :show-back="true">
|
||||
<view class="page">
|
||||
<view v-if="!list.length && !loading" class="empty">暂无下级推广员</view>
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="row"
|
||||
hover-class="row-hover"
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<image v-if="item.avatar" :src="item.avatar" class="avatar" mode="aspectFill" />
|
||||
<view v-else class="avatar placeholder">{{ (item.nick_name || '?').charAt(0) }}</view>
|
||||
<view class="main">
|
||||
<text class="name">{{ item.nick_name || '推广员' }}</text>
|
||||
<text class="phone">{{ item.phone }}</text>
|
||||
<text class="sub">邀请分成 {{ item.inviter_split }}%</text>
|
||||
</view>
|
||||
<view class="right">
|
||||
<text class="amount">¥{{ item.invite_total_amount || '0.00' }}</text>
|
||||
<text class="hint">待结算 ¥{{ item.invite_pending_amount || '0.00' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</clinic-salesperson-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||
import { getClinicSalespersonInvitedList } from '@/api/clinicSalesperson.js'
|
||||
|
||||
const ROOT = '/subPackages/sub_clinic_salesperson'
|
||||
|
||||
export default {
|
||||
components: { ClinicSalespersonPageLayout },
|
||||
data() {
|
||||
return { list: [], loading: false, page: 1 }
|
||||
},
|
||||
onLoad() {
|
||||
this.loadList()
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
this.page = 1
|
||||
this.loadList(true)
|
||||
},
|
||||
methods: {
|
||||
async loadList(stopRefresh = false) {
|
||||
this.loading = true
|
||||
try {
|
||||
const wrap = await getClinicSalespersonInvitedList({ page: this.page, pageSize: 50 })
|
||||
if (wrap && wrap.ok) this.list = wrap.data?.items || []
|
||||
} finally {
|
||||
this.loading = false
|
||||
if (stopRefresh) uni.stopPullDownRefresh()
|
||||
}
|
||||
},
|
||||
goDetail(item) {
|
||||
uni.navigateTo({
|
||||
url: `${ROOT}/invitees/detail?source_salesperson_id=${item.id}&nick_name=${encodeURIComponent(item.nick_name || '')}&inviter_split=${item.inviter_split || 0}&invite_total_amount=${item.invite_total_amount || 0}&invite_pending_amount=${item.invite_pending_amount || 0}&invite_settled_amount=${item.invite_settled_amount || 0}`,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page { padding: 24rpx; }
|
||||
.empty { text-align: center; color: #999; padding: 80rpx 0; }
|
||||
.row {
|
||||
background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 16rpx;
|
||||
display: flex; align-items: center;
|
||||
}
|
||||
.row-hover { opacity: 0.92; }
|
||||
.avatar { width: 88rpx; height: 88rpx; border-radius: 50%; margin-right: 20rpx; flex-shrink: 0; }
|
||||
.avatar.placeholder {
|
||||
background: #e8f8f5; color: #6acdbb; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 36rpx; font-weight: 600;
|
||||
}
|
||||
.main { flex: 1; min-width: 0; }
|
||||
.name { display: block; font-size: 28rpx; font-weight: 600; color: #333; }
|
||||
.phone, .sub { display: block; font-size: 24rpx; color: #666; margin-top: 4rpx; }
|
||||
.right { text-align: right; flex-shrink: 0; }
|
||||
.amount { display: block; font-size: 30rpx; color: #4db6a8; font-weight: 600; }
|
||||
.hint { font-size: 22rpx; color: #999; }
|
||||
</style>
|
||||
@@ -1,44 +1,69 @@
|
||||
<template>
|
||||
<clinic-salesperson-page-layout title="传方记录" :show-back="true">
|
||||
<view class="page">
|
||||
<view v-if="!list.length" class="empty">暂无传方记录</view>
|
||||
<view v-for="item in list" :key="item.id" class="row" @click="goDetail(item.id)">
|
||||
<view v-if="!list.length && !loading" class="empty">暂无传方记录</view>
|
||||
<view v-for="item in list" :key="item.id" class="row" @click="openDetail(item.id)">
|
||||
<view class="top">
|
||||
<text class="name">{{ item.patient_name }} {{ item.patient_mobile }}</text>
|
||||
<u-tag :text="item.is_imported ? '已导入' : '待导入'" :type="item.is_imported ? 'success' : 'warning'" size="mini" />
|
||||
<u-tag
|
||||
:text="item.status_text || (item.is_imported ? '已完成' : '未处理')"
|
||||
:type="statusTagType(item.status)"
|
||||
size="mini"
|
||||
/>
|
||||
</view>
|
||||
<text class="diag">诊断:{{ item.clinical_diagnose || '-' }}</text>
|
||||
<text class="time">{{ item.transfer_time_text }}</text>
|
||||
<text class="meta">{{ item.transfer_mode_text || '' }} · {{ item.transfer_time_text }}</text>
|
||||
<text v-if="item.drug_text_lines && item.drug_text_lines.length" class="drugs">
|
||||
药方:{{ item.drug_text_lines.join(';') }}
|
||||
</text>
|
||||
<text v-if="item.remark" class="remark">备注:{{ item.remark }}</text>
|
||||
<text v-if="item.status === 3 && item.exception_message" class="exception">
|
||||
异常:{{ item.exception_message }}
|
||||
</text>
|
||||
</view>
|
||||
<u-button v-if="list.length" plain type="primary" class="mt" @click="goForm">新建传方</u-button>
|
||||
</view>
|
||||
<transfer-detail-drawer ref="detailDrawer" />
|
||||
</clinic-salesperson-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||
import TransferDetailDrawer from '@/subPackages/sub_clinic_salesperson/components/TransferDetailDrawer.vue'
|
||||
import { getClinicSalespersonTransferPrescriptionList } from '@/api/clinicSalesperson.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicSalespersonPageLayout },
|
||||
components: { ClinicSalespersonPageLayout, TransferDetailDrawer },
|
||||
data() {
|
||||
return { list: [] }
|
||||
return { list: [], loading: false }
|
||||
},
|
||||
onShow() {
|
||||
this.loadList()
|
||||
},
|
||||
methods: {
|
||||
async loadList() {
|
||||
this.loading = true
|
||||
try {
|
||||
const wrap = await getClinicSalespersonTransferPrescriptionList({ page: 1, pageSize: 50 })
|
||||
if (wrap && wrap.ok) {
|
||||
this.list = wrap.data?.items || []
|
||||
}
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
statusTagType(status) {
|
||||
const map = { 0: 'warning', 1: 'primary', 2: 'success', 3: 'error' }
|
||||
return map[Number(status)] || 'info'
|
||||
},
|
||||
goForm() {
|
||||
uni.navigateTo({ url: '/subPackages/sub_clinic_salesperson/transfer-prescription/form' })
|
||||
uni.navigateBack({ fail: () => {
|
||||
uni.navigateTo({ url: '/subPackages/sub_clinic_salesperson/home/index' })
|
||||
}})
|
||||
},
|
||||
goDetail(id) {
|
||||
uni.showToast({ title: `传方#${id}`, icon: 'none' })
|
||||
openDetail(id) {
|
||||
if (this.$refs.detailDrawer) {
|
||||
this.$refs.detailDrawer.open(id)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -50,6 +75,7 @@ export default {
|
||||
.row { background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.top { display: flex; justify-content: space-between; align-items: center; }
|
||||
.name { font-size: 28rpx; font-weight: 600; }
|
||||
.diag, .time { display: block; font-size: 24rpx; color: #666; margin-top: 8rpx; }
|
||||
.meta, .drugs, .remark { display: block; font-size: 24rpx; color: #666; margin-top: 8rpx; }
|
||||
.exception { display: block; font-size: 24rpx; color: #ef4444; margin-top: 8rpx; }
|
||||
.mt { margin-top: 24rpx; }
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<view class="picker-wrap">
|
||||
<view class="promoter-picker" @click="onOpen">
|
||||
<view v-if="selected" class="promoter-selected">
|
||||
<image v-if="selected.avatar" :src="selected.avatar" class="avatar" mode="aspectFill" />
|
||||
<view v-else class="avatar placeholder">{{ (selected.nick_name || '?').charAt(0) }}</view>
|
||||
<view class="meta">
|
||||
<text class="name">{{ selected.nick_name || '-' }}</text>
|
||||
<text class="sub">{{ selected.phone || '' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-else class="placeholder-text">{{ placeholder }}</text>
|
||||
<u-icon v-if="!disabled" name="arrow-right" color="#c0c4cc" size="28"></u-icon>
|
||||
</view>
|
||||
<text v-if="selected && clearable" class="clear-btn" @click.stop="onClear">清空</text>
|
||||
|
||||
<drawer-page-container v-model="showPopup" title="选择邀请人推广员">
|
||||
<view class="search-wrap">
|
||||
<u-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索姓名、手机号"
|
||||
:clearable="true"
|
||||
border="surround"
|
||||
@change="onSearch"
|
||||
/>
|
||||
</view>
|
||||
<scroll-view scroll-y class="sheet-scroll">
|
||||
<view v-if="loading" class="empty">加载中...</view>
|
||||
<view v-else-if="!options.length" class="empty">无匹配推广员</view>
|
||||
<view
|
||||
v-for="item in options"
|
||||
:key="item.id"
|
||||
class="option-item"
|
||||
@click="onPick(item)"
|
||||
>
|
||||
<image v-if="item.avatar" :src="item.avatar" class="avatar" mode="aspectFill" />
|
||||
<view v-else class="avatar placeholder">{{ (item.nick_name || '?').charAt(0) }}</view>
|
||||
<view class="meta">
|
||||
<text class="name">{{ item.nick_name || '-' }}</text>
|
||||
<text v-if="item.phone" class="phone">{{ item.phone }}</text>
|
||||
</view>
|
||||
<u-icon v-if="value === item.id" name="checkmark" color="#6acdbb" size="32"></u-icon>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</drawer-page-container>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DrawerPageContainer from '@/subPackages/sub_salesperson/components/DrawerPageContainer.vue'
|
||||
|
||||
export default {
|
||||
name: 'SalespersonPicker',
|
||||
components: { DrawerPageContainer },
|
||||
props: {
|
||||
value: { type: Number, default: 0 },
|
||||
disabled: { type: Boolean, default: false },
|
||||
clearable: { type: Boolean, default: true },
|
||||
placeholder: { type: String, default: '请选择邀请人推广员(可选)' },
|
||||
excludeId: { type: Number, default: 0 },
|
||||
storeId: { type: Number, default: 0 },
|
||||
fetchOptions: { type: Function, required: true },
|
||||
initialSelected: { type: Object, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showPopup: false,
|
||||
searchKeyword: '',
|
||||
loading: false,
|
||||
options: [],
|
||||
selected: null,
|
||||
searchTimer: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (!val) {
|
||||
this.selected = null
|
||||
return
|
||||
}
|
||||
if (this.initialSelected && Number(this.initialSelected.id) === Number(val)) {
|
||||
this.selected = this.initialSelected
|
||||
}
|
||||
},
|
||||
},
|
||||
initialSelected: {
|
||||
immediate: true,
|
||||
handler(item) {
|
||||
if (item && Number(item.id) === Number(this.value)) {
|
||||
this.selected = item
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onOpen() {
|
||||
if (this.disabled) return
|
||||
this.showPopup = true
|
||||
this.loadOptions('')
|
||||
},
|
||||
onSearch() {
|
||||
if (this.searchTimer) clearTimeout(this.searchTimer)
|
||||
this.searchTimer = setTimeout(() => this.loadOptions(this.searchKeyword), 300)
|
||||
},
|
||||
async loadOptions(keyword) {
|
||||
this.loading = true
|
||||
try {
|
||||
const wrap = await this.fetchOptions({
|
||||
keyword: keyword || '',
|
||||
exclude_id: this.excludeId,
|
||||
store_id: this.storeId || undefined,
|
||||
})
|
||||
this.options = (wrap && wrap.ok && wrap.data) ? wrap.data : []
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
onPick(item) {
|
||||
this.selected = item
|
||||
this.$emit('input', Number(item.id))
|
||||
this.$emit('change', item)
|
||||
this.showPopup = false
|
||||
},
|
||||
onClear() {
|
||||
this.selected = null
|
||||
this.$emit('input', 0)
|
||||
this.$emit('change', null)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.picker-wrap { position: relative; }
|
||||
.promoter-picker {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
background: #fff; border-radius: 16rpx; padding: 24rpx;
|
||||
}
|
||||
.promoter-selected { display: flex; align-items: center; flex: 1; min-width: 0; }
|
||||
.avatar { width: 72rpx; height: 72rpx; border-radius: 50%; margin-right: 20rpx; flex-shrink: 0; }
|
||||
.avatar.placeholder {
|
||||
background: #e8f8f5; color: #6acdbb; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 32rpx; font-weight: 600;
|
||||
}
|
||||
.meta { flex: 1; min-width: 0; }
|
||||
.name { display: block; font-size: 28rpx; font-weight: 600; color: #333; }
|
||||
.sub, .phone { font-size: 24rpx; color: #999; }
|
||||
.placeholder-text { font-size: 28rpx; color: #999; flex: 1; }
|
||||
.clear-btn { position: absolute; right: 24rpx; top: -36rpx; font-size: 24rpx; color: #999; }
|
||||
.search-wrap { padding: 0 24rpx 16rpx; }
|
||||
.sheet-scroll { max-height: 60vh; }
|
||||
.empty { text-align: center; color: #999; padding: 60rpx 0; font-size: 26rpx; }
|
||||
.option-item {
|
||||
display: flex; align-items: center; padding: 24rpx; border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -39,6 +39,22 @@
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="section-title">邀请关系(可选)</view>
|
||||
<view class="field-col">
|
||||
<text class="label block">邀请人推广员</text>
|
||||
<salesperson-picker
|
||||
v-model="form.parent_salesperson_id"
|
||||
:exclude-id="form.id"
|
||||
:store-id="form.store_id"
|
||||
:fetch-options="fetchSalespersonOptions"
|
||||
:initial-selected="parentInitial"
|
||||
/>
|
||||
</view>
|
||||
<view v-if="Number(form.parent_salesperson_id) > 0" class="field">
|
||||
<text class="label">邀请分成(%)</text>
|
||||
<input class="input" v-model="form.inviter_split" type="digit" placeholder="上级从订单利润中获得的比例" />
|
||||
</view>
|
||||
|
||||
<view class="submit-btn" @click="submit">保存</view>
|
||||
</view>
|
||||
</page-layout>
|
||||
@@ -47,17 +63,19 @@
|
||||
<script>
|
||||
import PageLayout from './components/PageLayout.vue';
|
||||
import StoreSearchPicker from './components/StoreSearchPicker.vue';
|
||||
import SalespersonPicker from './components/SalespersonPicker.vue';
|
||||
import { getSalespersonManageContext, resolveManageMode } from './common/context.js';
|
||||
import { SPLIT_TYPE_OPTIONS, TCM_BASE_OPTIONS } from './common/labels.js';
|
||||
|
||||
const ROOT = '/subPackages/sub_salesperson_manage';
|
||||
|
||||
export default {
|
||||
components: { PageLayout, StoreSearchPicker },
|
||||
components: { PageLayout, StoreSearchPicker, SalespersonPicker },
|
||||
data() {
|
||||
return {
|
||||
ctx: getSalespersonManageContext('clinic'),
|
||||
isUpdate: false,
|
||||
parentInitial: null,
|
||||
form: {
|
||||
id: 0,
|
||||
store_id: 0,
|
||||
@@ -67,6 +85,8 @@ export default {
|
||||
split: 0,
|
||||
tcm_split: 0,
|
||||
tcm_base_type: 0,
|
||||
parent_salesperson_id: 0,
|
||||
inviter_split: 0,
|
||||
},
|
||||
splitTypeLabels: SPLIT_TYPE_OPTIONS.map((o) => o.label),
|
||||
tcmBaseLabels: TCM_BASE_OPTIONS.map((o) => o.label),
|
||||
@@ -90,6 +110,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchSalespersonOptions(params) {
|
||||
return this.ctx.api.getSalespersonOptions(params);
|
||||
},
|
||||
async loadDetail(options) {
|
||||
const wrap = await this.ctx.api.getList({ page: 1, pageSize: 100 });
|
||||
if (!wrap || !wrap.ok) return;
|
||||
@@ -101,6 +124,15 @@ export default {
|
||||
this.form.split = item.split || 0;
|
||||
this.form.tcm_split = item.tcm_split || 0;
|
||||
this.form.tcm_base_type = Number(item.tcm_base_type) || 0;
|
||||
this.form.parent_salesperson_id = Number(item.parent_salesperson_id) || 0;
|
||||
this.form.inviter_split = item.inviter_split || 0;
|
||||
if (this.form.parent_salesperson_id > 0) {
|
||||
this.parentInitial = {
|
||||
id: this.form.parent_salesperson_id,
|
||||
nick_name: item.parent_nick_name,
|
||||
phone: item.parent_phone,
|
||||
};
|
||||
}
|
||||
if (!this.form.store_id && item.qr_code) {
|
||||
this.form.store_id = item.qr_code.store_id || 0;
|
||||
}
|
||||
@@ -129,8 +161,16 @@ export default {
|
||||
uni.showToast({ title: '请选择诊所', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (Number(this.form.parent_salesperson_id) > 0 && !(Number(this.form.inviter_split) > 0)) {
|
||||
uni.showToast({ title: '请填写邀请分成比例', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const payload = { ...this.form };
|
||||
if (Number(payload.split_type) === 0) payload.split = 0;
|
||||
if (!(Number(payload.parent_salesperson_id) > 0)) {
|
||||
payload.parent_salesperson_id = 0;
|
||||
payload.inviter_split = 0;
|
||||
}
|
||||
|
||||
uni.showLoading({ title: '保存中' });
|
||||
try {
|
||||
@@ -165,6 +205,8 @@ export default {
|
||||
.picker-val { flex: 1; font-size: 28rpx; text-align: right; color: #333; }
|
||||
.link-text { font-size: 26rpx; color: #6ACDBB; }
|
||||
.section-title { font-size: 26rpx; color: #999; margin: 24rpx 0 12rpx 8rpx; }
|
||||
.field-col { background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.label.block { display: block; width: auto; margin-bottom: 16rpx; }
|
||||
.submit-btn {
|
||||
margin-top: 40rpx; background: linear-gradient(90deg, #00BFA6, #6ACDBB);
|
||||
color: #fff; text-align: center; padding: 24rpx; border-radius: 16rpx; font-size: 30rpx;
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<page-container
|
||||
v-if="show"
|
||||
:show="show"
|
||||
:overlay="false"
|
||||
@leave="handleClose"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<u-popup
|
||||
v-model="show"
|
||||
mode="bottom"
|
||||
@@ -13,7 +22,6 @@
|
||||
<view class="modal-header white">
|
||||
<d-text text="收货信息" className="fs-32 font-bold color-title"></d-text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="scroll-container" scroll-y>
|
||||
<view class="form-body">
|
||||
<view class="field">
|
||||
@@ -29,7 +37,10 @@
|
||||
<u-input v-model="form.patient_address" placeholder="请输入收货地址" border="surround" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<d-text text="处方图片(可选)" className="fs-28 font-bold color-title m-b-12"></d-text>
|
||||
<d-text
|
||||
:text="photoMode ? '处方图片(必填)' : '处方图片(可选)'"
|
||||
className="fs-28 font-bold color-title m-b-12"
|
||||
></d-text>
|
||||
<view class="img-list">
|
||||
<view v-for="(img, idx) in form.prescription_images" :key="idx" class="img-item">
|
||||
<image :src="img" mode="aspectFill" />
|
||||
@@ -40,9 +51,18 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="field">
|
||||
<d-text text="备注(选填)" className="fs-28 font-bold color-title m-b-12"></d-text>
|
||||
<u-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
placeholder="可填写补充说明"
|
||||
border="surround"
|
||||
:height="120"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="modal-footer flex-row flex-jus-between flex-ali-center white shadow-up">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
@@ -60,6 +80,7 @@
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -76,6 +97,11 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/** 拍照传方模式:处方图片必填 */
|
||||
photoMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -85,6 +111,7 @@ export default {
|
||||
patient_mobile: '',
|
||||
patient_address: '',
|
||||
prescription_images: [],
|
||||
remark: '',
|
||||
},
|
||||
cancelBtnStyle: {
|
||||
backgroundColor: '#F4F6F8',
|
||||
@@ -146,15 +173,21 @@ export default {
|
||||
const name = (this.form.patient_name || '').trim();
|
||||
const mobile = (this.form.patient_mobile || '').trim();
|
||||
const address = (this.form.patient_address || '').trim();
|
||||
const remark = (this.form.remark || '').trim();
|
||||
if (!name || !mobile || !address) {
|
||||
uni.showToast({ title: '请填写收货人、手机号和地址', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (this.photoMode && this.form.prescription_images.length === 0) {
|
||||
uni.showToast({ title: '请上传至少一张处方图片', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.$emit('confirm', {
|
||||
patient_name: name,
|
||||
patient_mobile: mobile,
|
||||
patient_address: address,
|
||||
prescription_images: [...this.form.prescription_images],
|
||||
remark,
|
||||
});
|
||||
},
|
||||
resetForm() {
|
||||
@@ -163,6 +196,7 @@ export default {
|
||||
patient_mobile: '',
|
||||
patient_address: '',
|
||||
prescription_images: [],
|
||||
remark: '',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
@@ -96,8 +96,8 @@
|
||||
<u-button size="mini" type="primary" plain @click="openSalespersonTransfer">查看传方</u-button>
|
||||
</view>
|
||||
|
||||
<!-- 诊断输入区域 -->
|
||||
<view class="top white p-32 flex-col m-t-16 radius-12 mx-32">
|
||||
<!-- 诊断输入区域(传方模式不展示) -->
|
||||
<view v-if="!isSalespersonTransferMode" class="top white p-32 flex-col m-t-16 radius-12 mx-32">
|
||||
<view class="top_title flex-row flex-ali-center">
|
||||
<image :src="require('@/static/image/js.png')" class="size-32"></image>
|
||||
<d-text text="临床诊断" className="fs-32 m-l-16 font-bold" color="#333"></d-text>
|
||||
@@ -232,7 +232,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="white p-32 radius-12 m-t-24 flex-col" v-if="currentDrugs.length > 0">
|
||||
<view class="white p-32 radius-12 m-t-24 flex-col" v-if="currentDrugs.length > 0 && !isSalespersonTransferMode">
|
||||
<view class="flex-jus-sp flex-row flex-ali-center">
|
||||
<d-text text="医嘱补充" color="#333" className="font-bold"></d-text>
|
||||
<d-text @click="handleOpenDoctorOrderModal" text="选择常用医嘱" color="#6ACDBB"></d-text>
|
||||
@@ -369,7 +369,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="white p-32 radius-12 m-t-24 flex-col" v-if="currentDrugs.length > 0">
|
||||
<view class="white p-32 radius-12 m-t-24 flex-col" v-if="currentDrugs.length > 0 && !isSalespersonTransferMode">
|
||||
<view class="flex-jus-sp flex-row flex-ali-center">
|
||||
<d-text text="医嘱补充" color="#333" className="font-bold"></d-text>
|
||||
<d-text @click="handleOpenDoctorOrderModal" text="选择常用医嘱" color="#6ACDBB"></d-text>
|
||||
@@ -381,8 +381,8 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 医嘱输入区域(中药和简单产品共用) -->
|
||||
<view class="flex-col white p-32 radius-12 mx-32 m-t-24" v-if="activeCategory === 1 && currentDrugs.length > 0">
|
||||
<!-- 医嘱输入区域(传方模式不展示) -->
|
||||
<view class="flex-col white p-32 radius-12 mx-32 m-t-24" v-if="activeCategory === 1 && currentDrugs.length > 0 && !isSalespersonTransferMode">
|
||||
<view class="flex-jus-sp flex-row flex-ali-center">
|
||||
<d-text text="医嘱补充" color="#333" className="font-bold"></d-text>
|
||||
<d-text @click="handleOpenDoctorOrderModal" text="选择常用医嘱" color="#6ACDBB" />
|
||||
@@ -469,8 +469,8 @@
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
<view class="ac p-b-40 p-x-32 flex-row flex-jus-center">
|
||||
<!-- 提示信息(传方模式不展示) -->
|
||||
<view v-if="!isSalespersonTransferMode" class="ac p-b-40 p-x-32 flex-row flex-jus-center">
|
||||
<u-icon name="shield-check" color="#B0B6C2" size="28" class="m-r-8"></u-icon>
|
||||
<text class="fs-24 color-sub">请确认患者已在实体医院就诊,并有明确诊断</text>
|
||||
</view>
|
||||
@@ -1016,6 +1016,12 @@ export default {
|
||||
if (this.isSalespersonTransferMode) {
|
||||
this.registerId = SALESPERSON_TRANSFER_STORAGE_KEY;
|
||||
this._presetActiveCategory = 1;
|
||||
// 传方模式默认自制剂 + 剂包
|
||||
this.chineseConfig = {
|
||||
...this.chineseConfig,
|
||||
ruleType: 1,
|
||||
packageMethodId: 2,
|
||||
};
|
||||
} else {
|
||||
this.registerId = options.register_id || '';
|
||||
}
|
||||
@@ -1082,6 +1088,9 @@ export default {
|
||||
if (this.activeCategory === 1 && this.chineseConfig.ruleType === 2) {
|
||||
await this.hydrateChineseProcessRuleListsFromConfig();
|
||||
}
|
||||
if (this.isSalespersonTransferMode) {
|
||||
this.ensureSalespersonTransferPackageMethodDefault();
|
||||
}
|
||||
if (!this.isSalespersonTransferMode) {
|
||||
await this.maybeApplyReusePrescription();
|
||||
if (this.activeCategory === 1) await this.checkAndShowTransferTip();
|
||||
@@ -1121,6 +1130,19 @@ export default {
|
||||
console.error('加载基础配置数据失败:', error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 传方模式:草稿未保存包法时默认剂包(id=2)
|
||||
*/
|
||||
ensureSalespersonTransferPackageMethodDefault() {
|
||||
if (!this.isSalespersonTransferMode || this.activeCategory !== 1) return;
|
||||
if (!this.chineseConfig.packageMethodId) {
|
||||
this.chineseConfig = {
|
||||
...this.chineseConfig,
|
||||
ruleType: 1,
|
||||
packageMethodId: 2,
|
||||
};
|
||||
}
|
||||
},
|
||||
restoreFromLocalStorage() {
|
||||
if (this._presetActiveCategory !== null && this._presetActiveCategory !== undefined && !Number.isNaN(this._presetActiveCategory)) {
|
||||
this.activeCategory = this._presetActiveCategory;
|
||||
@@ -2113,7 +2135,8 @@ export default {
|
||||
requireOnlineTcm: this.activeCategory === 1 && this.isOnlineRevisit,
|
||||
onlineTcmDiseasesId: this.onlineTcmDiseasesId,
|
||||
onlineTcmMethodId: this.onlineTcmMethodId,
|
||||
onlineTcmSyndromeId: this.onlineTcmSyndromeId
|
||||
onlineTcmSyndromeId: this.onlineTcmSyndromeId,
|
||||
skipDiagnosisAndAdvice: this.isSalespersonTransferMode,
|
||||
});
|
||||
},
|
||||
async checkChineseMedicineConflict() {
|
||||
@@ -2376,7 +2399,6 @@ export default {
|
||||
} catch (error) { console.error('保存常用方失败:', error); this.$toast('保存常用方失败,请重试'); }
|
||||
},
|
||||
buildSalespersonTransferPayload(patientData) {
|
||||
const clinicalDiagnose = this.diagnosisText || this.diagnoses.map((item) => item.name).join(',');
|
||||
const drugs = this.currentDrugs.map((drug) => ({
|
||||
drug_id: drug.id || drug.drug_id || drug.index_id,
|
||||
drug_name: drug.drug_name || drug.name,
|
||||
@@ -2388,8 +2410,10 @@ export default {
|
||||
patient_mobile: patientData.patient_mobile,
|
||||
patient_address: patientData.patient_address,
|
||||
prescription_images: patientData.prescription_images || [],
|
||||
clinical_diagnose: clinicalDiagnose,
|
||||
doctor_order: this.medicalAdvice || '',
|
||||
remark: patientData.remark || '',
|
||||
transfer_mode: 1,
|
||||
clinical_diagnose: '',
|
||||
doctor_order: '',
|
||||
dosage: this.chineseConfig.dosage || 7,
|
||||
day_dosage: this.chineseConfig.dayDosage || 2,
|
||||
drugs,
|
||||
|
||||
@@ -23,7 +23,8 @@ export class PrescriptionValidator {
|
||||
requireOnlineTcm,
|
||||
onlineTcmDiseasesId,
|
||||
onlineTcmMethodId,
|
||||
onlineTcmSyndromeId
|
||||
onlineTcmSyndromeId,
|
||||
skipDiagnosisAndAdvice,
|
||||
} = params;
|
||||
|
||||
// 验证药品
|
||||
@@ -34,21 +35,21 @@ export class PrescriptionValidator {
|
||||
};
|
||||
}
|
||||
|
||||
// 验证诊断
|
||||
// 传方模式跳过诊断与医嘱校验
|
||||
if (!skipDiagnosisAndAdvice) {
|
||||
if (!diagnoses || diagnoses.length === 0) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '请添加诊断'
|
||||
};
|
||||
}
|
||||
|
||||
// 验证医嘱
|
||||
if (!medicalAdvice || medicalAdvice.trim() === '') {
|
||||
return {
|
||||
valid: false,
|
||||
message: '请输入医嘱'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 验证中药配置
|
||||
if (category === 1 && chineseConfig) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<view class="status-banner"
|
||||
:class="infoList.status==1&&'bg_blue'||infoList.status==2&&'bg_green'||infoList.status==3&&'bg_purple'||infoList.status==7&&'bg_red'">
|
||||
<view class="status-content flex-row flex-jus-sp flex-ali-center">
|
||||
<text class="fs-36 font-bold text-white">{{infoList.status==1&&'待接诊'||infoList.status==2&&'已接诊'||infoList.status==3&&'已完成'||infoList.status==7&&'已拒诊'}}</text>
|
||||
<text class="fs-36 font-bold text-white">{{infoList.status==1&&'待接诊'||infoList.status==2&&'已接诊'||infoList.status==3&&'已完成'||infoList.status==4&&'已取消'||infoList.status==7&&'已拒诊'}}</text>
|
||||
<image class="status-icon"
|
||||
:src="infoList.status==1&&'/static/info/djz.png'||infoList.status==2&&'/static/info/yjz.png'||infoList.status==3&&'/static/info/ywc.png'||infoList.status==7&&'/static/info/jz.png'"
|
||||
:src="infoList.status==1&&'/static/info/djz.png'||infoList.status==2&&'/static/info/yjz.png'||infoList.status==3&&'/static/info/ywc.png'||infoList.status==4&&'/statuic/info/jz.png'||infoList.status==7&&'/static/info/jz.png'"
|
||||
mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
@@ -61,7 +61,7 @@
|
||||
<text class="info-label">规格</text>
|
||||
<text class="info-value">{{ specialPrescriptionPurchaseLabel }}</text>
|
||||
</view>
|
||||
<view class="-8">
|
||||
<view class="">
|
||||
<u-tag
|
||||
v-if="Number(infoList.special_prescription_patient_record.status) === 0"
|
||||
text="待导入"
|
||||
@@ -84,8 +84,9 @@
|
||||
shape="circle"
|
||||
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', border: 'none' }"
|
||||
@click="goImportSpecialPrescription"
|
||||
:disabled="infoList.status!=2"
|
||||
>
|
||||
导入
|
||||
{{ infoList.status!=2? '接诊后可导入特色方' : '导入特色方' }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -705,7 +706,7 @@ page {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16rpx;
|
||||
margin-bottom: 6rpx;
|
||||
line-height: 1.5;
|
||||
|
||||
&:last-child {
|
||||
|
||||
Reference in New Issue
Block a user