From c02f685d83c73936ddbd6f88ecd017cd8b580518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E7=90=A6?= Date: Mon, 6 Jul 2026 17:03:35 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E7=89=B9=E8=89=B2=E6=96=B9=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E8=BF=AD=E4=BB=A3(=E5=9B=BA=E5=AE=9A=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC)=EF=BC=8C=E5=9B=BA=E5=AE=9A=E4=BB=B7=E6=A0=BC?= =?UTF-8?q?=E7=9B=AE=E5=89=8D=E7=BB=9F=E4=B8=80=E4=BD=BF=E7=94=A8=E8=8D=AF?= =?UTF-8?q?=E5=93=81id=EF=BC=88-102=EF=BC=89=EF=BC=8C=E5=90=8E=E6=9C=9F?= =?UTF-8?q?=E4=BC=9A=E8=80=83=E8=99=91=E6=96=B0=E5=A2=9E=E7=89=B9=E8=89=B2?= =?UTF-8?q?=E6=96=B9=E7=9A=84=E6=97=B6=E5=80=99=E8=87=AA=E5=8A=A8=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E7=89=B9=E8=89=B2=E6=96=B9=E8=8D=AF?= =?UTF-8?q?=E5=93=81=202.=20=E9=80=80=E6=AC=BE=E7=9A=84=E6=97=B6=E5=80=99?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E9=80=80=E5=9B=9E=E5=88=86=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/Code-Standards.mdc | 10 + .gitignore | 2 +- apps/web-antd/src/api/core/file-gallery.ts | 12 + apps/web-antd/src/api/core/file-group.ts | 51 ++ apps/web-antd/src/app.vue | 9 + .../DoctorTransferFloat.vue | 847 ++++++++++++++++++ .../src/components/float-fab/FloatFab.vue | 194 ++++ .../src/components/sub-window/SubWindow.vue | 435 +++++++++ .../src/components/sub-window/constants.ts | 10 + .../composables/use-file-gallery-filter.ts | 32 + .../src/composables/use-file-group-cache.ts | 80 ++ .../business/clinic-salesperson/index.vue | 10 + .../src/views/business/order/api/order-ops.ts | 9 + .../business/order/product-order/api/index.ts | 8 + .../components/ProductOrderExportModal.vue | 13 + .../order/product-order/config/search.ts | 9 + .../order/product-order/config/table.ts | 2 +- .../business/order/product-order/index.vue | 242 ++++- .../api/index.ts | 23 +- .../components/DetailModal.vue | 51 +- .../index.vue | 62 +- .../special-prescription/api/index.ts | 25 + .../components/ChineseDrugEditor.vue | 54 +- .../components/FreeShippingDoseModal.vue | 115 +++ .../components/PriceConfigModal.vue | 316 +++---- .../components/SkuEditor.vue | 75 +- .../special-prescription/config/form.ts | 14 - .../special-prescription/config/table.ts | 48 +- .../business/special-prescription/index.vue | 193 +++- .../special-prescription/utils/flattenList.ts | 77 ++ .../business/store/settings/api/index.ts | 19 + .../settings/components/SalespersonCard.vue | 9 + .../SalespersonCommissionDrawer.vue | 103 ++- .../components/SalespersonCreateModal.vue | 48 +- .../components/SalespersonInvitedDrawer.vue | 67 ++ .../settings/components/SalespersonPicker.vue | 140 +++ .../business/store/settings/config/form.ts | 6 +- .../components/SalespersonTransferDrawer.vue | 19 +- .../useDoctorReceptionTransferImport.ts | 40 + .../views/doctor/doctor-reception/index.vue | 234 ++++- .../utils/parseSalespersonTransferDrugList.ts | 22 + .../withdrawal/config/account-change-table.ts | 7 +- .../src/views/finance/withdrawal/index.vue | 99 +- .../src/views/system/file-gallery/index.vue | 62 +- .../src/views/system/file-group/index.vue | 152 ++++ .../src/views/system/store/api/salesperson.ts | 9 + .../SalespersonCommissionDrawer.vue | 106 ++- 47 files changed, 3706 insertions(+), 464 deletions(-) create mode 100644 .cursor/rules/Code-Standards.mdc create mode 100644 apps/web-antd/src/api/core/file-group.ts create mode 100644 apps/web-antd/src/components/doctor-transfer-float/DoctorTransferFloat.vue create mode 100644 apps/web-antd/src/components/float-fab/FloatFab.vue create mode 100644 apps/web-antd/src/components/sub-window/SubWindow.vue create mode 100644 apps/web-antd/src/components/sub-window/constants.ts create mode 100644 apps/web-antd/src/composables/use-file-group-cache.ts create mode 100644 apps/web-antd/src/views/business/special-prescription/components/FreeShippingDoseModal.vue create mode 100644 apps/web-antd/src/views/business/special-prescription/utils/flattenList.ts create mode 100644 apps/web-antd/src/views/business/store/settings/components/SalespersonInvitedDrawer.vue create mode 100644 apps/web-antd/src/views/business/store/settings/components/SalespersonPicker.vue create mode 100644 apps/web-antd/src/views/doctor/doctor-reception/composables/useDoctorReceptionTransferImport.ts create mode 100644 apps/web-antd/src/views/doctor/doctor-reception/utils/parseSalespersonTransferDrugList.ts create mode 100644 apps/web-antd/src/views/system/file-group/index.vue diff --git a/.cursor/rules/Code-Standards.mdc b/.cursor/rules/Code-Standards.mdc new file mode 100644 index 00000000..9e5c2433 --- /dev/null +++ b/.cursor/rules/Code-Standards.mdc @@ -0,0 +1,10 @@ +--- +description: +alwaysApply: true +--- + +1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则 +2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于2行 +3. 有封装好的方法、组件需要复用,不要重复造轮子 +4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show) +5. 数据库的(created_at、updated_at、deleted_at)统一使用时间戳,不要使用字符串,并且我在查询器中一级格式化成字符串了,无需再次格式化 diff --git a/.gitignore b/.gitignore index b3814386..f73c2ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -49,5 +49,5 @@ vite.config.ts.* *.sln *.sw? .history -/.cursor/ /.mimocode/ +/.cursor/skills/ diff --git a/apps/web-antd/src/api/core/file-gallery.ts b/apps/web-antd/src/api/core/file-gallery.ts index 6f8f2015..aacc8ce3 100644 --- a/apps/web-antd/src/api/core/file-gallery.ts +++ b/apps/web-antd/src/api/core/file-gallery.ts @@ -11,6 +11,7 @@ export interface FileGalleryItem { type?: number; type_text?: string; type_icon?: string; + group_ids?: number[]; created_at?: string; original_name?: string; object_key?: string; @@ -36,6 +37,7 @@ export async function getFileGalleryList(params?: { page_size?: number; pid?: number; type?: number; + group_id?: number; keyword?: string; }) { return requestClient.get<{ @@ -76,3 +78,13 @@ export async function getFileTypes() { export async function syncFileGalleryFromOss() { return requestClient.post<{ synced: number; skipped: number; scanned: number }>(`${prefix}sync-oss`); } + +/** 文件加入自定义分组 */ +export async function addFileToGroup(file_id: number, group_id: number) { + return requestClient.post(`${prefix}add-to-group`, { file_id, group_id }); +} + +/** 文件移出自定义分组 */ +export async function removeFileFromGroup(file_id: number, group_id: number) { + return requestClient.post(`${prefix}remove-from-group`, { file_id, group_id }); +} diff --git a/apps/web-antd/src/api/core/file-group.ts b/apps/web-antd/src/api/core/file-group.ts new file mode 100644 index 00000000..36673a12 --- /dev/null +++ b/apps/web-antd/src/api/core/file-group.ts @@ -0,0 +1,51 @@ +import { requestClient } from '#/api/request'; + +const prefix = 'file-group/'; + +export interface FileGroupRecord { + id: number; + name: string; + sort: number; + created_at?: number; + updated_at?: number; +} + +export interface FileGroupOptionItem { + id: number; + name: string; + sort: number; +} + +/** 分组选项缓存结构 */ +export interface FileGroupCachePayload { + hash: string; + items: FileGroupOptionItem[]; +} + +/** + * 管理端分组列表 + */ +export async function getFileGroupList() { + return requestClient.get(`${prefix}list`); +} + +/** + * 带 hash 的分组选项:无变化时 result 为 0 + */ +export async function getFileGroupOptions(hash?: string) { + return requestClient.get(`${prefix}options`, { + params: hash ? { hash } : undefined, + }); +} + +export async function createFileGroup(data: Partial) { + return requestClient.post(`${prefix}create`, data); +} + +export async function updateFileGroup(data: Partial & { id: number }) { + return requestClient.post(`${prefix}update`, data); +} + +export async function deleteFileGroup(id: number) { + return requestClient.post(`${prefix}delete`, { id }); +} diff --git a/apps/web-antd/src/app.vue b/apps/web-antd/src/app.vue index 5d779892..b93e7d5c 100644 --- a/apps/web-antd/src/app.vue +++ b/apps/web-antd/src/app.vue @@ -9,6 +9,7 @@ import { App, ConfigProvider, theme } from 'ant-design-vue'; import { antdLocale } from '#/locales'; import { GlobalContextMenu } from '#/components/context-menu'; +import DoctorTransferFloat from '#/components/doctor-transfer-float/DoctorTransferFloat.vue'; import { useWebSocket } from '#/views/business/chat/composables/useWebSocket'; import { useChatStore } from '#/views/business/chat/stores/chat'; import { useUserStore } from '#/views/business/chat/stores/user'; @@ -75,6 +76,13 @@ watch(userInfoLoaded, (loaded) => { initWebsocket(); } }); + +/** 诊所医生账号显示传方悬浮窗(有 doctor_id 且绑定门店) */ +const showDoctorTransferFloat = computed(() => { + const info = vbenUserStore.userInfo as Record | null; + if (!info) return false; + return !!(info.doctor_id && info.store_id); +}); diff --git a/apps/web-antd/src/components/doctor-transfer-float/DoctorTransferFloat.vue b/apps/web-antd/src/components/doctor-transfer-float/DoctorTransferFloat.vue new file mode 100644 index 00000000..c5688cee --- /dev/null +++ b/apps/web-antd/src/components/doctor-transfer-float/DoctorTransferFloat.vue @@ -0,0 +1,847 @@ + + +