Quellcode durchsuchen

feat(@six/smart-pharmacy): 智慧药事系统第一版登录接口对接

cmj vor 1 Monat
Ursprung
Commit
d40acf858a

+ 9 - 0
apps/smart-pharmacy/public/database/menu.json

@@ -1,5 +1,6 @@
 [
   {
+    "id": "2410",
     "meta": {
       "icon": "ion:settings-outline",
       "order": 9997,
@@ -9,6 +10,7 @@
     "path": "/system",
     "children": [
       {
+        "id": "2412",
         "path": "/system/organization",
         "name": "MedicalOrganization",
         "meta": {
@@ -18,6 +20,7 @@
         "component": "/system/organization/list"
       },
       {
+        "id": "2413",
         "path": "/system/enterprise",
         "name": "SystemEnterprise",
         "meta": {
@@ -27,6 +30,7 @@
         "component": "/system/enterprise/list"
       },
       {
+        "id": "2414",
         "path": "/system/tisane",
         "name": "SystemTisane",
         "meta": {
@@ -36,6 +40,7 @@
         "component": "/system/tisane/list"
       },
       {
+        "id": "2415",
         "path": "/system/role",
         "name": "SystemRole",
         "meta": {
@@ -45,6 +50,7 @@
         "component": "/system/role/list"
       },
       {
+        "id": "2416",
         "path": "/system/user",
         "name": "SystemUser",
         "meta": {
@@ -56,6 +62,7 @@
     ]
   },
   {
+    "id": "2411",
     "meta": {
       "icon": "ion:settings-outline",
       "order": 9998,
@@ -65,6 +72,7 @@
     "path": "/prescription",
     "children": [
       {
+        "id": "2417",
         "path": "/prescription/management",
         "name": "PrescriptionManagement",
         "meta": {
@@ -74,6 +82,7 @@
         "component": "/prescription/management/list"
       },
       {
+        "id": "2418",
         "path": "/prescription/detail/:id",
         "name": "PrescriptionPreview",
         "meta": {

+ 24 - 6
apps/smart-pharmacy/src/api/index.ts

@@ -19,15 +19,27 @@ export const http = createRequestClient({
   transform(body, method) {
     /* prettier-ignore */
     if (body === null || typeof body !== 'object') return { code: 0, data: body, message: 'ok' };
-    const { ResultCode: code, ResultInfo: message, Data: data, ...r } = body;
-    const result = { code, message, data, ...r };
+
+    let code: number;
+    let message: string;
+    let data: Recordable;
+    let rest: Recordable = {};
+
+    if ('code' in body && ('msg' in body || 'message' in body)) {
+      const rawCode = body.code as number;
+      code = rawCode === 200 ? 0 : rawCode;
+      message = (body.msg ?? body.message ?? '') as string;
+      data = (body.data ?? (body.user ? body : void 0)) as Recordable;
+      const { code: _c, msg: _m, message: _msg, data: _d, ...r } = body;
+      rest = r;
+    }
+    const result = { code, message, data, ...rest };
 
     if (
       result.data?.TotalPageCount !== void 0 &&
       Array.isArray(result.data?.Items)
     ) {
       const {
-        // TotalPageCount: total,
         TotalRecordCount: total,
         PageIndex: page,
         PageSize: size,
@@ -42,11 +54,17 @@ export const http = createRequestClient({
 
     /* 额外处理登录接口 */
     if (method.meta?.login || method.meta?.authRole === 'login') {
-      const { token, ...data } = result.data ?? {};
+      const { access_token, token, expires_in, ...loginData } = result.data ?? {};
+      const accessToken = access_token ?? token;
       result.data = {
-        accessToken: token,
+        accessToken: accessToken
+          ? String(accessToken).startsWith('Bearer ')
+            ? accessToken
+            : `Bearer ${accessToken}`
+          : '',
         refreshToken: null,
-        ...data,
+        expiresIn: expires_in,
+        ...loginData,
       };
     }
     return result;

+ 12 - 31
apps/smart-pharmacy/src/api/method/access.ts

@@ -4,7 +4,11 @@ import type { SystemModel, TransformData } from '#/api';
 
 import { http } from '#/api';
 import { fromRole } from '#/api/model';
-import { fromMenus } from '#/api/model/menu';
+import {
+  filterMenusByPermissions,
+  fromTreeSelectMenus,
+  type TreeSelectMenuNode,
+} from '#/api/model/menu';
 
 export namespace AccessModel {
   export interface LoginParams {
@@ -21,42 +25,19 @@ export namespace AccessModel {
 }
 
 export function loginMethod(data: AccessModel.LoginParams) {
-  return http.post<AccessModel.LoginResponse>(`/login`, data, {
+  return http.post<AccessModel.LoginResponse>(`/manager/auth/token`, data, {
     meta: { login: true, visitor: true },
   });
 }
 
 export function getAccessMenuMethod(permissions?: string[]) {
-  return http.post<SystemModel.Menu[], TransformData[]>(
-    `/admin/menu/allMenu`,
-    void 0,
+  return http.get<SystemModel.Menu[], TreeSelectMenuNode[]>(
+    `/manager/system/menu/treeselect`,
     {
       transform(data) {
-        const menus = fromMenus(data);
+        const menus = fromTreeSelectMenus(data);
         if (!permissions?.length) return menus;
-        const forEach = (
-          permissions: Set<string>,
-          menus: SystemModel.Menu[],
-          parentMenu: SystemModel.Menu[] = [],
-        ) => {
-          for (const menu of menus) {
-            const id = menu.id;
-            if (permissions.has(id)) {
-              permissions.delete(id);
-              if (menu.type === 'menu') parentMenu.push(menu);
-              else if (menu.type === 'catalog' && menu.children?.length) {
-                const parent = { ...menu, children: [] };
-                parentMenu.push(parent);
-                forEach(permissions, menu.children, parent.children);
-              }
-            } else if (menu.type === 'catalog' && menu.children?.length) {
-              forEach(permissions, menu.children, parentMenu);
-            }
-            if (permissions.size === 0) break;
-          }
-          return parentMenu;
-        };
-        return forEach(new Set(permissions), menus);
+        return filterMenusByPermissions(permissions, menus);
       },
     },
   );
@@ -64,7 +45,7 @@ export function getAccessMenuMethod(permissions?: string[]) {
 
 export function getUserInfoMethod(token?: string) {
   return http.get<UserInfo & { roles: SystemModel.Role[] }, TransformData>(
-    `/getInfo`,
+    `/manager/system/user/getInfo`,
     {
       headers: { Authorization: token },
       transform(data, headers) {
@@ -82,5 +63,5 @@ export function getUserInfoMethod(token?: string) {
       },
     },
   );
-
+  
 }

+ 95 - 0
apps/smart-pharmacy/src/api/model/menu.ts

@@ -1,5 +1,100 @@
 import type { SystemModel, TransformData } from '#/api';
 
+import accessMenuRoutes from '../../../public/database/menu.json';
+
+export interface TreeSelectMenuNode {
+  id: number | string;
+  label: string;
+  children?: TreeSelectMenuNode[];
+}
+
+type AccessMenuRoute = TransformData & {
+  id?: string;
+  children?: AccessMenuRoute[];
+};
+
+const accessMenuRouteMap = buildAccessMenuRouteMap(
+  accessMenuRoutes as AccessMenuRoute[],
+);
+
+function buildAccessMenuRouteMap(
+  menus: AccessMenuRoute[],
+  map = new Map<string, AccessMenuRoute>(),
+) {
+  for (const menu of menus) {
+    if (menu.id != null) map.set(String(menu.id), menu);
+    if (menu.children?.length) buildAccessMenuRouteMap(menu.children, map);
+  }
+  return map;
+}
+
+/** 将 treeselect 接口数据与本地路由配置合并为可生成路由的菜单 */
+export function fromTreeSelectMenus(
+  nodes: TreeSelectMenuNode[],
+): SystemModel.Menu[] {
+  if (!Array.isArray(nodes)) return [];
+
+  return nodes
+    .map((node) => toAccessMenu(node))
+    .filter((menu): menu is SystemModel.Menu => menu != null)
+    .sort((a, b) => (a.meta.order ?? 999_999) - (b.meta.order ?? 999_999));
+}
+
+function toAccessMenu(node: TreeSelectMenuNode): SystemModel.Menu | null {
+  const route = accessMenuRouteMap.get(String(node.id));
+  if (!route) return null;
+
+  const children = node.children?.length
+    ? fromTreeSelectMenus(node.children)
+    : undefined;
+
+  const hasChildren = !!children?.length;
+
+  return {
+    id: String(node.id),
+    type: hasChildren ? 'catalog' : 'menu',
+    name: route.name,
+    path: route.path,
+    component: route.component,
+    meta: fromMenuMeta({
+      ...route.meta,
+      title: node.label || route.meta?.title,
+    }),
+    children: hasChildren ? children : undefined,
+  } satisfies SystemModel.Menu;
+}
+
+export function filterMenusByPermissions(
+  permissions: Array<number | string>,
+  menus: SystemModel.Menu[],
+): SystemModel.Menu[] {
+  const permissionSet = new Set(permissions.map(String));
+
+  const walk = (
+    items: SystemModel.Menu[],
+    parentMenu: SystemModel.Menu[] = [],
+  ) => {
+    for (const menu of items) {
+      const id = String(menu.id);
+      if (permissionSet.has(id)) {
+        permissionSet.delete(id);
+        if (menu.type === 'menu') parentMenu.push(menu);
+        else if (menu.type === 'catalog' && menu.children?.length) {
+          const parent = { ...menu, children: [] as SystemModel.Menu[] };
+          parentMenu.push(parent);
+          walk(menu.children, parent.children);
+        }
+      } else if (menu.type === 'catalog' && menu.children?.length) {
+        walk(menu.children, parentMenu);
+      }
+      if (permissionSet.size === 0) break;
+    }
+    return parentMenu;
+  };
+
+  return walk(menus);
+}
+
 export function fromMenus(menus: TransformData[]): SystemModel.Menu[] {
   const getType = (menu: TransformData): SystemModel.Menu['type'] => {
     if (menu.type) return menu.type;

+ 18 - 8
apps/smart-pharmacy/src/layouts/basic.vue

@@ -41,24 +41,34 @@ async function handleLogout() {
 
 const dropdownMenus = computed(() => [
   {
-    text: $t('system.title'),
-    icon: 'ion:settings-outline',
-    handler: () => router.push('/system'),
+    text: $t('system.medicalInstitution._'),
+    icon: 'mdi:hospital-building',
+    handler: () => router.push('/system/organization'),
   },
   {
-    text: $t('system.role.title'),
+    text: $t('system.enterprise.title'),
     icon: 'mdi:account-group',
+    handler: () => router.push('/system/enterprise'),
+  },
+  {
+    text: $t('system.tisane.title'),
+    icon: 'mdi:pill-multiple',
+    handler: () => router.push('/system/tisane'),
+  },
+  {
+    text: $t('system.role.title'),
+    icon: 'charm:organisation',
     handler: () => router.push('/system/role'),
   },
   {
     text: $t('system.user.title'),
-    icon: 'charm:organisation',
+    icon: 'mdi:account-group',
     handler: () => router.push('/system/user'),
   },
   {
-    text: $t('system.organization.title'),
-    icon: 'charm:organisation',
-    handler: () => router.push('/system/organization'),
+    text: $t('prescription.title'),
+    icon: 'ion:settings-outline',
+    handler: () => router.push('/prescription/management'),
   },
 ]);
 

+ 10 - 0
apps/smart-pharmacy/src/locales/langs/zh-CN/system.json

@@ -64,5 +64,15 @@
     "remark": "备注",
     "relatedOrganization": "关联医疗机构",
     "relatedEnterprise": "关联企业"
+  },
+  "medicalInstitution": {
+    "_": "医疗机构",
+    "title": "医疗机构管理",
+    "name": "医疗机构名称",
+    "type": "医疗机构类型",
+    "code": "医疗机构代码",
+    "createTime": "创建时间",
+    "createUser": "创建者",
+    "remark": "备注"
   }
 }

+ 1 - 4
apps/smart-pharmacy/src/router/access.ts

@@ -32,10 +32,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
         });
       }, 500);
       close = () => clearTimeout(id);
-      const res = await fetch('/database/menu.json');
-      const data = await res.json();
-      const menus = data;
-      // const menus = await getAccessMenuMethod(permissions).catch(() => []);
+      const menus = await getAccessMenuMethod(permissions).catch(() => []);
       close?.();
       return menus as any;
     },

+ 11 - 7
apps/smart-pharmacy/src/views/prescription/management/detail.vue

@@ -342,16 +342,20 @@ function goBack() {
                   <div v-for="(item, index) in processFlowDetails" :key="index" class="relative pb-4 last:pb-0">
                     <div class="absolute -left-[9px] w-4 h-4 bg-green-500 rounded-full border-2 border-white"></div>
                     <div class="ml-2">
-                      <div class="text-sm font-medium text-gray-800">{{ item.status }}</div>
-                      <div class="text-xs text-gray-500 mt-1">操作人: {{ item.operator }}</div>
-                      <div class="text-xs text-gray-500">操作时间: {{ item.operationTime }}</div>
+                      <div style="display: flex;align-items: center;justify-content: space-between;padding-right: 100px;">
+                        <div class="text-sm font-medium text-gray-800">{{ item.status }}</div>
+                        <div class="text-xs text-gray-500">操作时间: {{ item.operationTime }}</div>
+                      </div>
+                      <div style="display: flex;align-items: center;justify-content: space-between;padding-right: 100px;">
+                        <div class="text-xs text-gray-500 mt-1">操作人: {{ item.operator }}</div>
+                        <div class="mt-1">
+                          <span class="text-xs text-gray-500 mt-1">环节照片/视频:</span><a href="#" class="text-xs text-blue-500 hover:text-blue-700"> 查看照片</a>
+                        </div>
+                      </div>
                       <div v-if="item.water" class="text-xs text-gray-500">加水量: {{ item.water }}</div>
                       <div v-if="item.weightCheck" class="text-xs text-gray-500">复核重量: {{ item.weightCheck }}</div>
                       <div v-if="item.weight" class="text-xs text-gray-500">调配重量: {{ item.weight }}</div>
-                      <div v-if="item.note" class="text-xs text-gray-500">备注: {{ item.note }}</div>
-                      <div v-if="item.hasPhoto" class="mt-1">
-                        <a href="#" class="text-xs text-blue-500 hover:text-blue-700">环节照片/视频: 查看照片</a>
-                      </div>
+                      <div v-if="item.note" class="text-xs text-gray-500 mt-1">备注: {{ item.note }}</div>
                     </div>
                   </div>
                 </div>

+ 1 - 0
apps/smart-pharmacy/vite.config.mts

@@ -9,6 +9,7 @@ export default defineConfig(async () => {
           '/wf': {
             changeOrigin: true,
             target: `https://wx.hzliuzhi.com:4433`,
+            rewrite: (path) => path.replace(/^\/wf/, ''),
             ws: true,
           },
           '/api': {