1. 优化医生、诊所的设置
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
This commit is contained in:
@@ -25,6 +25,7 @@ import { formatAddressDisplay } from '#/util/address-index';
|
||||
|
||||
import { createAdminGridOptions } from './table-config';
|
||||
import { getRoleMeta } from './role-meta';
|
||||
import DoctorCardModal from '#/views/doctor/doctor/components/DoctorCardModal.vue';
|
||||
|
||||
function formatAdminRegion(row: {
|
||||
province_id?: number;
|
||||
@@ -107,6 +108,16 @@ const [Modal, modalApi] = useVbenModal({
|
||||
},
|
||||
});
|
||||
|
||||
const [DoctorCardModals, DoctorCardModalApi] = useVbenModal({
|
||||
connectedComponent: DoctorCardModal,
|
||||
});
|
||||
|
||||
const openDoctorCard = (row: { doctor_id?: number }) => {
|
||||
if (!row.doctor_id) return;
|
||||
DoctorCardModalApi.setData({ su_id: row.doctor_id });
|
||||
DoctorCardModalApi.open();
|
||||
};
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
@@ -195,6 +206,7 @@ const copyToClipboard = async (text: string) => {
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
<DoctorCardModals />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
@@ -229,6 +241,17 @@ const copyToClipboard = async (text: string) => {
|
||||
<template #avatar="{ row }">
|
||||
<Image :src="row.avatar" height="30" width="30" />
|
||||
</template>
|
||||
<template #nick_name="{ row }">
|
||||
<Button
|
||||
v-if="meta.formType === 'doctor' && row.doctor_id"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="openDoctorCard(row)"
|
||||
>
|
||||
{{ row.nick_name }}
|
||||
</Button>
|
||||
<span v-else>{{ row.nick_name }}</span>
|
||||
</template>
|
||||
<template #region="{ row }">
|
||||
{{ formatAdminRegion(row) }}
|
||||
</template>
|
||||
|
||||
@@ -26,7 +26,7 @@ function buildColumns(formType: AdminFormType) {
|
||||
const cols: VxeGridProps<RowType>['columns'] = [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'nick_name', align: 'left', title: '名称' },
|
||||
{ field: 'nick_name', align: 'left', title: '名称', ...(formType === 'doctor' ? { slots: { default: 'nick_name' } } : {}) },
|
||||
{
|
||||
field: 'avatar',
|
||||
align: 'left',
|
||||
|
||||
@@ -77,6 +77,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
end_time: dayjs(values.end_time || '20:00', 'HH:mm'),
|
||||
// 确保订阅状态有值,如果后端没有返回则默认为0(订阅)
|
||||
subscribe_price_change: values.subscribe_price_change ?? 0,
|
||||
sync_admin_phone: false,
|
||||
});
|
||||
} else {
|
||||
formApi.setValues({
|
||||
|
||||
@@ -154,6 +154,23 @@ export const modalFormProps: VbenFormProps = {
|
||||
label: '联系人手机号',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
// 编辑时可选:同步修改药店管理员后台登录手机号
|
||||
component: 'Switch',
|
||||
formItemClass: 'col-span-12',
|
||||
fieldName: 'sync_admin_phone',
|
||||
label: '同步管理员登录号',
|
||||
defaultValue: false,
|
||||
componentProps: {
|
||||
checkedChildren: '是',
|
||||
unCheckedChildren: '否',
|
||||
},
|
||||
help: '勾选后,保存时将同步修改药店管理员后台登录手机号(需已开通后台)',
|
||||
dependencies: {
|
||||
show: (values) => values.id != null,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'PromoterPicker',
|
||||
formItemClass: 'col-span-6',
|
||||
|
||||
@@ -31,6 +31,16 @@ export async function bindOnlineConsultationApi(data: {
|
||||
}) {
|
||||
return requestClient.post<any>(`${prefix}bind-online-consultation`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定特色方默认挂号医生
|
||||
*/
|
||||
export async function updateSpecialPrescriptionDoctorApi(data: {
|
||||
id: number;
|
||||
special_prescription_doctor_id?: number | null;
|
||||
}) {
|
||||
return requestClient.post<any>(`${prefix}update-special-prescription-doctor`, data);
|
||||
}
|
||||
/**
|
||||
* 分页查询用户列表
|
||||
* @param data
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Form, FormItem, message, Select } from 'ant-design-vue';
|
||||
|
||||
import { getDoctorOptionApi, updateSpecialPrescriptionDoctorApi } from '../api';
|
||||
|
||||
const formState = ref({
|
||||
id: undefined as number | undefined,
|
||||
special_prescription_doctor_id: undefined as number | undefined,
|
||||
});
|
||||
|
||||
const doctorOptions = ref<Array<{ id: number; name: string }>>([]);
|
||||
const gridApiRef = ref<any>(null);
|
||||
const loading = ref(false);
|
||||
|
||||
async function loadDoctorOptions(storeId?: number) {
|
||||
if (!storeId) {
|
||||
doctorOptions.value = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const doctors = await getDoctorOptionApi(storeId);
|
||||
doctorOptions.value = doctors || [];
|
||||
} catch (error) {
|
||||
console.error('获取医生列表失败:', error);
|
||||
doctorOptions.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
loading.value = true;
|
||||
modalApi.setState({ confirmLoading: true });
|
||||
try {
|
||||
await updateSpecialPrescriptionDoctorApi({
|
||||
id: formState.value.id!,
|
||||
special_prescription_doctor_id: formState.value.special_prescription_doctor_id || null,
|
||||
});
|
||||
message.success('绑定成功');
|
||||
gridApiRef.value?.reload();
|
||||
modalApi.close();
|
||||
} catch {
|
||||
message.error('绑定失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
modalApi.setState({ confirmLoading: false });
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (isOpen) {
|
||||
const { values, gridApi } = modalApi.getData<Record<string, any>>();
|
||||
gridApiRef.value = gridApi;
|
||||
formState.value = {
|
||||
id: values?.id,
|
||||
special_prescription_doctor_id: values?.special_prescription_doctor_id || undefined,
|
||||
};
|
||||
await loadDoctorOptions(formState.value.id);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="绑定特色方默认医生" class="w-[500px]">
|
||||
<Form :model="formState" layout="vertical">
|
||||
<FormItem label="特色方默认挂号医生">
|
||||
<Select
|
||||
v-model:value="formState.special_prescription_doctor_id"
|
||||
:options="doctorOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
placeholder="请选择特色方默认挂号医生"
|
||||
allow-clear
|
||||
show-search
|
||||
:filter-option="(input: string, option: any) => option.name.toLowerCase().includes(input.toLowerCase())"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -56,6 +56,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
end_time: dayjs(values.end_time || '20:00', 'HH:mm'),
|
||||
// 确保订阅状态有值,如果后端没有返回则默认为0(订阅)
|
||||
subscribe_price_change: values.subscribe_price_change ?? 0,
|
||||
sync_admin_phone: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +179,23 @@ export const modalFormProps: VbenFormProps = {
|
||||
label: '联系人手机号',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
// 编辑时可选:同步修改诊所管理员后台登录手机号
|
||||
component: 'Switch',
|
||||
formItemClass: 'col-span-12',
|
||||
fieldName: 'sync_admin_phone',
|
||||
label: '同步管理员登录号',
|
||||
defaultValue: false,
|
||||
componentProps: {
|
||||
checkedChildren: '是',
|
||||
unCheckedChildren: '否',
|
||||
},
|
||||
help: '勾选后,保存时将同步修改诊所管理员后台登录手机号(需已开通后台)',
|
||||
dependencies: {
|
||||
show: (values) => values.id != null,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'PromoterPicker',
|
||||
formItemClass: 'col-span-6',
|
||||
|
||||
@@ -37,6 +37,13 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
width: 160,
|
||||
slots: { default: 'online_consultation_config' },
|
||||
},
|
||||
{
|
||||
field: 'special_prescription_config',
|
||||
align: 'left',
|
||||
title: '特色方配置',
|
||||
width: 140,
|
||||
slots: { default: 'special_prescription_config' },
|
||||
},
|
||||
{
|
||||
field: 'store_config_1',
|
||||
align: 'left',
|
||||
|
||||
@@ -34,6 +34,7 @@ import StoreBasicInfoCell from './components/cells/StoreBasicInfoCell.vue';
|
||||
import StoreConfigTogglesCell from './components/cells/StoreConfigTogglesCell.vue';
|
||||
import DrugPriceModal from './components/DrugPriceModal.vue';
|
||||
import BindConsultationModal from './components/BindConsultationModal.vue';
|
||||
import BindSpecialPrescriptionDoctorModal from './components/BindSpecialPrescriptionDoctorModal.vue';
|
||||
import StoreExternalFieldModal from './components/StoreExternalFieldModal.vue';
|
||||
import SalespersonCommissionDrawer from './components/SalespersonCommissionDrawer.vue';
|
||||
import BankCardReportModal from '#/views/system/store-bank-card/components/BankCardReportModal.vue';
|
||||
@@ -102,6 +103,11 @@ const [BindConsultationModalComponent, bindConsultationModalApi] = useVbenModal(
|
||||
connectedComponent: BindConsultationModal,
|
||||
});
|
||||
|
||||
const [BindSpecialPrescriptionDoctorModalComponent, bindSpecialPrescriptionDoctorModalApi] =
|
||||
useVbenModal({
|
||||
connectedComponent: BindSpecialPrescriptionDoctorModal,
|
||||
});
|
||||
|
||||
const [StoreExternalFieldModalComponent, storeExternalFieldModalApi] = useVbenModal({
|
||||
connectedComponent: StoreExternalFieldModal,
|
||||
});
|
||||
@@ -216,6 +222,14 @@ const showBindConsultationModal = (row: any) => {
|
||||
bindConsultationModalApi.open();
|
||||
};
|
||||
|
||||
const showBindSpecialPrescriptionDoctorModal = (row: any) => {
|
||||
bindSpecialPrescriptionDoctorModalApi.setData({
|
||||
values: row,
|
||||
gridApi,
|
||||
});
|
||||
bindSpecialPrescriptionDoctorModalApi.open();
|
||||
};
|
||||
|
||||
const deleteApi = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
@@ -369,6 +383,7 @@ const handleSwitchClinicType = (row: any) => {
|
||||
<QrCodePreviewModal />
|
||||
<DrugPriceModalComponent />
|
||||
<BindConsultationModalComponent />
|
||||
<BindSpecialPrescriptionDoctorModalComponent />
|
||||
<StoreExternalFieldModalComponent />
|
||||
<SalespersonCommissionDrawer ref="salespersonDrawerRef" />
|
||||
<Grid>
|
||||
@@ -512,6 +527,23 @@ const handleSwitchClinicType = (row: any) => {
|
||||
绑定
|
||||
</Button>
|
||||
</template>
|
||||
<template #special_prescription_config="{ row }">
|
||||
<div
|
||||
v-if="row.special_prescription_doctor_id"
|
||||
class="cursor-pointer hover:text-blue-500"
|
||||
@click="showBindSpecialPrescriptionDoctorModal(row)"
|
||||
>
|
||||
<div>医生ID:{{ row.special_prescription_doctor_id }}</div>
|
||||
</div>
|
||||
<Button
|
||||
v-else
|
||||
type="link"
|
||||
size="small"
|
||||
@click="showBindSpecialPrescriptionDoctorModal(row)"
|
||||
>
|
||||
绑定
|
||||
</Button>
|
||||
</template>
|
||||
<template #start-time="{ row }">
|
||||
早:<Tag color="success">{{ row.start_time }}</Tag>
|
||||
<br />
|
||||
@@ -595,6 +627,14 @@ const handleSwitchClinicType = (row: any) => {
|
||||
ifShow: isPlatformAdmin,
|
||||
onClick: showDrugPriceModal.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '绑定特色方医生',
|
||||
type: 'link',
|
||||
icon: 'ant-design:user-add-outlined',
|
||||
size: 'small',
|
||||
auth: ['Super Admin', 'Admin'],
|
||||
onClick: () => showBindSpecialPrescriptionDoctorModal(row),
|
||||
},
|
||||
{
|
||||
label: '查看卡状态',
|
||||
type: 'link',
|
||||
|
||||
@@ -3,16 +3,21 @@ import { onMounted, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button, Card, Input, InputNumber, message, Radio, Space, Switch, Tag } from 'ant-design-vue';
|
||||
import { Button, Card, Input, InputNumber, message, Radio, Space, Switch, Tabs, Tag } from 'ant-design-vue';
|
||||
|
||||
import type { QuickDiscountOption } from '#/utils/pricePercentAdjust';
|
||||
|
||||
import FormAvatar from '#/components/form/components/avatar.vue';
|
||||
|
||||
import { getSystemConfigList, saveSystemConfig } from './api';
|
||||
|
||||
defineOptions({ name: 'SystemConfig' });
|
||||
|
||||
const TAB_STORAGE_KEY = 'system_config_active_tab';
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const activeKey = ref('price_adjust');
|
||||
const scope = ref<'both' | 'sale_only'>('sale_only');
|
||||
const quickOptions = ref<QuickDiscountOption[]>([
|
||||
{ name: '九五折', value: 95 },
|
||||
@@ -25,6 +30,19 @@ const quickOptions = ref<QuickDiscountOption[]>([
|
||||
const newQuickName = ref('');
|
||||
const newQuickValue = ref<number | null>(null);
|
||||
const inputAutoAuditPass = ref(true);
|
||||
const miniprogramEmptyImage = ref('');
|
||||
|
||||
function readStoredTab() {
|
||||
const stored = localStorage.getItem(TAB_STORAGE_KEY);
|
||||
if (stored === 'price_adjust' || stored === 'input_audit' || stored === 'miniprogram') {
|
||||
activeKey.value = stored;
|
||||
}
|
||||
}
|
||||
|
||||
function handleTabChange(key: string | number) {
|
||||
activeKey.value = String(key);
|
||||
localStorage.setItem(TAB_STORAGE_KEY, String(key));
|
||||
}
|
||||
|
||||
function parseQuickOptions(raw: unknown): QuickDiscountOption[] {
|
||||
if (typeof raw === 'string') {
|
||||
@@ -63,6 +81,9 @@ async function load() {
|
||||
if (row.config_key === 'input_auto_audit_pass') {
|
||||
inputAutoAuditPass.value = row.config_value === '1' || row.config_value === true || row.config_value === 'true';
|
||||
}
|
||||
if (row.config_key === 'miniprogram_empty_image') {
|
||||
miniprogramEmptyImage.value = String(row.config_value || '');
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
@@ -100,6 +121,10 @@ async function handleSave() {
|
||||
config_key: 'input_auto_audit_pass',
|
||||
config_value: inputAutoAuditPass.value ? '1' : '0',
|
||||
},
|
||||
{
|
||||
config_key: 'miniprogram_empty_image',
|
||||
config_value: miniprogramEmptyImage.value,
|
||||
},
|
||||
]);
|
||||
message.success('保存成功');
|
||||
} finally {
|
||||
@@ -107,46 +132,64 @@ async function handleSave() {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load);
|
||||
onMounted(() => {
|
||||
readStoredTab();
|
||||
load();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="系统配置">
|
||||
<Card :loading="loading" title="订单调价配置">
|
||||
<div class="mb-6">
|
||||
<div class="mb-2 font-medium">订单打折时价格浮动范围</div>
|
||||
<Radio.Group v-model:value="scope">
|
||||
<Radio value="sale_only">仅售价浮动</Radio>
|
||||
<Radio value="both">供货价与售价一起浮动</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
<Card :loading="loading">
|
||||
<Tabs :active-key="activeKey" @change="handleTabChange">
|
||||
<Tabs.TabPane key="price_adjust" tab="订单调价">
|
||||
<div class="py-4">
|
||||
<div class="mb-6">
|
||||
<div class="mb-2 font-medium">订单打折时价格浮动范围</div>
|
||||
<Radio.Group v-model:value="scope">
|
||||
<Radio value="sale_only">仅售价浮动</Radio>
|
||||
<Radio value="both">供货价与售价一起浮动</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<div class="mb-2 font-medium">调价快捷选项(name 展示名,value 比例:100=原价,90=九折,120=涨20%)</div>
|
||||
<Space wrap class="mb-3">
|
||||
<Tag
|
||||
v-for="item in quickOptions"
|
||||
:key="item.name + item.value"
|
||||
closable
|
||||
:color="item.value < 100 ? 'processing' : 'warning'"
|
||||
@close="removeQuick(item)"
|
||||
>
|
||||
{{ item.name }}({{ item.value }}%)
|
||||
</Tag>
|
||||
</Space>
|
||||
<Space wrap>
|
||||
<Input v-model:value="newQuickName" placeholder="名称,如九折" style="width: 140px" />
|
||||
<InputNumber v-model:value="newQuickValue" :min="1" :max="500" placeholder="比例值" />
|
||||
<Button @click="addQuick">添加快捷项</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<div class="mb-2 font-medium">调价快捷选项(name 展示名,value 比例:100=原价,90=九折,120=涨20%)</div>
|
||||
<Space wrap class="mb-3">
|
||||
<Tag
|
||||
v-for="item in quickOptions"
|
||||
:key="item.name + item.value"
|
||||
closable
|
||||
:color="item.value < 100 ? 'processing' : 'warning'"
|
||||
@close="removeQuick(item)"
|
||||
>
|
||||
{{ item.name }}({{ item.value }}%)
|
||||
</Tag>
|
||||
</Space>
|
||||
<Space wrap>
|
||||
<Input v-model:value="newQuickName" placeholder="名称,如九折" style="width: 140px" />
|
||||
<InputNumber v-model:value="newQuickValue" :min="1" :max="500" placeholder="比例值" />
|
||||
<Button @click="addQuick">添加快捷项</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Button type="primary" :loading="saving" @click="handleSave">保存配置</Button>
|
||||
</Card>
|
||||
<Tabs.TabPane key="input_audit" tab="录入审核">
|
||||
<div class="py-4">
|
||||
<div class="mb-2 font-medium">诊所/医生信息预填录入后是否自动通过审核</div>
|
||||
<Switch v-model:checked="inputAutoAuditPass" checked-children="是" un-checked-children="否" />
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane key="miniprogram" tab="小程序">
|
||||
<div class="py-4">
|
||||
<div class="mb-2 font-medium">空状态图片</div>
|
||||
<div class="mb-2 text-sm text-gray-500">用于小程序列表等无数据时的占位图</div>
|
||||
<FormAvatar v-model:value="miniprogramEmptyImage" />
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
|
||||
<Card :loading="loading" class="mt-4" title="录入审核配置">
|
||||
<div class="mb-2 font-medium">诊所/医生信息预填录入后是否自动通过审核</div>
|
||||
<Switch v-model:checked="inputAutoAuditPass" checked-children="是" un-checked-children="否" />
|
||||
<div class="mt-4">
|
||||
<Button type="primary" :loading="saving" @click="handleSave">保存配置</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user