Эх сурвалжийг харах

Merge tag '3.0.0' into develop

cc12458 1 долоо хоног өмнө
parent
commit
7a3760e008

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "six-aio.web",
-  "version": "0.0.0",
+  "version": "3.0.0",
   "private": true,
   "type": "module",
   "scripts": {
@@ -35,7 +35,7 @@
     "pinia-plugin-persistedstate": "^4.1.1",
     "qrcode.vue": "^3.6.0",
     "svg-pathdata": "^7.1.0",
-    "vant": "4",
+    "vant": "~4.9.24",
     "vconsole": "^3.15.1",
     "vue": "^3.5.30",
     "vue-echarts": "^7.0.3",

+ 11 - 6
pnpm-lock.yaml

@@ -69,8 +69,8 @@ importers:
         specifier: ^7.1.0
         version: 7.2.0
       vant:
-        specifier: '4'
-        version: 4.9.21(vue@3.5.30(typescript@5.6.3))
+        specifier: ~4.9.24
+        version: 4.9.24(vue@3.5.30(typescript@5.6.3))
       vconsole:
         specifier: ^3.15.1
         version: 3.15.1
@@ -979,6 +979,9 @@ packages:
   '@vue/shared@3.5.30':
     resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==}
 
+  '@vue/shared@3.5.32':
+    resolution: {integrity: sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==}
+
   '@vue/tsconfig@0.5.1':
     resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==}
 
@@ -2573,8 +2576,8 @@ packages:
   util-deprecate@1.0.2:
     resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
 
-  vant@4.9.21:
-    resolution: {integrity: sha512-hXUoZMrLLjykimFRLDlGNd+K2iYSRh9YwLMKnsVdVZ+9inUKxpqnjhOqlZwocbnYkvJlS+febf9u9aJpDol4Pw==}
+  vant@4.9.24:
+    resolution: {integrity: sha512-tP1A7Vjzv1/B1ljb95Jhv9Q9w6acaaZDJvy6wcKrwGgY0gQZlg+FXLZH/AIKZBE3xvYGDUsv/M7AuGcr/Pqd6A==}
     peerDependencies:
       vue: ^3.0.0
 
@@ -3661,6 +3664,8 @@ snapshots:
 
   '@vue/shared@3.5.30': {}
 
+  '@vue/shared@3.5.32': {}
+
   '@vue/tsconfig@0.5.1': {}
 
   '@vueuse/components@13.9.0(vue@3.5.30(typescript@5.6.3))':
@@ -5316,11 +5321,11 @@ snapshots:
 
   util-deprecate@1.0.2: {}
 
-  vant@4.9.21(vue@3.5.30(typescript@5.6.3)):
+  vant@4.9.24(vue@3.5.30(typescript@5.6.3)):
     dependencies:
       '@vant/popperjs': 1.3.0
       '@vant/use': 1.6.0(vue@3.5.30(typescript@5.6.3))
-      '@vue/shared': 3.5.17
+      '@vue/shared': 3.5.32
       vue: 3.5.30(typescript@5.6.3)
 
   varint@6.0.0: {}

+ 2 - 2
src/components/AnalysisComponent.vue

@@ -57,8 +57,8 @@ const {
       </div>
       <slot name="exception">
         <div class="grid grid-rows-1 grid-cols-2 gap-8 m-6" v-if="exceptionType === 'list'">
-          <template  v-for="item in exception" :key="item.title">
-            <div class="card text-lg" v-if="item.cover">
+          <template v-for="item in exception" :key="item.title">
+            <div class="card text-lg" v-if="!filterEmptyException || item.cover">
               <div class="card__title mb-3 text-primary text-2xl font-bold">{{ item.title }}</div>
               <div class="card__content">
                 <div class="flex my-2 justify-center">

+ 5 - 2
src/composables/FloatPanel/FloatPanel.vue

@@ -24,7 +24,7 @@ const {
   full = false,
   closable = false,
 
-  contentDraggable = true,
+  contentDraggable = false,
 
   autoHeight = false,
 
@@ -233,6 +233,7 @@ defineExpose(instance);
     :style="overlayStyle"
     :show="height !== 0 || isClosing"
     :duration="durationSec"
+    :lock-scroll="false"
     @click="onClickOverlay"
   >
     <van-floating-panel
@@ -244,12 +245,13 @@ defineExpose(instance);
       :content-draggable="contentDraggable"
       @click.stop
       @height-change="onUpdateHeight"
+      :draggable="false"
     >
       <template #header>
         <div class="van-floating-panel__header">
           <div ref="panel-header">
             <van-icon v-if="closable" class="van-floating-panel__header_icon" name="cross" size="18" @click.stop="onClose()" />
-            <div class="van-floating-panel__header-bar"></div>
+            <div class="van-floating-panel__header-bar" v-show="title"></div>
             <slot name="header" :title="title">
               <div>{{ title }}</div>
             </slot>
@@ -288,6 +290,7 @@ defineExpose(instance);
       min-height: 30px;
     }
     .van-floating-panel__header-bar {
+      //opacity: 0;
       & + * {
         padding: calc($gap / 2) 0;
       }

+ 2 - 1
src/composables/FloatPanel/types.ts

@@ -117,8 +117,9 @@ export interface FloatPanelApi<P extends Record<string, any>, R = any> extends F
   /**
    * 打开面板并传入内容区 props;返回的 Promise 在关闭时 resolve。
    * @param props - 传给 `Content` 或默认插槽作用域的对象
+   * @param title
    */
-  open(props: P | void | null): Promise<R | void>;
+  open(props: P | void | null, title?: string): Promise<R | void>;
 
   /**
    * 关闭面板并 resolve `open` 的 Promise。

+ 4 - 2
src/composables/FloatPanel/useFloatPanel.ts

@@ -34,6 +34,7 @@ export function useFloatPanel<P extends Record<string, any> = Record<string, unk
   let dismissResult: R | void | undefined;
 
   const show = ref(false);
+  const title = ref('');
   /** 为 true 表示正在等待 FloatPanel `closed`,用于忽略重开后的陈旧 `closed` */
   const pendingCloseUnmount = ref(false);
   const panelOpenKey = ref(0);
@@ -60,7 +61,7 @@ export function useFloatPanel<P extends Record<string, any> = Record<string, unk
   }
 
   const api: FloatPanelApi<P, R> = {
-    open(props: P): Promise<R | void> {
+    open(props: P, text = ''): Promise<R | void> {
       pending?.resolve(void 0);
       pending = Promise.withResolvers<R | void>();
       dismissResult = undefined;
@@ -68,6 +69,7 @@ export function useFloatPanel<P extends Record<string, any> = Record<string, unk
       innerProps.value = props;
       panelOpenKey.value += 1;
       show.value = true;
+      title.value = text;
       return pending.promise;
     },
     close(result?: R) {
@@ -137,7 +139,7 @@ export function useFloatPanel<P extends Record<string, any> = Record<string, unk
 
         return h(
           __FloatPanel_vue,
-          { ref: floatPanelRef, key: panelOpenKey.value, closable: true, ...panelBindings },
+          { ref: floatPanelRef, key: panelOpenKey.value, closable: true, title: title.value, ...panelBindings },
           {
             header: slots.header,
             content: () => {

+ 30 - 9
src/lib/fabric/brush/class/PolygonBrush.ts

@@ -1,7 +1,7 @@
 import type { Canvas, FabricObjectProps, TBrushEventData, XY } from 'fabric';
 import type { PolygonShape } from '../types';
 
-import { Point, Polygon, Shadow } from 'fabric';
+import { Rect, Point, Polygon, Shadow } from 'fabric';
 import { ShapeBrush } from './ShapeBrush';
 import { mergeObjectOptions } from '@/lib/fabric';
 
@@ -114,6 +114,7 @@ type BrushInvalidReason = 'outOfRegion' | 'selfIntersection' | 'degenerateTriang
 export class PolygonBrush extends ShapeBrush<PolygonShape> {
   /** 有效顶点数达到该值即自动闭合落成;默认 100;三角形工具用 3。 */
   vertexTargetCount = 100;
+  private readonly rect: Rect;
 
   private get vc() {
     return this.shape === 'triangle' ? 3 : this.vertexTargetCount;
@@ -133,15 +134,17 @@ export class PolygonBrush extends ShapeBrush<PolygonShape> {
   areaEps = 13;
 
   private vertices: XY[] = [];
-  private moveHint: XY | null = null;
+  private moveHint: Point | null = null;
 
   constructor(canvas: Canvas) {
     super(canvas);
-  }
-
-  onMouseDoubleClick(pointer: Point, { e }: TBrushEventData): void {
-    if (!this.canvas._isMainEvent(e)) return;
-    this.tryCloseFromKeyboardOrDblClick({ x: pointer.x, y: pointer.y });
+    this.rect = new Rect({
+      width: this.decimate / 2,
+      height: this.decimate / 2,
+      fill: this.color,
+      selectable: false,
+      evented: false,
+    })
   }
 
   private closeSnapSq(): number {
@@ -200,6 +203,7 @@ export class PolygonBrush extends ShapeBrush<PolygonShape> {
     poly.setCoords();
     this._resetShadow();
     this.canvas.fire('object:created', { object: poly });
+    this.canvas.remove(this.rect);
     return true;
   }
 
@@ -220,6 +224,11 @@ export class PolygonBrush extends ShapeBrush<PolygonShape> {
     return ok;
   }
 
+  onMouseDoubleClick(pointer: Point, { e }: TBrushEventData): void {
+    if (!this.canvas._isMainEvent(e)) return;
+    this.tryCloseFromKeyboardOrDblClick({ x: pointer.x, y: pointer.y });
+  }
+
   onMouseDown(pointer: Point, { e }: TBrushEventData): void {
     if (!this.canvas._isMainEvent(e)) return;
     if (this._isOutSideContainer(pointer)) return;
@@ -254,6 +263,7 @@ export class PolygonBrush extends ShapeBrush<PolygonShape> {
     }
 
     this.vertices.push(r);
+    if (this.vertices.length === 1) this._renderVertex(r);
 
     if (this.vertices.length === this.vertexTargetCount) {
       if (this.isTriangleTool()) {
@@ -279,8 +289,12 @@ export class PolygonBrush extends ShapeBrush<PolygonShape> {
     this.refreshPreview();
   }
 
-  onMouseUp({ e }: TBrushEventData): boolean {
-    if (!this.canvas._isMainEvent(e)) return false;
+  onMouseUp(event: TBrushEventData): boolean {
+    if (this.moveHint) {
+      this.onMouseDown(this.moveHint, event)
+      this.moveHint = null;
+    }
+    if (!this.canvas._isMainEvent(event.e)) return false;
     if (this.isTriangleTool() && this.vertices.length >= 3) return false;
     if (this.isPolygonTool()) return this.vertices.length > 0;
     return this.vertices.length > 0 && this.vertices.length < 3;
@@ -309,10 +323,17 @@ export class PolygonBrush extends ShapeBrush<PolygonShape> {
   reset(): void {
     this.vertices = [];
     this.moveHint = null;
+    this.canvas.remove(this.rect);
     this.canvas.clearContext(this.canvas.contextTop);
     this.canvas.requestRenderAll();
   }
 
+  private _renderVertex(point: Point) {
+    this.rect.setXY(point);
+    this.rect.fill = this.color;
+    this.canvas.add(this.rect);
+  }
+
   protected override cleanupBrushState(): void {
     this.vertices = [];
     this.moveHint = null;

+ 3 - 3
src/loader/debug.loader.ts

@@ -4,10 +4,10 @@ type Lib = 'eruda' | 'vconsole';
 
 export default function debugLoader(tag = 'debug', ignoreDevelop = true): DEV.Loader {
   return async function (app, config) {
-    const debug = config.image.debug;
-    if (import.meta.env.DEV && ignoreDevelop && !debug) return;
-
     const query = getURLSearchParams();
+    const debug = config.image.debug || query.has(tag);
+    if (!debug && (import.meta.env.DEV || ignoreDevelop)) return;
+
     let lib = query.get(tag) as Lib;
     if (!lib && debug) lib = 'eruda';
     switch (lib) {

+ 0 - 5
src/loader/index.ts

@@ -4,13 +4,8 @@ import { createApp } from 'vue';
 import { Toast } from '@/platform/toast.ui';
 import { getApplicationMethod } from '@/request/api';
 
-import router from '@/router';
-import pinia from '@/stores';
-
 export default async function Loader(component: Component, ...loader: DEV.Loader[]) {
   const app = createApp(component);
-  app.use(router);
-  app.use(pinia);
 
   const toast = Toast.loading(500);
 

+ 2 - 0
src/loader/launch.loader.ts

@@ -5,6 +5,8 @@ const components = import.meta.glob('@/pages/**/*.vue');
 
 export default function launchLoader(container = '#app'): DEV.Loader {
   return async function (app, config) {
+    app.use(router);
+    app.use(pinia);
 
     let currentRoute = router.currentRoute.value.fullPath;
     if (currentRoute === '/') currentRoute = '/screen';

+ 4 - 1
src/modules/monitor/Annotator/Annotator.vue

@@ -76,7 +76,10 @@ async function complete() {
 async function picker(object: AnnotatorObject) {
   if (!object) return false;
   const result = await startAnnotator(object, props.picker);
-  if (typeof result === 'string') object.annotatorId = result;
+  if (typeof result === 'string') {
+    if (object.annotatorId === result) return;
+    object.annotatorId = result;
+  }
   const canvas = toValue(fabric.canvas);
   if (canvas) {
     canvas.remove(object);

+ 1 - 1
src/modules/monitor/annotator.page.vue

@@ -26,7 +26,7 @@ const edit = useTemplateRef<InstanceType<typeof MedicalReportEdit>>('edit');
 const [FloatPanel, panel] = useFloatPanel<MedicalData, MedicalData>(MedicalRecordPreview);
 const onPreview = () => {
   const data = edit.value?.preview();
-  if (data) panel.open(data as unknown as MedicalData);
+  if (data) panel.open(data as unknown as MedicalData, '诊断报告');
 };
 
 const completeProps = shallowRef<MedicalData>();

+ 1 - 0
src/modules/monitor/components/MedicalRecordPreview.vue

@@ -54,6 +54,7 @@ const updateReport = (url: string) => {
       <van-button block type="primary" plain :disabled="printing" @click="emits('cancel')">关闭</van-button>
       <van-button block type="primary" :loading="printing" loading-text="生成中…" @click="handle">打印</van-button>
     </div>
+    <div style="margin-bottom: 54px;"></div>
   </div>
 </template>
 

+ 8 - 5
src/modules/monitor/components/PictureUpload.vue

@@ -22,12 +22,12 @@ const url = defineModel('url', { default: '' });
 
 const support = ref(false);
 
-const dialog = useFile({ multiple: false, accept: 'image/*', reset: true });
+let dialog = useFile({ multiple: false, accept: 'image/*', reset: true });
 
 const onTrigger = () => {
   if (props.disabled) return;
   if (url.value) emits('delete', url.value);
-  else if (support.value) onTake({ capture: 'user' });
+  else if (support.value) onTake({ capture: 'user' }, true);
 };
 
 const onClick = (event: PointerEvent) => {
@@ -36,8 +36,11 @@ const onClick = (event: PointerEvent) => {
   else onTake();
 };
 
-const onTake = async (options?: UseFileDialogOptions) => {
-  let file = await dialog.open(options);
+const onTake = async (options?: UseFileDialogOptions, clear = false) => {
+  let file;
+  try { file = await dialog.open(options); } catch { return; } finally {
+    if (clear) dialog = useFile({ multiple: false, accept: 'image/*', reset: true });
+  }
   try {
     loading.value = true;
     url.value = (await props.update?.(file)) ?? URL.createObjectURL(file);
@@ -80,7 +83,7 @@ tryOnUnmounted(() => {
       <img class="size-full object-cover" v-if="url" :src="url" alt="" />
       <template v-else>
         <van-icon class="text-primary" name="photo-o" :size="50" />
-        <div class="text-sm mt-2 text-gray-300">
+        <div class="text-sm mt-2 text-gray-300 select-none">
           <span>点击上传</span>
           <span v-if="support"> 或 长按拍摄</span>
         </div>

+ 3 - 2
src/modules/monitor/composables/useAnnotatorFlow.ts

@@ -84,9 +84,10 @@ export function useAnnotatorFlow(data: Ref<MedicalModel>, options: UseAnnotatorF
       try {
         const values = await options.picker<string[]>({ title: `标注${picture.label}`, withDefault: false, props: { scope: key, seed } });
         seed = values[0];
+        object.strokeWidth = 2;
         object.stroke = hashStringToColor(seed);
       } catch {}
-      return seed;
+      return seed ?? false;
     };
     const result = await options.opener<AnnotatorExportObject>({
       type: 'annotator',
@@ -200,7 +201,7 @@ export function useAnnotatorFlow(data: Ref<MedicalModel>, options: UseAnnotatorF
         .join('<br>');
 
       row.columns[1] = value || (sub ? row.columns[2] : '');
-      row.exception = annotator.size > 1 || !row.columns[1].includes(row.columns[2]);
+      row.exception = annotator.size > 1 || !row.columns[1].split('<br>').includes(row.columns[2]);
     }
   }
 

+ 4 - 4
src/modules/monitor/composables/usePrint.ts

@@ -23,7 +23,7 @@ const defaultDefinitions = (info?: TDocumentDefinitions['info']) =>
     pageSize,
     pageOrientation: 'portrait',
     pageMargins,
-    info: { author: ``, subject: `病案报告`, keywords: '舌象 面象 标注', creator: '', producer: 'six', ...info },
+    info: { author: ``, subject: `诊断报告`, keywords: '舌象 面象 标注', creator: '', producer: 'six', ...info },
 
     defaultStyle: { font: 'NotoSansSC', fontSize: 14, lineHeight: 1.45, color: '#00000' },
     styles: {
@@ -53,13 +53,13 @@ export function usePrint(data: Ref<MedicalModel | undefined>) {
     source ??= data.value;
     if (!source) throw new Error(`数据为空`);
     try {
-      const title = `病案报告 ${source.code ?? Date.now()}`;
+      const title = `诊断报告 ${source.code ?? Date.now()}`;
       const footerCode = source.code ?? '';
       const report = getMedicalReportData(source?.report as any) as unknown as MedicalModel['report'];
       const content: TDocumentDefinitions['content'] = [
         { canvas: [{ type: 'rect', x: 0, y: 0, w: halfPageContentWidth * 2, h: 40, r: 4, color: palette.dark }] },
         {
-          text: '病 案 报 告',
+          text: '诊 断 报 告',
           fontSize: 18,
           lineHeight: 1,
           color: palette.green,
@@ -252,7 +252,7 @@ function extractImage(images: NonNullable<TDocumentDefinitions['images']>) {
       const src = `p` + cache.size;
       cache.set(value, src);
       cache.set(client, src);
-      images[src] = { url: client, headers: { Authorization: getToken() } };
+      images[src] = { url: client, headers: { Authorization: getToken() || '' } };
       return src;
     } else return value;
   };

+ 1 - 1
src/modules/monitor/medical-record.page.vue

@@ -42,7 +42,7 @@ const floatPanelType = ref<'edit-patient' | 'preview'>();
 const [FloatPanel, panel] = useFloatPanel<MedicalString, MedicalString>();
 const onPreview = (item: MedicalString, index: number) => {
   floatPanelType.value = 'preview';
-  panel.open(item);
+  panel.open(item, '诊断报告');
 };
 const onEdit = async (item: MedicalString, index: number) => {
   floatPanelType.value = 'edit-patient';

+ 3 - 3
src/modules/system/components/UpdatePassword.vue

@@ -8,7 +8,7 @@ export interface UpdatePasswordProps {
   token?: string;
 }
 
-const { title = '重置密码', pid, token } = defineProps<UpdatePasswordProps>();
+const { title = '修改密码', pid, token } = defineProps<UpdatePasswordProps>();
 const emits = defineEmits<{
   complete: [];
   cancel: [];
@@ -52,8 +52,8 @@ const validator: any = {
   required: { required: true, message: '请输入', trigger: 'onChange' },
   password: {
     trigger: ['onBlur', 'onSubmit'],
-    pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{6,18}$/,
-    message: '密码必须由大小写字母和数字三种组合,6-18 位字符',
+    pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{6,13}$/,
+    message: '密码必须由大小写字母和数字三种组合,6-13 位字符',
     validateEmpty: false,
   },
   again: {

+ 1 - 0
src/modules/system/logger.preview.vue

@@ -159,6 +159,7 @@ const collapseActive = ref<string[]>([]);
   --van-cell-text-color: hsl(var(--primary-hover) / 0.5);
   --van-cell-value-color: #fff;
   --van-cell-value-font-size: 14px;
+  max-height: calc(100vh - 30px);
   :deep(.van-cell__value) {
     flex: 2;
   }

+ 1 - 1
src/modules/system/user.page.vue

@@ -152,7 +152,7 @@ const onRolePickerComplete = (roles: any[]) => {
               <template #value>
                 <van-button square type="danger" text="删除" @click="onDelete(item, index)" />
                 <van-button square type="primary" text="修改" @click="onEdit(item, index)" />
-                <van-button square type="warning" text="重置密码" @click="onResetPassword(item, index)" />
+                <van-button square type="warning" text="修改密码" @click="onResetPassword(item, index)" />
               </template>
             </van-cell>
           </van-list>

+ 1 - 1
src/pages/510k.page.vue

@@ -26,7 +26,7 @@ const settingProps = reactive<Partial<PopoverProps> & { onSelect: any }>({
   closeOnClickAction: true,
   actions: [
     {
-      text: '重置密码',
+      text: '修改密码',
       async callback() {
         const pid = unref(Account.user)?.id;
         if (pid && (await password.open({ pid }))) logout().then();

+ 7 - 13
src/platform/file.ts

@@ -38,23 +38,17 @@ function filenameFromUrl(url: string): string {
  */
 export function downloadFromUrl(url: string, options?: DownloadFromUrlOptions): Promise<void> {
   return (async () => {
-    const res = await fetch(url, options?.fetchInit);
-    if (!res.ok) throw new Error(`下载失败: ${res.status} ${res.statusText}`);
-
-    const blob = await res.blob();
-    const filename = options?.filename ?? filenameFromContentDisposition(res.headers.get('content-disposition')) ?? filenameFromUrl(url);
-
-    const objectUrl = URL.createObjectURL(blob);
+    const filename = options?.filename ?? filenameFromUrl(url);
     try {
       const a = document.createElement('a');
-      a.href = objectUrl;
+      a.href = url;
       a.download = filename;
       a.rel = 'noopener';
       document.body.appendChild(a);
       a.click();
       a.remove();
     } finally {
-      URL.revokeObjectURL(objectUrl);
+
     }
   })();
 }
@@ -68,16 +62,16 @@ export async function printFromUrl(url: string, options?: { rollback?: boolean;
       } catch {
         window.AIO?.print?.(url);
       }
-    } catch (e) {
+    } catch (e: any) {
       Notify.warning(`打印失败 (${e.message})`, { duration: 1500 });
       closed = true;
     }
   } else {
     try {
       const current = window.open(url, '_blank');
-      closed = current.closed;
-      current.location.href;
-    } catch (e) {
+      closed = current?.closed ?? true;
+      if (current) current.location.href;
+    } catch (e: any) {
       Notify.warning(`无法打开窗口 (${e.message})`, { duration: 1500 });
       closed = true;
     }

+ 1 - 1
src/request/alova.ts

@@ -20,7 +20,7 @@ export default createAlova({
       try {
         if ( response.status >= 400 ) throw new Error(`${ response.statusText }(${ response.status })`);
 
-        if (response.headers.get('content-type') === 'text/html') return response.text();
+        if (response.headers.get('content-type')?.includes('text/html')) return response.text();
 
         let result = await response.json();
         if (method.url.startsWith(`${location.origin}${import.meta.env.BASE_URL}database`)) result = { code: 0, data: result };

+ 1 - 1
src/request/model/manage.model.ts

@@ -29,7 +29,7 @@ export interface UserModel {
   account: string;
   roles: RoleModel[];
   /**
-   * 需要重置密码
+   * 需要修改密码
    */
   resetPassword?: boolean;
 }

+ 2 - 2
src/request/model/medical-report.model.ts

@@ -1,7 +1,7 @@
 import type { MedicalReportData, MedicalReportModel } from '@/request/model/medical-record.model';
 import type { AnnotatorTreeNode } from '@/request/model/annotator.model';
-import type { AnnotatorExportObject, AnnotatorObject } from '@/lib/Annotator';
-import type { CropperExportObject } from '@/lib/Cropper';
+import type { AnnotatorExportObject, AnnotatorObject } from '@/modules/monitor/Annotator';
+import type { CropperExportObject } from '@/modules/monitor/Cropper';
 import type { AnalysisData, AnalysisModel } from '@/request/model/analysis.model';
 
 import dayjs from 'dayjs';

+ 1 - 1
src/request/model/scheme.model.ts

@@ -84,6 +84,6 @@ function fromSchemeGoods(data?: Data): SchemeGoodsProps | void {
     label: data?.buyName ?? '去购买',
     value: data?.buyUrl,
   } satisfies SchemeGoodsProps;
-  if (result.type === 'miniprogram') { result.value = data.miniprogram; }
+  if (result.type === 'miniprogram') { result.value = data?.miniprogram; }
   return result.value ? result : void 0;
 }

+ 1 - 1
src/stores/account.store.ts

@@ -38,7 +38,7 @@ export const useAccountStore = defineStore('account', () => {
 export function getToken() {
   try {
     const value = localStorage.getItem(`${import.meta.env.SIX_APP_NAME ?? '@six/unknown'}:account`);
-    return JSON.parse(value).token;
+    return JSON.parse(value ?? '').token;
   } catch {
     return void 0;
   }

+ 4 - 2
src/tools/url.tool.ts

@@ -1,3 +1,5 @@
+const url_prefix = __APP_URL__;
+
 export function getURLSearchParams(value?: string): URLSearchParams {
   value ??= `${ location.search }&${ location.hash.split('?')[ 1 ] || '' }`;
   return new URLSearchParams(value);
@@ -9,14 +11,14 @@ export function getURLSearchParamsByUrl(value: string): URLSearchParams {
 }
 
 export function getClientURL(value: string, origin = location.origin) {
-  if ( value?.startsWith(__APP_URL__) ) return value.replace(__APP_URL__, origin);
+  if ( url_prefix && value?.startsWith(url_prefix) ) return value.replace(url_prefix, origin);
   if ( !value || /^https?:\/\//.test(value) ) return value;
   if ( value.startsWith('~') ) { value = value.slice(1); }
   return fullURL(value, origin);
 }
 
 export function getServerURL(value: string, origin = location.origin) {
-  return value?.startsWith(origin) ? value.replace(origin, __APP_URL__) : value;
+  return value?.startsWith(origin) ? value.replace(origin, url_prefix) : value;
 }
 
 function fullURL(value: string, origin = location.origin) {

+ 1 - 1
vite.config.ts

@@ -19,7 +19,7 @@ export default defineConfig((configEnv) => {
     envPrefix: 'SIX_',
     define: {
       __APP_VERSION__: JSON.stringify(process.env.npm_package_version),
-      __APP_URL__: JSON.stringify(env.REQUEST_API_PROXY_URL),
+      __APP_URL__: configEnv.mode === 'development' ? JSON.stringify(env.REQUEST_API_PROXY_URL) : JSON.stringify(""),
       __FORBID_AUTO_PROCESS_PULSE_AGENCY__: argv.includes('--legacy-pulse-agency')
     },
     css: {