1. 推广员功能增强

This commit is contained in:
李琦
2026-06-12 15:07:14 +08:00
parent 2f37bf2909
commit 4090c2ca3a
16 changed files with 1006 additions and 287 deletions

View File

@@ -80,6 +80,7 @@ export type ComponentType =
| 'Upload'
| 'UploadImage'
| 'UploadImageSortable'
| 'UploadOssFile'
| BaseFormComponentType;
async function initComponentAdapter() {

View File

@@ -19,6 +19,8 @@ setupVbenForm<ComponentType>({
Radio: 'checked',
Switch: 'checked',
Upload: 'fileList',
UploadImage: 'modelValue',
UploadOssFile: 'modelValue',
},
},
defineRules: {

View File

@@ -0,0 +1,156 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { preferences } from '@vben/preferences';
import { Button, Upload } from 'ant-design-vue';
import { Icon } from '#/components/icon';
import { uploadFile } from '#/api/core/upload';
import { uploadToOss } from '#/utils/oss-upload';
interface FileItem {
uid: string;
name: string;
status: 'uploading' | 'done' | 'error';
url: string;
}
interface Props {
modelValue?: string;
maxCount?: number;
}
const props = withDefaults(defineProps<Props>(), {
modelValue: '',
maxCount: 1,
});
const emit = defineEmits<{
'update:modelValue': [value: string];
}>();
const fileList = ref<FileItem[]>([]);
function syncFileListFromValue(url: string) {
if (!url) {
fileList.value = [];
return;
}
fileList.value = [
{
uid: url,
name: url.split('/').pop() || 'file',
status: 'done',
url,
},
];
}
syncFileListFromValue(props.modelValue);
watch(
() => props.modelValue,
(newVal) => {
const currentUrl =
fileList.value.find((file) => file.status === 'done')?.url ?? '';
if ((newVal ?? '') !== currentUrl) {
syncFileListFromValue(newVal ?? '');
}
},
);
function updateModelValue() {
const url =
fileList.value.find((file) => file.status === 'done' && file.url)?.url ??
'';
emit('update:modelValue', url);
}
function beforeUpload(file: File) {
const maxSize = 20 * 1024 * 1024;
if (file.size > maxSize) {
return Upload.LIST_IGNORE;
}
return true;
}
const customRequest = async (options: any) => {
const { file, onProgress, onSuccess, onError } = options;
fileList.value = [
{
uid: file.uid,
name: file.name,
status: 'uploading',
url: '',
},
];
try {
const uploadMethod = preferences.app.uploadMethod || 'direct';
let res: { url: string };
if (uploadMethod === 'direct') {
res = await uploadToOss({
file: file as File,
onProgress: (percent) => {
onProgress?.({ percent });
},
});
} else {
res = await uploadFile({
file: file as File,
});
}
fileList.value = [
{
uid: file.uid,
name: file.name,
status: 'done',
url: res.url,
},
];
updateModelValue();
onSuccess?.(res);
} catch (error) {
fileList.value = [
{
uid: file.uid,
name: file.name,
status: 'error',
url: '',
},
];
onError?.(error);
}
};
function handleRemove() {
fileList.value = [];
updateModelValue();
return true;
}
const showUploadButton = computed(
() => fileList.value.filter((item) => item.status !== 'error').length === 0,
);
</script>
<template>
<Upload
:file-list="fileList"
:before-upload="beforeUpload"
:custom-request="customRequest"
:max-count="maxCount"
list-type="text"
accept=".pdf,.jpg,.jpeg,.png,.doc,.docx,image/*,application/pdf"
@remove="handleRemove"
>
<Button v-if="showUploadButton">
上传文件
<template #icon>
<Icon icon="ant-design:cloud-upload-outlined" />
</template>
</Button>
</Upload>
</template>

View File

@@ -126,7 +126,7 @@ const orderTypeMap = {
{{ data.total_pay_price }}
</Descriptions.Item>
<Descriptions.Item label="物品总价">
{{ data.items_price }}
药品{{ data.items_price }} 加工费{{ data.process_price }}
</Descriptions.Item>
<Descriptions.Item label="供货价">
{{ data.market_price }}

View File

@@ -255,121 +255,6 @@ export const modalFormProps: VbenFormProps = {
rules: 'required',
suffix: () => '%', // 后缀显示百分号
},
{
// 银行卡信息分隔线(仅在新增时显示)
component: 'Divider',
fieldName: '',
label: 'x',
rules: 'required',
hideLabel: true,
dependencies: {
show: (values) => {
// 仅在新增时显示id为空时
return values.id == null;
},
triggerFields: ['id'],
},
renderComponentContent: () => {
return {
default: () => {
return '银行卡信息';
},
};
},
},
{
// 开户人姓名字段(仅在新增时显示)
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入开户人姓名',
},
dependencies: {
show: (values) => {
// 仅在新增时显示id为空时
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_user_name',
label: '开户人姓名',
rules: 'required',
},
{
// 银行卡号字段(仅在新增时显示)
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入银行卡号',
},
dependencies: {
show: (values) => {
// 仅在新增时显示id为空时
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_card',
label: '银行卡号',
rules: 'required',
},
{
// 开户行字段(仅在新增时显示)
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入开户行',
},
dependencies: {
show: (values) => {
// 仅在新增时显示id为空时
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_name',
label: '开户行',
rules: 'required',
},
{
// 银联行号字段(仅在新增时显示,非必填)
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入银联行号',
},
dependencies: {
show: (values) => {
// 仅在新增时显示id为空时
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_no',
label: '银联行号',
},
{
// 银行账户类型字段(仅在新增时显示)
component: 'RadioGroup',
componentProps: {
options: [
{ label: '对公', value: 1 },
{ label: '对私', value: 2 },
{ label: '存折', value: 5 },
],
placeholder: '请选择银行账户类型',
},
dependencies: {
show: (values) => {
// 仅在新增时显示id为空时
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_account_type',
label: '银行账户类型',
rules: 'required',
},
],
showDefaultActions: false, // 不显示默认操作按钮
};

View File

@@ -42,13 +42,19 @@ export const gridOptions: VxeGridProps<RowType> = {
slots: { default: 'online_consultation_config' },
},
{
// 是否包邮列(显示为开关)
field: 'is_shipping_free',
align: 'left',
title: '是否包邮',
slots: { default: 'is_shipping_free' },
width: 120,
},
{
field: 'bank_card_report_status',
align: 'left',
title: '银行卡报备',
slots: { default: 'bank_card_report_status' },
width: 120,
},
{
// 订阅价格波动列(显示为开关)
field: 'subscribe_price_change',

View File

@@ -31,6 +31,9 @@ import FormModalDemo from './components/modal.vue';
import BindConsultationModal from '#/views/system/store/components/BindConsultationModal.vue';
import DrugPriceModal from '#/views/system/store/components/DrugPriceModal.vue';
import StoreExternalFieldModal from '#/views/system/store/components/StoreExternalFieldModal.vue';
import BankCardReportModal from '#/views/system/store-bank-card/components/BankCardReportModal.vue';
import BankCardStatusModal from '#/views/system/store-bank-card/components/BankCardStatusModal.vue';
import { getStoreBankCardReportDetail } from '#/views/system/store-bank-card/api';
// 导入搜索表单配置
import { formOptions } from './config/search';
// 导入表格配置
@@ -110,6 +113,68 @@ const [DrugPriceModalComponent, drugPriceModalApi] = useVbenModal({
connectedComponent: DrugPriceModal,
});
const [BankCardReportModalComponent, bankCardReportModalApi] = useVbenModal({
connectedComponent: BankCardReportModal,
});
const [BankCardStatusModalComponent, bankCardStatusModalApi] = useVbenModal({
connectedComponent: BankCardStatusModal,
});
const openBankCardReport = async (row: any, update = false) => {
let report = null;
try {
report = await getStoreBankCardReportDetail(row.id);
} catch {
report = null;
}
bankCardReportModalApi.setData({
storeId: row.id,
storeName: row.name,
mode: update && report?.id ? 'update' : 'report',
report,
gridApi,
});
bankCardReportModalApi.open();
};
const openBankCardStatus = (row: any) => {
bankCardStatusModalApi.setData({
storeId: row.id,
storeName: row.name,
});
bankCardStatusModalApi.open();
};
function getBankCardReportTagColor(status: number) {
switch (Number(status)) {
case 1:
return 'processing';
case 2:
return 'success';
case 3:
case 4:
return 'error';
default:
return 'default';
}
}
function getBankCardReportTagText(row: any) {
return Number(row.bank_card_report_status) === 0
? '报备'
: row.bank_card_report_status_text || '未报备';
}
function handleBankCardReportColumnClick(row: any) {
const status = Number(row.bank_card_report_status ?? 0);
if (status === 1 || status === 2) {
openBankCardStatus(row);
return;
}
openBankCardReport(row, status !== 0);
}
const showDrugPriceModal = (row: any) => {
drugPriceModalApi.setData({
store_id: row.id,
@@ -258,6 +323,8 @@ const batchSyncDrugPrice = () => {
<template>
<Page auto-content-height title="药店管理">
<FormModal />
<BankCardReportModalComponent />
<BankCardStatusModalComponent />
<QrCodePreviewModal />
<DrugPriceModalComponent />
<BindConsultationModalComponent />
@@ -389,6 +456,15 @@ const batchSyncDrugPrice = () => {
@click="updateShippingFree(row.id)"
/>
</template>
<template #bank_card_report_status="{ row }">
<Tag
:color="getBankCardReportTagColor(row.bank_card_report_status)"
style="cursor: pointer"
@click="handleBankCardReportColumnClick(row)"
>
{{ getBankCardReportTagText(row) }}
</Tag>
</template>
<template #subscribe_price_change="{ row }">
<Tag
:color="row.subscribe_price_change === 0 ? 'success' : 'default'"
@@ -468,6 +544,30 @@ const batchSyncDrugPrice = () => {
ifShow: isPlatformAdmin,
onClick: showDrugPriceModal.bind(null, row),
},
{
label: '查看卡状态',
type: 'link',
icon: 'ant-design:eye-outlined',
size: 'small',
auth: ['Super Admin', 'Admin'],
onClick: () => openBankCardStatus(row),
},
{
label: '银行卡报备',
type: 'link',
icon: 'ant-design:bank-outlined',
size: 'small',
auth: ['Super Admin', 'Admin'],
onClick: () => openBankCardReport(row, false),
},
{
label: '修改银行卡',
type: 'link',
icon: 'ant-design:credit-card-outlined',
size: 'small',
auth: ['Super Admin', 'Admin'],
onClick: () => openBankCardReport(row, true),
},
]"
/>
</template>

View File

@@ -0,0 +1,43 @@
import { requestClient } from '#/api/request';
const prefix = 'store-bank-card-report/';
export async function getStoreBankCardReportDetail(storeId: number) {
return requestClient.get<any>(`${prefix}detail`, {
params: { store_id: storeId },
});
}
export async function getSelectableBankCards(storeId: number) {
return requestClient.get<{ options: Array<{ value: string; label: string; payload: Record<string, any> }> }>(
`${prefix}selectable-cards`,
{
params: { store_id: storeId },
},
);
}
export async function reportStoreBankCard(data: Record<string, any>) {
return requestClient.post<any>(`${prefix}report`, data);
}
export async function updateStoreBankCard(data: Record<string, any>) {
return requestClient.post<any>(`${prefix}update`, data);
}
export async function queryStoreBankCardStatus(data: { id: number }) {
return requestClient.post<any>(`${prefix}query-status`, data);
}
export async function syncStoreBankCardStatusByStore(storeId: number) {
return requestClient.post<any>(`${prefix}sync-status-by-store`, {
store_id: storeId,
});
}
export async function migrateStoreBankCardToYiiStore(data: {
store_id: number;
report_id?: number;
}) {
return requestClient.post<any>(`${prefix}migrate-bank-to-store`, data);
}

View File

@@ -0,0 +1,222 @@
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Alert, message, Select } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import {
getSelectableBankCards,
queryStoreBankCardStatus,
reportStoreBankCard,
updateStoreBankCard,
} from '../api';
import { USE_TYPE_TRANSFER } from '../config/constants';
import { bankCardReportFormProps } from '../config/form';
const mode = ref<'report' | 'update'>('report');
const storeId = ref(0);
const storeName = ref('');
const reportInfo = ref<Record<string, any> | null>(null);
const gridApi = ref();
const selectableOptions = ref<
Array<{ value: string; label: string; payload: Record<string, any> }>
>([]);
const selectedCardKey = ref<string | undefined>();
const importReportId = ref(0);
const onlyRemark = computed(
() =>
mode.value === 'update' &&
reportInfo.value &&
reportInfo.value.can_full_update === false,
);
const showCardSelector = computed(
() => mode.value === 'report' && !onlyRemark.value,
);
const showImportAlert = computed(
() => mode.value === 'report' && importReportId.value > 0,
);
const [Form, formApi] = useVbenForm(bankCardReportFormProps);
async function loadSelectableCards() {
if (!storeId.value || !showCardSelector.value) {
selectableOptions.value = [];
return;
}
try {
const res = await getSelectableBankCards(storeId.value);
selectableOptions.value = res?.options ?? [];
} catch {
selectableOptions.value = [];
}
}
async function handleSelectExistingCard(value: string | undefined) {
if (!value) {
importReportId.value = 0;
return;
}
const option = selectableOptions.value.find((item) => item.value === value);
if (!option) {
importReportId.value = 0;
return;
}
importReportId.value = Number(option.payload.import_report_id ?? 0);
const currentValues = await formApi.getValues();
await formApi.setValues({
...currentValues,
...option.payload,
store_id: storeId.value,
use_type: USE_TYPE_TRANSFER,
use_type_display: '转账卡useType=2',
only_remark: 0,
attachment: currentValues.attachment ?? '',
id: undefined,
});
}
const [Modal, modalApi] = useVbenModal({
fullscreenButton: false,
draggable: true,
onCancel() {
modalApi.close();
},
onConfirm: async () => {
const result = await formApi.validate();
if (!result.valid) {
return;
}
const values = await formApi.getValues();
modalApi.setState({ loading: true, confirmLoading: true });
try {
if (mode.value === 'report') {
await reportStoreBankCard({
...values,
store_id: values.store_id,
use_type: USE_TYPE_TRANSFER,
import_report_id: importReportId.value,
});
message.success(
importReportId.value > 0 ? '银行卡报备已导入' : '银行卡报备已提交',
);
} else {
await updateStoreBankCard({
...values,
id: values.id,
use_type: USE_TYPE_TRANSFER,
});
message.success('银行卡修改已提交');
}
gridApi.value?.reload();
modalApi.close();
} finally {
modalApi.setState({ loading: false, confirmLoading: false });
}
},
onOpenChange(isOpen: boolean) {
if (!isOpen) {
selectedCardKey.value = undefined;
importReportId.value = 0;
selectableOptions.value = [];
return;
}
const data = modalApi.getData<Record<string, any>>();
gridApi.value = data?.gridApi;
mode.value = data?.mode ?? 'report';
storeId.value = data?.storeId ?? 0;
storeName.value = data?.storeName ?? '';
reportInfo.value = data?.report ?? null;
importReportId.value = 0;
formApi.resetForm();
formApi.setValues({
store_id: storeId.value,
only_remark: onlyRemark.value ? 1 : 0,
use_type: USE_TYPE_TRANSFER,
use_type_display: '转账卡useType=2',
account_type: reportInfo.value?.account_type ?? '2',
...(reportInfo.value ?? {}),
id: reportInfo.value?.id,
});
loadSelectableCards();
},
});
const modalTitle = computed(() => {
const prefix = mode.value === 'report' ? '银行卡报备' : '修改银行卡';
return storeName.value ? `${prefix} - ${storeName.value}` : prefix;
});
async function refreshStatus() {
if (!reportInfo.value?.id) {
return;
}
modalApi.setState({ loading: true });
try {
const res = await queryStoreBankCardStatus({ id: reportInfo.value.id });
reportInfo.value = res;
formApi.setValues({
...res,
store_id: res.store_id,
use_type: USE_TYPE_TRANSFER,
use_type_display: '转账卡useType=2',
only_remark: res.can_full_update === false ? 1 : 0,
});
message.success('状态已刷新');
gridApi.value?.reload();
} finally {
modalApi.setState({ loading: false });
}
}
</script>
<template>
<Modal :title="modalTitle" class="w-[560px]">
<Alert
v-if="reportInfo?.report_status_text"
:message="`当前报备状态:${reportInfo.report_status_text}${reportInfo.audit_remarks ? `${reportInfo.audit_remarks}` : ''}`"
class="mb-4"
show-icon
type="info"
/>
<Alert
v-if="onlyRemark"
message="该卡已生效,仅可修改备注和摘要"
class="mb-4"
show-icon
type="warning"
/>
<Alert
v-if="showImportAlert"
message="已从历史报备导入,不会重复提交易票联,请上传本门店合同附件"
class="mb-4"
show-icon
type="info"
/>
<div v-if="showCardSelector && selectableOptions.length > 0" class="mb-4">
<div class="mb-2 text-sm text-gray-600">选择已有报备记录</div>
<Select
v-model:value="selectedCardKey"
allow-clear
class="w-full"
:options="selectableOptions"
placeholder="从历史报备记录选择(可跨门店)"
@change="handleSelectExistingCard"
/>
</div>
<Form />
<a
v-if="reportInfo?.id"
class="mt-2 inline-block text-primary"
@click.prevent="refreshStatus"
>
刷新报备状态
</a>
</Modal>
</template>

View File

@@ -0,0 +1,200 @@
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Alert, Button, Descriptions, message, Spin } from 'ant-design-vue';
import {
getStoreBankCardReportDetail,
migrateStoreBankCardToYiiStore,
syncStoreBankCardStatusByStore,
} from '../api';
import {
EPL_TRANSFER_ACCOUNT_TYPE_OPTIONS,
USE_TYPE_TRANSFER_TEXT,
} from '../config/constants';
const storeId = ref(0);
const storeName = ref('');
const statusInfo = ref<Record<string, any> | null>(null);
const loading = ref(false);
const syncing = ref(false);
const migrating = ref(false);
const hasReportRecord = computed(() => Boolean(statusInfo.value?.id));
const eplStateTextMap: Record<string, string> = {
'0': '无效',
'1': '有效',
'2': '待审核',
'3': '审核不通过',
};
const eplStateText = computed(() => {
const state = String(statusInfo.value?.epl_state ?? '');
return (
statusInfo.value?.epl_state_text ||
eplStateTextMap[state] ||
(hasReportRecord.value ? '未知' : '-')
);
});
const lastQueryAtText = computed(() => {
const ts = statusInfo.value?.last_query_at;
if (!ts) {
return '-';
}
const date = new Date(Number(ts) * 1000);
return Number.isNaN(date.getTime()) ? '-' : date.toLocaleString();
});
const accountTypeText = computed(() => {
if (statusInfo.value?.account_type_text) {
return statusInfo.value.account_type_text;
}
const accountType = String(statusInfo.value?.account_type ?? '');
const option = EPL_TRANSFER_ACCOUNT_TYPE_OPTIONS.find(
(item) => item.value === accountType,
);
return option?.label || '-';
});
async function loadStatus() {
if (!storeId.value) {
return;
}
loading.value = true;
try {
statusInfo.value = await getStoreBankCardReportDetail(storeId.value);
} catch {
statusInfo.value = null;
message.error('查询卡状态失败');
} finally {
loading.value = false;
}
}
async function handleSync() {
if (!storeId.value) {
return;
}
syncing.value = true;
try {
statusInfo.value = await syncStoreBankCardStatusByStore(storeId.value);
message.success('卡状态已同步');
} finally {
syncing.value = false;
}
}
async function handleMigrateToStore() {
if (!storeId.value) {
return;
}
if (!hasReportRecord.value) {
message.warning('暂无报备记录,无法迁移到门店');
return;
}
migrating.value = true;
try {
await migrateStoreBankCardToYiiStore({
store_id: storeId.value,
report_id: statusInfo.value?.id,
});
message.success('银行卡信息已迁移到门店');
} finally {
migrating.value = false;
}
}
const [Modal, modalApi] = useVbenModal({
fullscreenButton: false,
draggable: true,
showConfirmButton: false,
cancelText: '关闭',
onCancel() {
modalApi.close();
},
onOpenChange(isOpen: boolean) {
if (!isOpen) {
statusInfo.value = null;
return;
}
const data = modalApi.getData<Record<string, any>>();
storeId.value = data?.storeId ?? 0;
storeName.value = data?.storeName ?? '';
loadStatus();
},
});
const modalTitle = computed(() => {
const prefix = '查看卡状态';
return storeName.value ? `${prefix} - ${storeName.value}` : prefix;
});
</script>
<template>
<Modal :title="modalTitle" class="w-[560px]">
<Spin :spinning="loading">
<Alert
v-if="!loading && !statusInfo"
message="未找到银行卡信息"
show-icon
type="warning"
class="mb-4"
/>
<Alert
v-else-if="!loading && statusInfo && !hasReportRecord"
message="该卡尚未在易票联报备,以下为门店银行卡信息"
show-icon
type="info"
class="mb-4"
/>
<Descriptions v-if="statusInfo" bordered :column="1" size="small">
<Descriptions.Item label="报备状态">
{{ statusInfo.report_status_text || '未报备' }}
</Descriptions.Item>
<Descriptions.Item label="易票联状态">
{{ eplStateText }}
</Descriptions.Item>
<Descriptions.Item label="报备卡用途">
{{ statusInfo.use_type_text || USE_TYPE_TRANSFER_TEXT }}
</Descriptions.Item>
<Descriptions.Item label="账户类型">
{{ accountTypeText }}
</Descriptions.Item>
<Descriptions.Item label="开户人">
{{ statusInfo.bank_user_name || '-' }}
</Descriptions.Item>
<Descriptions.Item label="银行卡号">
{{ statusInfo.bank_card_masked || statusInfo.bank_card || '-' }}
</Descriptions.Item>
<Descriptions.Item label="开户行">
{{ statusInfo.bank_name || '-' }}
</Descriptions.Item>
<Descriptions.Item label="联行号">
{{ statusInfo.bank_no || '-' }}
</Descriptions.Item>
<Descriptions.Item label="审核意见">
{{ statusInfo.audit_remarks || '-' }}
</Descriptions.Item>
<Descriptions.Item label="最后查询时间">
{{ lastQueryAtText }}
</Descriptions.Item>
</Descriptions>
</Spin>
<div v-if="statusInfo" class="mt-4 flex justify-end gap-2">
<Button
v-if="hasReportRecord"
:loading="migrating"
@click="handleMigrateToStore"
>
迁移到门店
</Button>
<Button :loading="syncing" type="primary" @click="handleSync">
同步卡状态
</Button>
</div>
</Modal>
</template>

View File

@@ -0,0 +1,10 @@
export const EPL_TRANSFER_ACCOUNT_TYPE_OPTIONS = [
{ label: '对公账户', value: '1' },
{ label: '法人账户', value: '2' },
{ label: '授权对公账户', value: '3' },
{ label: '授权个人账户', value: '4' },
];
export const USE_TYPE_TRANSFER = '2';
export const USE_TYPE_TRANSFER_TEXT = '转账卡';

View File

@@ -0,0 +1,157 @@
import type { VbenFormProps } from '#/adapter/form';
import {
EPL_TRANSFER_ACCOUNT_TYPE_OPTIONS,
USE_TYPE_TRANSFER,
} from './constants';
export const bankCardReportFormProps: VbenFormProps = {
wrapperClass: 'grid-cols-12',
commonConfig: {
formItemClass: 'col-span-12',
componentProps: {
class: 'w-full',
},
},
layout: 'horizontal',
schema: [
{
component: 'VbenInput',
fieldName: 'id',
label: 'ID',
dependencies: { show: false, triggerFields: ['id'] },
},
{
component: 'VbenInput',
fieldName: 'store_id',
label: '门店ID',
dependencies: { show: false, triggerFields: ['store_id'] },
},
{
component: 'VbenInput',
fieldName: 'only_remark',
label: '仅备注',
dependencies: { show: false, triggerFields: ['only_remark'] },
},
{
component: 'VbenInput',
fieldName: 'use_type',
label: '报备卡用途',
defaultValue: USE_TYPE_TRANSFER,
dependencies: { show: false, triggerFields: ['use_type'] },
},
{
component: 'VbenInput',
componentProps: {
disabled: true,
placeholder: '转账卡useType=2',
},
fieldName: 'use_type_display',
label: '报备卡用途',
defaultValue: '转账卡useType=2',
dependencies: {
show: (values) => !values.only_remark,
triggerFields: ['only_remark'],
},
},
{
component: 'VbenInput',
componentProps: { placeholder: '请输入开户人姓名' },
fieldName: 'bank_user_name',
label: '开户人姓名',
rules: 'required',
dependencies: {
show: (values) => !values.only_remark,
triggerFields: ['only_remark'],
},
},
{
component: 'VbenInput',
componentProps: { placeholder: '请输入银行卡号' },
fieldName: 'bank_card',
label: '银行卡号',
rules: 'required',
dependencies: {
show: (values) => !values.only_remark,
triggerFields: ['only_remark'],
},
},
{
component: 'VbenInput',
componentProps: {
placeholder: '转账卡必填,参考易票联银行名称规范',
},
fieldName: 'bank_name',
label: '开户银行名称',
rules: 'required',
dependencies: {
show: (values) => !values.only_remark,
triggerFields: ['only_remark'],
},
},
{
component: 'RadioGroup',
componentProps: {
options: EPL_TRANSFER_ACCOUNT_TYPE_OPTIONS,
},
fieldName: 'account_type',
label: '账户类型',
rules: 'required',
defaultValue: '2',
dependencies: {
show: (values) => !values.only_remark,
triggerFields: ['only_remark'],
},
},
{
component: 'VbenInput',
componentProps: {
placeholder: '对公/授权对公账户必填;其他卡号非 62 开头必填',
},
fieldName: 'bank_no',
label: '银联行号',
dependencies: {
show: (values) => !values.only_remark,
triggerFields: ['only_remark', 'account_type'],
},
},
{
component: 'VbenInput',
componentProps: {
placeholder: '选填;对公/授权对公填营业执照号,法人/授权个人填身份证号',
},
fieldName: 'cert_no',
label: '证件号码',
dependencies: {
show: (values) => !values.only_remark,
triggerFields: ['only_remark', 'account_type'],
},
},
{
component: 'UploadOssFile',
componentProps: {
maxCount: 1,
},
fieldName: 'attachment',
label: '合同附件',
rules: 'required',
dependencies: {
show: (values) => !values.only_remark && !values.id,
triggerFields: ['only_remark', 'id'],
},
},
{
component: 'VbenInput',
componentProps: { placeholder: '备注' },
fieldName: 'remark',
label: '备注',
},
{
component: 'VbenInput',
componentProps: { placeholder: '摘要' },
fieldName: 'summary',
label: '摘要',
},
],
showDefaultActions: false,
};

View File

@@ -239,73 +239,6 @@ export const modalFormProps: VbenFormProps = {
suffix: () => '%',
defaultValue: 100,
},
{
component: 'Divider',
fieldName: '',
label: 'x',
rules: 'required',
hideLabel: true,
renderComponentContent: () => {
return {
default: () => {
return '银行卡信息';
},
};
},
},
{
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入开户人姓名',
},
fieldName: 'bank_user_name',
label: '开户人姓名',
rules: 'required',
},
{
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入银行卡号',
},
fieldName: 'bank_card',
label: '银行卡号',
rules: 'required',
},
{
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入开户行',
},
fieldName: 'bank_name',
label: '开户行',
rules: 'required',
},
{
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入银联行号',
},
fieldName: 'bank_no',
label: '银联行号',
},
{
component: 'RadioGroup',
componentProps: {
options: [
{ label: '对公', value: 1 },
{ label: '对私', value: 2 },
{ label: '存折', value: 5 },
],
placeholder: '请选择银行账户类型',
},
fieldName: 'bank_account_type',
label: '银行账户类型',
rules: 'required',
},
],
showDefaultActions: false,
};

View File

@@ -287,109 +287,6 @@ export const modalFormProps: VbenFormProps = {
// label: '颗粒药销售比例',
// rules: 'required',
// },
{
component: 'Divider',
fieldName: '',
label: 'x',
rules: 'required',
hideLabel: true,
dependencies: {
show: (values) => {
return values.id == null;
},
triggerFields: ['id'],
},
renderComponentContent: () => {
return {
default: () => {
return '银行卡信息';
},
};
},
},
{
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入开户人姓名',
},
dependencies: {
show: (values) => {
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_user_name',
label: '开户人姓名',
rules: 'required',
},
{
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入银行卡号',
},
dependencies: {
show: (values) => {
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_card',
label: '银行卡号',
rules: 'required',
},
{
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入开户行',
},
dependencies: {
show: (values) => {
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_name',
label: '开户行',
rules: 'required',
},
{
component: 'VbenInput',
formItemClass: 'col-span-6',
componentProps: {
placeholder: '请输入银联行号',
},
dependencies: {
show: (values) => {
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_no',
label: '银联行号',
},
{
component: 'RadioGroup',
componentProps: {
options: [
{ label: '对公', value: 1 },
{ label: '对私', value: 2 },
{ label: '存折', value: 5 },
],
placeholder: '请选择银行账户类型',
},
dependencies: {
show: (values) => {
return values.id == null;
},
triggerFields: ['id'],
},
fieldName: 'bank_account_type',
label: '银行账户类型',
rules: 'required',
},
],
showDefaultActions: false,
};

View File

@@ -46,6 +46,13 @@ export const gridOptions: VxeGridProps<RowType> = {
slots: { default: 'is_shipping_free' },
width: 120,
},
{
field: 'bank_card_report_status',
align: 'left',
title: '银行卡报备',
slots: { default: 'bank_card_report_status' },
width: 120,
},
{
// 订阅价格波动列(显示为开关)
field: 'subscribe_price_change',

View File

@@ -32,6 +32,9 @@ import DrugPriceModal from './components/DrugPriceModal.vue';
import BindConsultationModal from './components/BindConsultationModal.vue';
import StoreExternalFieldModal from './components/StoreExternalFieldModal.vue';
import SalespersonCommissionDrawer from './components/SalespersonCommissionDrawer.vue';
import BankCardReportModal from '#/views/system/store-bank-card/components/BankCardReportModal.vue';
import BankCardStatusModal from '#/views/system/store-bank-card/components/BankCardStatusModal.vue';
import { getStoreBankCardReportDetail } from '#/views/system/store-bank-card/api';
import { formOptions } from './config/search';
import { gridOptions } from './config/table';
@@ -99,6 +102,68 @@ const [StoreExternalFieldModalComponent, storeExternalFieldModalApi] = useVbenMo
connectedComponent: StoreExternalFieldModal,
});
const [BankCardReportModalComponent, bankCardReportModalApi] = useVbenModal({
connectedComponent: BankCardReportModal,
});
const [BankCardStatusModalComponent, bankCardStatusModalApi] = useVbenModal({
connectedComponent: BankCardStatusModal,
});
const openBankCardReport = async (row: any, update = false) => {
let report = null;
try {
report = await getStoreBankCardReportDetail(row.id);
} catch {
report = null;
}
bankCardReportModalApi.setData({
storeId: row.id,
storeName: row.name,
mode: update && report?.id ? 'update' : 'report',
report,
gridApi,
});
bankCardReportModalApi.open();
};
const openBankCardStatus = (row: any) => {
bankCardStatusModalApi.setData({
storeId: row.id,
storeName: row.name,
});
bankCardStatusModalApi.open();
};
function getBankCardReportTagColor(status: number) {
switch (Number(status)) {
case 1:
return 'processing';
case 2:
return 'success';
case 3:
case 4:
return 'error';
default:
return 'default';
}
}
function getBankCardReportTagText(row: any) {
return Number(row.bank_card_report_status) === 0
? '报备'
: row.bank_card_report_status_text || '未报备';
}
function handleBankCardReportColumnClick(row: any) {
const status = Number(row.bank_card_report_status ?? 0);
if (status === 1 || status === 2) {
openBankCardStatus(row);
return;
}
openBankCardReport(row, status !== 0);
}
const showStoreExternalFieldModal = (
row: any,
field: 'erp_id' | 'mes_id',
@@ -285,6 +350,8 @@ const handleSwitchClinicType = (row: any) => {
<template>
<Page auto-content-height title="诊所管理">
<FormModal />
<BankCardReportModalComponent />
<BankCardStatusModalComponent />
<QrCodePreviewModal />
<DrugPriceModalComponent />
<BindConsultationModalComponent />
@@ -438,6 +505,15 @@ const handleSwitchClinicType = (row: any) => {
@click="updateShippingFree(row.id)"
/>
</template>
<template #bank_card_report_status="{ row }">
<Tag
:color="getBankCardReportTagColor(row.bank_card_report_status)"
style="cursor: pointer"
@click="handleBankCardReportColumnClick(row)"
>
{{ getBankCardReportTagText(row) }}
</Tag>
</template>
<template #subscribe_price_change="{ row }">
<Tag
:color="row.subscribe_price_change === 0 ? 'success' : 'default'"
@@ -557,6 +633,30 @@ const handleSwitchClinicType = (row: any) => {
ifShow: isPlatformAdmin,
onClick: showDrugPriceModal.bind(null, row),
},
{
label: '查看卡状态',
type: 'link',
icon: 'ant-design:eye-outlined',
size: 'small',
auth: ['Super Admin', 'Admin'],
onClick: () => openBankCardStatus(row),
},
{
label: '银行卡报备',
type: 'link',
icon: 'ant-design:bank-outlined',
size: 'small',
auth: ['Super Admin', 'Admin'],
onClick: () => openBankCardReport(row, false),
},
{
label: '修改银行卡',
type: 'link',
icon: 'ant-design:credit-card-outlined',
size: 'small',
auth: ['Super Admin', 'Admin'],
onClick: () => openBankCardReport(row, true),
},
// {
// label: '推广员分成',
// type: 'link',