Kaynağa Gözat

📦 拆分通用组件

快乐的梦鱼 3 ay önce
ebeveyn
işleme
a7a36136bd
81 değiştirilmiş dosya ile 42 ekleme ve 5046 silme
  1. 3 3
      package-lock.json
  2. 1 0
      package.json
  3. 1 1
      src/api/RequestModules.ts
  4. 0 44
      src/components/VNodeRenderer.vue
  5. 0 142
      src/components/content/CommonCatalog.vue
  6. 2 3
      src/components/content/CommonListBlock.vue
  7. 0 63
      src/components/content/ImageGrid.vue
  8. 0 34
      src/components/content/ImageSwiper.vue
  9. 0 0
      src/components/content/MapConfig.ts
  10. 0 80
      src/components/content/SimplePageContentLoader.vue
  11. 1 1
      src/components/content/SimplePointedMap.vue
  12. 3 3
      src/components/controls/Dropdown.vue
  13. 0 90
      src/components/display/SimplePopup.vue
  14. 0 24
      src/components/display/SimpleRemoveRichHtml.vue
  15. 0 146
      src/components/display/SimpleRichHtml.vue
  16. 0 57
      src/components/display/SimpleScrollView.vue
  17. 0 29
      src/components/dynamicf/ActionRender.ts
  18. 0 35
      src/components/dynamicf/ActionRender.vue
  19. 0 50
      src/components/dynamicf/CascaderFormItem.ts
  20. 0 165
      src/components/dynamicf/CascaderFormItem.vue
  21. 0 56
      src/components/dynamicf/CheckBoxToInt.vue
  22. 0 8
      src/components/dynamicf/CheckBoxValue.ts
  23. 0 51
      src/components/dynamicf/CheckBoxValue.vue
  24. 0 30
      src/components/dynamicf/Display/ShowDateOrNull.vue
  25. 0 86
      src/components/dynamicf/Display/ShowImageList.vue
  26. 0 76
      src/components/dynamicf/Display/ShowImageOrNull.vue
  27. 0 69
      src/components/dynamicf/Display/ShowInList.vue
  28. 0 35
      src/components/dynamicf/Display/ShowMomentOrNull.vue
  29. 0 48
      src/components/dynamicf/Display/ShowTagList.vue
  30. 0 73
      src/components/dynamicf/Display/ShowValueOrNull.vue
  31. 0 37
      src/components/dynamicf/Display/StateRenderer.vue
  32. 0 88
      src/components/dynamicf/Dropdown/IdAsValueDropdown.ts
  33. 0 253
      src/components/dynamicf/Dropdown/IdAsValueDropdown.vue
  34. 0 336
      src/components/dynamicf/Dropdown/IdAsValueTreeDropdown.vue
  35. 0 109
      src/components/dynamicf/Editor/EditorWrapper.vue
  36. 0 127
      src/components/dynamicf/IdAsValueTree.ts
  37. 0 179
      src/components/dynamicf/IdAsValueTree.vue
  38. 0 194
      src/components/dynamicf/Map/AddressSercher.vue
  39. BIN
      src/components/dynamicf/Map/Maker.png
  40. 0 112
      src/components/dynamicf/Map/MapPointPicker.vue
  41. 0 69
      src/components/dynamicf/NumberRange.vue
  42. 0 116
      src/components/dynamicf/PasswordStrengthMeter.vue
  43. 0 37
      src/components/dynamicf/PasswordWithStrengthInput.vue
  44. 0 29
      src/components/dynamicf/RadioValue.ts
  45. 0 77
      src/components/dynamicf/RadioValue.vue
  46. 0 25
      src/components/dynamicf/SelectValue.ts
  47. 0 74
      src/components/dynamicf/SelectValue.vue
  48. 0 189
      src/components/dynamicf/Sign.vue
  49. 0 99
      src/components/dynamicf/SimpleEditDynamicStringList.vue
  50. 0 128
      src/components/dynamicf/SimpleListDynamicForm.vue
  51. 0 42
      src/components/dynamicf/SimpleSelectFormItem.ts
  52. 0 69
      src/components/dynamicf/SimpleSelectFormItem.vue
  53. 0 156
      src/components/dynamicf/UploadImageFormItem.ts
  54. 0 218
      src/components/dynamicf/UploadImageFormItem.vue
  55. 0 194
      src/components/dynamicf/UploadVideoFormItem.vue
  56. 0 6
      src/components/dynamicf/WhiteSpace.ts
  57. 0 16
      src/components/dynamicf/WhiteSpace.vue
  58. 0 28
      src/components/dynamicf/WrapperRangePicker.vue
  59. 0 24
      src/components/dynamicf/WrapperTimeRangePicker.vue
  60. 0 103
      src/components/dynamicf/index.ts
  61. 0 98
      src/components/error/ErrorReporter.vue
  62. 0 28
      src/components/error/ErrorReporterIs.ts
  63. 2 3
      src/components/parts/EmptyToRecord.vue
  64. 1 1
      src/components/parts/TitleDescBlock.vue
  65. 0 9
      src/composeable/LoaderCommon.ts
  66. 0 18
      src/composeable/MemorizeVar.ts
  67. 0 65
      src/composeable/SimpleDataLoader.ts
  68. 0 128
      src/composeable/SimplePagerDataLoader.ts
  69. 0 14
      src/composeable/WindowOnUnLoad.ts
  70. 5 22
      src/main.ts
  71. 2 3
      src/pages/admin.vue
  72. 1 1
      src/pages/admin/seminar.vue
  73. 2 2
      src/pages/admin/works.vue
  74. 1 1
      src/pages/editor-test.vue
  75. 1 1
      src/pages/forms/form.vue
  76. 4 3
      src/pages/forms/ich.vue
  77. 1 1
      src/pages/forms/inheritor.vue
  78. 1 2
      src/pages/forms/seminar.vue
  79. 4 1
      src/pages/forms/works.vue
  80. 5 8
      src/pages/inheritor.vue
  81. 1 1
      src/pages/login.vue

+ 3 - 3
package-lock.json

@@ -12010,9 +12010,9 @@
       "license": "MIT"
     },
     "node_modules/sass": {
-      "version": "1.91.0",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.91.0.tgz",
-      "integrity": "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA==",
+      "version": "1.87.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.87.0.tgz",
+      "integrity": "sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw==",
       "devOptional": true,
       "license": "MIT",
       "dependencies": {

+ 1 - 0
package.json

@@ -16,6 +16,7 @@
   },
   "dependencies": {
     "@imengyu/imengyu-utils": "^0.0.14",
+    "@imengyu/imengyu-web-shared": "^0.0.1",
     "@imengyu/js-request-transform": "^0.3.5",
     "@imengyu/vue-dynamic-form": "^0.1.1",
     "@imengyu/vue-scroll-rect": "^0.1.3",

+ 1 - 1
src/api/RequestModules.ts

@@ -16,7 +16,7 @@ import {
   defaultResponseDataGetErrorInfo, defaultResponseDataHandlerCatch, 
   RequestResponse
 } from "@imengyu/imengyu-utils/dist/request";
-import { logError } from "@/components/error/ErrorReporterIs";
+import { logError } from "@imengyu/imengyu-web-shared";
 import type { DataModel, KeyValue, NewDataModel } from "@imengyu/js-request-transform";
 import { appendGetUrlParams, appendPostParams } from "@imengyu/imengyu-utils/dist/request/utils/Utils";
 import { useAuthStore } from "@/stores/auth";

+ 0 - 44
src/components/VNodeRenderer.vue

@@ -1,44 +0,0 @@
-<script lang="ts">
-import { defineComponent, h, type PropType, type VNode } from 'vue'
-
-export default defineComponent({
-  name: 'VNodeRenderer',
-  props: {
-    vnode: {
-      type: Object as PropType<VNode>,
-      default: null
-    },
-    render: {
-      type: Function as PropType<(data: Record<string, unknown>|null) => VNode>,
-      default: null
-    },
-    renderChild: {
-      type: Boolean,
-      default: null
-    },
-    data: {
-      type: Object as PropType<Record<string, unknown>>,
-      default: null
-    },
-  },
-  render() {
-    if(this.vnode) {
-      if (typeof this.vnode === 'string')
-        return h('div', this.vnode);
-      const props = this.vnode.props;
-      if(props)
-        for(let key in this.data)
-          props[key] = this.data[key];
-      return this.renderChild ? 
-        h('div', { style: { height: '100%'} }, [
-          this.vnode
-        ]) : 
-        this.vnode;
-    }
-    if(this.render) {
-      return this.render(this.data);
-    }
-    return h('div');
-  }
-})
-</script>

+ 0 - 142
src/components/content/CommonCatalog.vue

@@ -1,142 +0,0 @@
-<script setup lang="ts">
-import SimpleScrollView from '../display/SimpleScrollView.vue';
-import { ref, watch, type PropType } from 'vue';
-
-export interface CatalogItem {
-  title: string,
-  level: number,
-  scrollPos: number, 
-  anchor: string,
-}
-
-const emit = defineEmits([	
-  "goToItem"	
-])
-const props = defineProps({	
-  items: {
-    type: Object as PropType<CatalogItem[]>,
-    default: () => []
-  },
-  scrollContainer: {
-    type: Object as PropType<HTMLElement|null>,
-    default: () => null,
-  },
-})
-
-const activeIndex = ref(-1);
-
-function handlerContainerScroll(e: Event) {
-  const container = e.target as HTMLElement;
-  const scrollTop = container.scrollTop;
-
-  activeIndex.value = 0;
-  for (let i = props.items.length - 1; i >= 0; i--) {
-    const item = props.items[i];
-    if (scrollTop >= item.scrollPos) {
-      activeIndex.value = i;
-      break;
-    }
-  }
-}
-function handlerItemClick(item: CatalogItem) {
-  if (item.anchor) {
-    const el = document.getElementById(item.anchor);
-    if (el) {
-      el.scrollIntoView({ behavior: 'smooth' });
-    }
-  }
-  emit('goToItem', item);
-}
-
-watch(() => props.scrollContainer, (newVal, oldVal) => {
-  if (oldVal && oldVal instanceof HTMLElement)
-    oldVal.removeEventListener('scroll', handlerContainerScroll);
-  if (newVal && newVal instanceof HTMLElement)
-    newVal.addEventListener('scroll', handlerContainerScroll);
-}, { immediate: true });
-
-</script>
-
-<template>
-  <SimpleScrollView class="nana-catalog" :scrollY="true">
-    <div>
-      <div 
-        v-for="(item, index) in props.items"
-        :key="index"
-        :class="[
-          'nana-catalog-item',
-          `level-${item.level}`,
-          activeIndex === index ? 'active' : '',
-        ]"
-        @click="handlerItemClick(item)"
-      >
-        {{ item.title }}
-      </div>
-    </div>
-  </SimpleScrollView>
-</template>
-
-<style lang="scss">
-.nana-catalog {
-  position: relative; 
-  margin-left: 0.5rem;
-
-  > div {
-    display: flex;
-    flex-direction: column;
-  }
-
-  &::before {
-    content: '';
-    position: absolute;
-    top: 0;
-    bottom: 0;
-    left: 0;
-    width: 1px;
-    background-color: var(--nana-text-6);
-  }
-
-  .nana-catalog-item {
-    position: relative;
-    padding: 0.4rem 0.8rem;
-    font-size: 1rem;
-    color: var(--nana-text-6);
-    user-select: none;
-    cursor: pointer;
-
-    &.active {
-      font-weight: bold; 
-      color: var(--nana-text-1);
-
-      &::after {
-        content: '';
-        position: absolute;
-        top: calc(50% - 6px);
-        left: 0;
-        border: 8px solid transparent;
-        border-left: 8px solid var(--nana-text-1);
-      }
-    }
-    &.level-1 {
-      font-size: 1.2rem;
-      padding-left: 1rem;
-    }
-    &.level-3,
-    &.level-4,
-    &.level-5 {
-      font-size: 0.8rem;
-      padding-left: 1.2rem;
-      
-      &::before {
-        content: '·';
-        display: inline-block;
-        padding-right: 0.6rem;
-      }
-    }
-    &.level-6 {
-      font-size: 0.7rem;
-      padding-left: 1.6rem;
-    }
-  }
-}
-</style>

+ 2 - 3
src/components/content/CommonListBlock.vue

@@ -139,15 +139,14 @@
 
 <script setup lang="ts">
 import { computed, onMounted, ref, watch, type PropType } from 'vue';
-import { useSimplePagerDataLoader } from '@/composeable/SimplePagerDataLoader';
+import { useRoute, useRouter } from 'vue-router';
+import { useSimplePagerDataLoader, SimplePageContentLoader } from '@imengyu/imengyu-web-shared';
 import TagBar from '../content/TagBar.vue';
 import Dropdown from '../controls/Dropdown.vue';
 import SimpleInput from '../controls/SimpleInput.vue';
-import SimplePageContentLoader from '@/components/content/SimplePageContentLoader.vue';
 import Pagination from '../controls/Pagination.vue';
 import TitleDescBlock from '../parts/TitleDescBlock.vue';
 import IconSearch from '../icons/IconSearch.vue';
-import { useRoute, useRouter } from 'vue-router';
 
 export interface DropdownCommonItem {
   id: number; 

+ 0 - 63
src/components/content/ImageGrid.vue

@@ -1,63 +0,0 @@
-<template>
-  <div class="w-100 d-flex flex-row flex-wrap" :style="{ gap: gap }">
-    <slot 
-      name="item"
-      v-for="(v, k) in data"
-      :key="k"
-      :item="v"
-      :index="k"
-      :width="`calc(${100 / rowCount}% - ${gap})`"
-      :height="imageHeight"
-      :url="imagekey ? v[imagekey] : v" 
-    >
-      <a-image
-        :src="imagekey ? v[imagekey] : v" 
-        :style="{ 
-          width: `calc(${100 / rowCount}% - ${gap})`,
-          height: imageHeight,
-          borderRadius: '5px',
-          objectFit: 'cover',
-        }"
-        @click="()=>emit('itemClick', v)"
-      />
-    </slot>
-    <slot name="empty" v-if="!data || data.length === 0">
-      <div class="w-100 text-center">
-        <ExclamationCircleOutlined />
-        <div>暂无数据</div>
-      </div>
-    </slot>
-  </div>
-</template>
-
-<script setup lang="ts">
-import type { PropType } from 'vue';
-import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
-
-defineProps({	
-  rowCount : {
-    type: Number,
-    default: 3,
-  },
-  imagekey : {
-    type: String,
-    default: undefined,
-  },
-  imageHeight : {
-    type: String,
-    default: undefined,
-  },
-  gap: {
-    type: String,
-    default: '10px',
-  },
-  data: {
-    type: Object as PropType<any[]>,
-    default: null,
-  },
-})
-
-const emit = defineEmits([	
-  "itemClick"	
-])
-</script>

+ 0 - 34
src/components/content/ImageSwiper.vue

@@ -1,34 +0,0 @@
-<template>
-  <Carousel 
-    v-bind="carouselConfig"
-    @slide-end="(i) => $emit('switch', i)"
-  >
-    <Slide v-for="(slide, index) in items" :key="index">
-      <slot name="item" :index="index" :item="slide" />
-    </Slide>
-    <template #addons>
-      <Navigation />
-      <Pagination />
-    </template>
-  </Carousel>
-</template>
-
-<script setup lang="ts">
-import type { PropType } from 'vue';
-import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel'
-
-defineEmits([	
-  "switch"	
-])
-
-const props = defineProps({	
-  items : {
-    type: Object as PropType<Array<any>>,
-    default: () => ([]),
-  },
-})
-const carouselConfig =  {
-  wrapAround: true,
-  ...props,
-}
-</script>

src/components/dynamicf/Map/MapConfig.ts → src/components/content/MapConfig.ts


+ 0 - 80
src/components/content/SimplePageContentLoader.vue

@@ -1,80 +0,0 @@
-<template>
-  <div
-    v-if="loader?.loadStatus.value == 'loading'"
-    style="min-height: 200rpx;display: flex;justify-content: center;align-items: center;"
-  >
-    <a-spin tip="加载中" />
-  </div>
-  <div
-    v-else-if="loader?.loadStatus.value == 'error'"
-    style="min-height: 200rpx"
-  >
-    <a-empty :description="loader.loadError.value" >
-      <a-button  @click="handleRetry">重试</a-button>
-    </a-empty>
-  </div>
-  <template v-else-if="loader?.loadStatus.value == 'finished' || loader?.loadStatus.value == 'nomore'">
-    <slot />
-  </template>
-  <div
-    v-if="showEmpty || loader?.loadStatus.value == 'nomore'"
-    style="min-height: 200rpx"
-    class="empty"
-  >
-    <a-empty :description="emptyView?.text ?? '暂无数据'">
-      <a-button
-        v-if="emptyView?.button"
-        @click="emptyView?.buttonClick ?? handleRetry"
-      >
-        {{emptyView?.buttonText ?? '刷新'}}
-      </a-button>
-    </a-empty>
-  </div>
-</template>
-
-<script setup lang="ts">
-import type { ILoaderCommon } from '@/composeable/LoaderCommon';
-import { onMounted, ref, type PropType } from 'vue';
-
-const props = defineProps({	
-  loader: {
-    type: Object as PropType<ILoaderCommon<any>>,
-    default: null,
-  },
-  autoLoad: {
-    type: Boolean,
-    default: false, 
-  },
-  showEmpty: {
-    type: Boolean,
-    default: false, 
-  },
-  emptyView: {
-    type: Object as PropType<{
-      text: string,
-      buttonText: string,
-      button: boolean,
-      buttonClick: () => void,
-    }>,
-    default: null,
-  },
-})
-
-const loaded = ref(false);
-
-onMounted(() => {
-  loaded.value = false;
-  if (props.autoLoad)
-    handleLoad(); 
-});
-
-function handleRetry() {
-  props.loader.loadData(undefined);
-}
-function handleLoad() {
-  if (loaded.value) 
-    return;
-  loaded.value = true;
-  props.loader.loadData(undefined);
-}
-</script>

+ 1 - 1
src/components/content/SimplePointedMap.vue

@@ -14,7 +14,7 @@
 
 <script setup lang="ts">
 import { onMounted, ref, watch } from 'vue';
-import { defaultCenter } from '../dynamicf/Map/MapConfig';
+import { defaultCenter } from './MapConfig';
 
 const props = defineProps({
   latitude: {

+ 3 - 3
src/components/controls/Dropdown.vue

@@ -6,7 +6,7 @@
       <DropDownIcon :class="['arrow',isDropdownOpen?'open':'']" />
     </div>
     <div v-if="isDropdownOpen" class="nana-dropdown-options">
-      <SimpleScrollView :scroll-y="true">
+      <ScrollRect scroll="vertical">
         <div
           v-for="(option, index) in options"
           :key="index"
@@ -18,7 +18,7 @@
         >
           <span>{{ option[labelKey] }}</span>
         </div>
-      </SimpleScrollView>
+      </ScrollRect>
     </div>
   </div>
   
@@ -27,7 +27,7 @@
 <script setup lang="ts">
 import { computed, ref, type PropType } from 'vue';
 import DropDownIcon from './DropdownIcon.vue';
-import SimpleScrollView from '../display/SimpleScrollView.vue';
+import { ScrollRect } from '@imengyu/vue-scroll-rect';
 
 const props = defineProps({
   options: {

+ 0 - 90
src/components/display/SimplePopup.vue

@@ -1,90 +0,0 @@
-<template>
-  <teleport to="body">
-    <!-- 遮罩层 -->
-    <div
-      v-if="isClose2 || isVisible" 
-      :class="[
-        'popup-overlay',
-        isVisible ? 'open' : '',
-        isClose2 ? 'close' : '',
-      ]" 
-      @click="close"
-    >
-      <!-- 弹出框内容 -->
-      <div class="popup-content" @click.stop>
-        <slot />
-      </div>
-    </div>
-  </teleport>
-</template>
-
-<script lang="ts" setup>
-import { ref } from 'vue';
-
-const emit = defineEmits([ 'change' ]);
-
-const isVisible = ref(false);
-const isClose2 = ref(false);
-
-const open = () => {
-  isClose2.value = true;
-  setTimeout(() => {
-    isClose2.value = false;
-    isVisible.value = true;
-    emit('change', true);
-  }, 100);
-};
-const close = () => {
-  isClose2.value = true;
-  isVisible.value = false;
-  setTimeout(() => {
-    isClose2.value = false;
-    emit('change', false);
-  }, 300);
-};
-
-defineExpose({
-  open,
-  close
-});
-</script>
-
-<style lang="scss">
-@use "@/assets/scss/colors";
-
-.popup-overlay {
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  background-color: rgba(0, 0, 0, 0.5);
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  z-index: 300;
-  opacity: 0;
-  transition: all 0.3s ease-in-out;
-
-  &.open {
-    opacity: 1;
-
-    .popup-content {
-      opacity: 1;
-      transform: scale(1);
-    }
-  }
-  &.close {
-    pointer-events: none;
-
-    .popup-content {
-      opacity: 0;
-    }
-  }
-}
-.popup-content {
-  position: relative;
-  transition: all 0.3s ease-in-out;
-  transform: scale(0.9);
-}
-</style>

+ 0 - 24
src/components/display/SimpleRemoveRichHtml.vue

@@ -1,24 +0,0 @@
-<template>
-  <span>{{ removeHtmlTags(content) }}</span>
-</template>
-
-<script setup lang="ts">
-const props = defineProps({	
-  content: {
-    type: String,
-    default: '',
-  },
-})
-
-function removeHtmlTags(str: string) {
-  str = str.replace(/<[^>]*>/g, '');
-  str = str.replace(/&nbsp;/gi, ' '); // 替换为普通空格
-  str = str.replace(/&amp;/gi, '&');  // 替换为 &
-  str = str.replace(/&lt;/gi, '<');  // 替换为 <
-  str = str.replace(/&gt;/gi, '>');  // 替换为 >
-  str = str.replace(/&quot;/gi, '"'); // 替换为 "
-  str = str.replace(/&#39;/gi, "'");  // 替换为 '
-  return str;
-}
-
-</script>

+ 0 - 146
src/components/display/SimpleRichHtml.vue

@@ -1,146 +0,0 @@
-<template>
-  <div v-show="show" ref="scrollContainer" class="nana-rich-html-container">
-    <div class="rich-html">
-      <slot name="prepend" />
-      <template 
-        v-for="(content, i) in contents"
-        :key="i"
-      >
-        <div 
-          v-if="content && content != 'null'"
-          :data-r-id="id"
-          class="content"
-          v-html="content"
-        />
-      </template>
-      <div v-if="!contents || contents.length === 0 || (contents.length === 1 && !contents[0])" class="w-100 text-center">
-        <ExclamationCircleOutlined />
-        <div>暂无数据</div>
-      </div>
-      <slot name="append" />
-    </div>
-    <div class="rich-html-catalog" v-if="catalog && catalogItems.length > 0">
-      <CommonCatalog
-        :items="catalogItems"
-        :scrollContainer="scrollContainer"
-      />
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { RandomUtils } from '@imengyu/imengyu-utils';
-import CommonCatalog, { type CatalogItem } from '../content/CommonCatalog.vue';
-import { onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue';
-import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
-
-const props = defineProps({	
-  contents: {
-    type: Array as PropType<string[]>,
-    default: () => ([]),
-  },
-  tagStyle: {
-    type: Object as PropType<Record<string, string>>,
-    default: () => ({}),
-  },
-  catalog: {
-    type: Boolean,
-    default: true,
-  },
-  noStyle: {
-    type: Boolean,
-    default: false,
-  },
-  show: {
-    type: Boolean,
-    default: true,
-  },
-})
-
-const id = RandomUtils.genNonDuplicateIDHEX(12);
-const catalogItems = ref<CatalogItem[]>([]);
-const scrollContainer = ref<HTMLElement|null>(null);
-let lastStyleTag : HTMLElement|null = null;
-
-function genTagCss() {
-  if (Object.keys(props.tagStyle).length > 0) {
-    const style = document.createElement('style');
-    let css = '';
-    for (const key in props.tagStyle) {
-      css += `.rich-html div[data-r-id="${id}"] ${key} { ${props.tagStyle[key]} } `
-    }
-    style.innerHTML = css;
-    document.body.appendChild(style);
-    lastStyleTag = style;
-  }
-}
-function generateCatalog() {
-  catalogItems.value = [];
-
-  if (!props.catalog) 
-    return;
-
-  let anchrId = 0;
-  for (let i = 1; i <= 6; i++) {
-    const heades = document.querySelectorAll(`.rich-html div[data-r-id="${id}"] h${i}`);
-    for (const header of heades) {
-      anchrId++;
-      if (header instanceof HTMLHeadingElement) {
-        if (header.id == '')
-          header.id = 'header' + anchrId + 'a' + RandomUtils.genNonDuplicateIDHEX(12);
-        catalogItems.value.push({
-          title: header.textContent || '',
-          scrollPos: header.offsetTop,
-          level: i,
-          anchor: header.id,
-        });
-      }
-    }
-  }
-  catalogItems.value.sort((a, b) => {
-    return a.scrollPos - b.scrollPos;
-  })
-}
-
-watch(() => props.contents, () => {
-  setTimeout(() => {
-    generateCatalog();
-  }, 200);
-}, { immediate: true })
-
-onBeforeUnmount(() => {
-  if (lastStyleTag) {
-    lastStyleTag.parentElement?.removeChild(lastStyleTag);
-    lastStyleTag = null;
-  }
-})
-onMounted(() => {
-  genTagCss()
-});
-</script>
-
-<style lang="scss">
-.nana-rich-html-container {
-  position: relative;
-  display: flex;
-  flex-direction: row;
-
-  .rich-html {
-    flex: 1 1 100%;
-  }
-  .rich-html-catalog {
-    position: fixed;
-    top: 140px;
-    right: 0;
-    bottom: 0px;
-    width: 15rem;
-  }
-}
-
-
-@media (max-width: 1648px) {
-  .rich-html-catalog {
-    display: none;
-  }
-}
-</style>

+ 0 - 57
src/components/display/SimpleScrollView.vue

@@ -1,57 +0,0 @@
-<template>
-  <div 
-    :class="[
-      'nana-scroll-view',
-      scrollX ? 'x' : '',
-      scrollY ? 'y' : ''
-    ]"
-  >
-    <slot />
-  </div>
-</template>
-
-<script lang="ts" setup>
-/**
- * 组件说明:可滚动的容器。
- */
-const props = defineProps({	
-  scrollX: {
-    type: Boolean,
-    default: false
-  },
-  scrollY: {
-    type: Boolean,
-    default: false 
-  }
-})
-</script>
-
-<style lang="scss" scoped>
-.nana-scroll-view {
-  overflow: hidden;
-  
-  &::-webkit-scrollbar {
-    width: 5px;
-    height: 5px;
-  }
-  &::-webkit-scrollbar-thumb {
-    background: #d6d6d6;
-    opacity: .7;
-    border-radius: 3px;
-
-    &:hover {
-      background: #707070;
-    }
-  }
-  &::-webkit-scrollbar-track {
-    background: transparent;
-  }
-
-  &.x {
-    overflow-x: scroll; 
-  }
-  &.y {
-    overflow-y: scroll; 
-  }
-}
-</style>

+ 0 - 29
src/components/dynamicf/ActionRender.ts

@@ -1,29 +0,0 @@
-export interface ActionRenderProps {
-  /**
-   * 操作条目
-   */
-  actions: Array<ActionRenderItem>;
-}
-
-export interface ActionRenderItem {
-  /**
-   * 按钮文字
-   */
-  text: string,
-  /**
-   * 按钮键值
-   */
-  key?: string,
-  /**
-   * 这个按钮是否换行,默认否
-   */
-  wrap?: boolean,
-  /**
-   * 按钮类型
-   */
-  type?: 'primary'|'danger'|'success'|'warning'|'secondary',
-  /**
-   * 按钮点击回调
-   */
-  onClick?: (key: string|undefined, record: Record<string, unknown>) => void;
-}

+ 0 - 35
src/components/dynamicf/ActionRender.vue

@@ -1,35 +0,0 @@
-<template>
-  <span>
-    <a-button
-      v-for="(act, k) in actions" 
-      :key="k" 
-      :class="`mr-3 text-${act.type}` + (act.wrap ? ' display-block' : '')"
-      @click="actionClick(act)"
-    >{{act.text}}</a-button>
-  </span>
-</template>
-
-<script lang="ts">
-import type { DataModel } from '@imengyu/js-request-transform';
-import type { ActionRenderItem } from './ActionRender';
-import { defineComponent, type PropType } from "vue";
-
-export default defineComponent({
-  props: {
-    rawModel: {
-      type: Object as PropType<Record<string, unknown>>,
-    },
-    actions: {
-      type: Object as PropType<Array<ActionRenderItem>>,
-    },
-  },
-  methods: {
-    actionClick(action: ActionRenderItem) {
-      if (typeof action.onClick === 'function')
-        action.onClick(action.key, this.rawModel as DataModel);
-      else
-        console.warn('action ' + action.key + ' onClick is not a function!');
-    },
-  },
-});
-</script>

+ 0 - 50
src/components/dynamicf/CascaderFormItem.ts

@@ -1,50 +0,0 @@
-import type { CascaderProps } from "ant-design-vue";
-
-
-export type CascaderFormItemOptionType = CascaderProps['options'];
-
-export type LoadDataFun = (parentValue: string|number|null, level: number, parentObject: unknown) => Promise<CascaderFormItemOptionType>;
-export type OnChooseFun = (values: (string|number|null)[], objects: unknown[]) => void;
-
-/**
- * CascaderFormItem 的公共接口
- */
-export interface CascaderFormItemInterface {
-  /**
-   * 加载树形数据至当前选中层级
-   */
-  doLoadDataToCurrentValue: () => void;
-}
-/**
- * CascaderFormItem 的公共接口
- */
-export interface CascaderFormItemProps {
-  /**
-   * 初始化时加载数据
-   */
-  loadAtStart:  boolean,
-  /**
-   * 初始化时是否递归加载数据到当前选中的数据
-   */
-  loadCascaderToCurrentValueAtStart:  boolean,
-  /**
-   * 加载数据
-   */
-  loadData: LoadDataFun;
-  /**
-   * placeholder
-   */
-  placeholder?: string;
-  /**
-   * 选择后回调查找出的对象键,默认是id
-   */
-  onSelectFindIdKey?: string;
-  /**
-   * 选择后回调
-   */
-  onSelect?: OnChooseFun;
-  /**
-   * a-cascader 其他自定义参数
-   */
-  customProps?: CascaderProps;
-}

+ 0 - 165
src/components/dynamicf/CascaderFormItem.vue

@@ -1,165 +0,0 @@
-<template>
-  <a-cascader
-    :value="value"
-    :options="options"
-    :load-data="doLoadData"
-    :placeholder="placeholder"
-    @update:value="onUpdateValue"
-    change-on-select
-    v-bind="(customProps as any)"
-  />
-</template>
-
-<script lang="ts">
-import { defineComponent, onMounted, type PropType, ref, toRefs, watch } from "vue";
-import type { CascaderProps } from "ant-design-vue";
-import type { LoadDataFun, OnChooseFun } from "./CascaderFormItem";
-
-export default defineComponent({
-  props: {
-    loadData: {
-      type: Function as PropType<LoadDataFun>,
-      default: null,
-    },
-    placeholder: {
-      type: String,
-      default: '请选择地址',
-    },
-    loadAtStart:  {
-      type: Boolean,
-      default: true,
-    },
-    loadCascaderToCurrentValueAtStart: {
-      type: Boolean,
-      default: true,
-    },
-    value: {},
-    /**
-     * 选择后回调
-     */
-    onSelect: {
-      type: Function as PropType<OnChooseFun>,
-      default: null,
-    },
-    onSelectFindIdKey: {
-      type: String,
-      default: 'id',
-    },
-    /**
-     * a-cascader 其他自定义参数
-     */
-    customProps: {
-      //type: Object as PropType<CascaderProps>,
-      default: null,
-    },
-  },
-  emits: [
-    'update:value'
-  ],
-  setup(props, context) {
-
-    const { loadAtStart, loadCascaderToCurrentValueAtStart, value, loadData, onSelect } = toRefs(props);
-    const options = ref<CascaderProps['options']>([]);
-
-    const doLoadData = ((selectedOptions) => {
-      const parent = selectedOptions && selectedOptions.length > 0 ? selectedOptions[selectedOptions.length - 1] : null;
-      loadData.value(parent?.value as number, selectedOptions ? selectedOptions.length : 0, parent)
-        .then((d) => {
-          if (!d)
-            throw new Error("loadData return invalid data!");
-          if (parent) {
-            //添加数据至指定层级下方
-            if (!parent.children)
-              parent.children = d;
-            else
-              parent.children = parent.children.concat(d);
-          } else {
-            //添加数据
-            options.value = d;
-            //这个时候加载一下默认选择项目
-            if (loadCascaderToCurrentValueAtStart.value)
-              doLoadDataToCurrentValue();
-          }
-        }).catch((e) => {
-          console.error(e);
-        });
-    }) as CascaderProps['loadData'];
-
-    function onUpdateValue(v: number[]) {
-      context.emit('update:value', v);
-
-      //选中后回调
-      if (typeof onSelect.value === 'function') {
-        const objArr = [] as unknown[];
-        const valueArrNow = v.concat();
-        //通过ID查找指定的对象
-        let optionsCurrent = options.value as CascaderProps['options'];
-        for (let i = 0; i < v.length; i++) {
-          if (!optionsCurrent)
-            break;
-          const item = optionsCurrent.find(k => k.value === valueArrNow[i]);
-          if (item) {
-            objArr.push(item);
-            optionsCurrent = item.children;//下一级
-          } else {
-            break;
-          }
-        }
-        //回调
-        onSelect.value(v, objArr);
-      }
-    }
-
-    //加载树形数据至当前选中层级
-    function doLoadDataToCurrentValue() {
-      const valueArrNow = (value.value as number[]).concat();
-
-      function findChildren(index: number, optionsCurrent: CascaderProps['options']) {
-        if (!optionsCurrent)
-          return;
-        //当前级数据,查找是否存在,
-        const option = optionsCurrent.find(k => k.value === valueArrNow[index]);
-        if (option && (!option.children || option.children.length === 0)) {
-          //存在,尝试加载下一级数据
-          loadData.value(valueArrNow[index], index, option)
-            .then((d) => {
-              if (!d)
-                throw new Error("loadData return invalid data!");
-              if (parent) {
-                if (!option.children)
-                  option.children = d;
-                else
-                  option.children = option.children.concat(d);
-                
-                //如果还没有达到输入选择的层级,则进行下一次加载
-                if (index < valueArrNow.length - 1) {
-                  findChildren(index + 1, option.children);
-                }
-              }
-            }).catch((e) => {
-              console.error(e);
-            });
-        }
-      }
-
-      findChildren(0, options.value);
-    }
-
-    onMounted(() => {
-      if (loadAtStart.value && typeof doLoadData === 'function')
-        doLoadData([]);
-    });
-
-    watch(value, () => {
-      doLoadDataToCurrentValue();
-    });
-
-    return {
-      doLoadData,
-      doLoadDataToCurrentValue,
-      onUpdateValue,
-      options,
-    };
-  },
-});
-</script>

+ 0 - 56
src/components/dynamicf/CheckBoxToInt.vue

@@ -1,56 +0,0 @@
-<template>
-  <a-checkbox 
-    :checked="checked"
-    @update:checked="(v: boolean) => $emit('update:value', v)"
-    :disabled="disabled"
-  >
-    <slot>{{text}}</slot>
-  </a-checkbox>
-</template>
-
-<script lang="ts">
-import { defineComponent } from "vue";
-
-export default defineComponent({
-  name: "CheckBoxToInt",
-  data() {
-    return {
-      checked: false
-    }
-  },
-  emits: [ 'update:value' ],
-  props: {
-    checkedValue: {
-      type: Number,
-      default: 1
-    },
-    uncheckedValue: {
-      type: Number,
-      default: 0
-    },
-    disabled: {
-      type: Boolean,
-      default: false
-    },
-    text: {
-      type: String,
-      default: '启用',
-    },
-    value: {
-      default: null,
-    }
-  },
-  mounted: function() {
-    this.loadChecked();
-  },
-  watch: {
-    value() { this.loadChecked(); },
-    checked() { this.$emit('update:value', this.checked ? this.checkedValue : this.uncheckedValue) }
-  },
-  methods: {
-    loadChecked() {
-      this.checked = this.value == this.checkedValue;
-    }
-  }
-});
-</script>

+ 0 - 8
src/components/dynamicf/CheckBoxValue.ts

@@ -1,8 +0,0 @@
-import type { CheckboxProps } from "ant-design-vue";
-
-export interface CheckBoxValueProps {
-  checkboxProps?: CheckboxProps,
-  text: string,
-  checkedValue?: unknown,
-  uncheckedValue?: unknown,
-}

+ 0 - 51
src/components/dynamicf/CheckBoxValue.vue

@@ -1,51 +0,0 @@
-<template>
-  <a-checkbox 
-    v-model:checked="checked" 
-    v-bind="checkboxProps"
-  >{{text}}</a-checkbox>
-</template>
-
-<script lang="ts">
-import { defineComponent, type PropType } from "vue";
-import type { CheckboxProps } from "ant-design-vue";
-
-export default defineComponent({
-  props: {
-    checkboxProps: {
-      type: Object as PropType<CheckboxProps>,
-      default: null,
-    },
-    text: {
-      type: String,
-      default: '',
-    },
-    checkedValue: {
-      default: true,
-    },
-    uncheckedValue: {
-      default: false,
-    },
-    value: {
-    },
-  },
-  emits: [ 'update:value' ],
-  watch: {
-    checked(v) {
-      this.$emit('update:value', v ? this.checkedValue : this.uncheckedValue);
-    },
-    value(v) {
-      const checked = v === this.checkedValue;
-      if (this.checked != checked)
-        this.checked = checked;
-    },
-  },
-  data() {
-    return {
-      checked: false,
-    }
-  },
-  mounted() {
-    this.checked = this.value === this.checkedValue;
-  },
-});
-</script>

+ 0 - 30
src/components/dynamicf/Display/ShowDateOrNull.vue

@@ -1,30 +0,0 @@
-<template>
-  <span :class="'vc-show-date '+size">
-    <span v-if="value !== undefined && value !== null">
-      {{ typeof value.format === 'function' ? value.format() : '不是日期类型' }}
-    </span>
-    <span v-else class="text-secondary"><i>{{ nullText }}</i></span>
-  </span>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-
-export default defineComponent({
-  name: "ShowDateOrNull",
-  props: {
-    nullText: {
-      default: '暂无',
-      type: String
-    },
-    size: {
-      default: '',
-      type: String
-    },
-    value: {
-      type: Object as import('vue').PropType<Date>,
-      default: null,
-    }
-  },
-});
-</script>

+ 0 - 86
src/components/dynamicf/Display/ShowImageList.vue

@@ -1,86 +0,0 @@
-<template>
-  <span v-if="!images||images.length==0">无图片</span>
-  <div v-else-if="images" class="image-list">
-    <a-image 
-      v-for="(image, k) in (showAll ? images : images.filter((_: unknown, i: number) => i < maxCount))"
-      :key="k"
-      :width="imgSize"
-      :height="imgSize"
-      :src="image"
-      :fallback="failImage"
-    />
-    <div v-if="images.length > maxCount" class="overflow-count" :style="{ 
-      width: `${imgSize}px`, 
-      height: `${imgSize}px`,
-      lineHeight: `${imgSize}px`,
-    }" @click="showAll=!showAll">
-      {{showAll ? '折叠' : `+${images.length - maxCount}` }}
-    </div>
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent, type PropType } from "vue";
-
-export default defineComponent({
-  name: "ShowImageList",
-  props: {
-    images: {
-      type: Object as PropType<Array<string>>,
-      default: null,
-    },
-    size: {
-      type: [Number,String],
-      default: 30,
-    },
-    maxCount: {
-      type: Number,
-      default: 5,
-    },
-    failImage: {
-      default: () => require('@/assets/images/failed.svg'),
-      type: String
-    },
-  },
-  computed: {
-    imgSize() : number {
-      if (typeof this.size === 'string')
-        switch(this.size) {
-          case 'default': return 45;
-          case 'middle': return 30;
-          case 'small': return 20;
-        }
-      return this.size as number;
-    },  
-  },
-  data() {
-    return {
-      showAll: false,
-    };
-  },
-});
-</script>
-
-<style lang="scss">
-.image-list {
-  position: relative;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  flex-wrap: wrap;
-
-  .overflow-count {
-    color: #fff;
-    background-color: rgba(0,0,0,0.5);
-    text-align: center;
-    cursor: pointer;
-  }
-  .ant-image {
-    background-color: #ececec;
-    overflow: hidden;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-}
-</style>

+ 0 - 76
src/components/dynamicf/Display/ShowImageOrNull.vue

@@ -1,76 +0,0 @@
-<template>
-  <div :style="{
-    display: 'inline-block',
-    overflow: 'hidden',
-    width: `${imgSize}px`,
-    height: `${imgSize}px`,
-  }">
-    <a-image
-      :src="imgUrl"
-      :fallback="failImage"
-      :width="imgSize"
-      :height="imgSize"
-    />
-  </div>
-</template>
-
-<script lang="ts">
-import { StringUtils } from '@imengyu/imengyu-utils';
-import { defineComponent } from 'vue';
-
-export default defineComponent({
-  name: "ShowImageOrNull",
-  props: {
-    nullImage: {
-      type: String,
-      default: () => require('@/assets/images/none.png'),
-    },
-    failImage: {
-      type: String,
-      default: () => require('@/assets/images/failed.svg'),
-    },
-    size: {
-      type: [Number,String],
-      default: 30,
-    },
-    src: {
-      type: String,
-      default: null,
-    }
-  },
-  data() {
-    return {
-      imgUrl: '',
-    }
-  },
-  computed: {
-    imgSize() : number {
-      if (typeof this.size === 'string')
-        switch(this.size) {
-          case 'default': return 55;
-          case 'middle': return 45;
-          case 'small': return 25;
-        }
-      return this.size as number;
-    },  
-  },
-  mounted() {
-    setTimeout(() => { this.loadImage(); },100);
-  },
-  watch: {
-    src() { this.loadImage(); }
-  },
-  methods: {
-    loadImage() {
-      if(StringUtils.isNullOrEmpty(this.src))
-        this.imgUrl = this.nullImage as string;
-      else
-        this.imgUrl = this.src as string;
-    },
-    onError() {
-      if(this.imgUrl != this.failImage)
-        this.imgUrl = this.failImage as string;
-    }
-  }
-});
-</script>

+ 0 - 69
src/components/dynamicf/Display/ShowInList.vue

@@ -1,69 +0,0 @@
-<template>
-  <div>{{ result }}</div>
-</template>
-
-<script lang="ts">
-import type { KeyValue } from "@imengyu/js-request-transform";
-import { defineComponent, type PropType } from "vue";
-
-export default defineComponent({
-  name: "ShowInList",
-  data() {
-    return {
-      result: ''
-    }
-  },
-  props: {
-    noMatchText: {
-      type: String,
-      default: '暂无',
-    },
-    useProp: {
-      type: Boolean,
-      default: true,
-    },
-    usePropName: {
-      type: String,
-      default: 'id',
-    },
-    usePropValue: {
-      type: String,
-      default: 'name',
-    },
-    list: {
-      type: Object as PropType<Array<KeyValue>>,
-      default: null,
-    },
-    value: {
-      default: null,
-    }
-  },
-  mounted: function() {
-    this.loadText();
-  },
-  watch: {
-    list() { this.loadText(); },
-    value() { this.loadText(); }
-  },
-  methods: {
-    loadText() {
-      const list = this.list as Array<KeyValue>;
-      if(list && this.value && list.length > 0){
-        for(let i = 0, c = list.length; i < c; i++){
-          if(this.useProp)
-            if(list[i][this.usePropName as string] == this.value) {
-              this.result = list[i][this.usePropValue as string] as string;
-              return;
-            }
-          else
-            if(list[i] == this.value) {
-              this.result = list[i] as unknown as string;
-              return;
-            }
-        }
-        this.result = this.noMatchText as string;
-      }else this.result = this.noMatchText as string;
-    }
-  }
-});
-</script>

+ 0 - 35
src/components/dynamicf/Display/ShowMomentOrNull.vue

@@ -1,35 +0,0 @@
-<template>
-  <span :class="'vc-show-date '+size">
-    <span v-if="value && value!=null">
-      {{ typeof value.format === 'function' ? value.format(dateFormat) : '不是日期类型' }}
-    </span>
-    <span v-else class="text-secondary"><i>{{ nullText }}</i></span>
-  </span>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import dayjs from 'dayjs';
-
-export default defineComponent({
-  name: "ShowDateOrNull",
-  props: {
-    nullText: {
-      default: '暂无',
-      type: String
-    },
-    dateFormat: {
-      default: 'YYYY-MM-DD HH:mm:ss',
-      type: String
-    },
-    size: {
-      default: '',
-      type: String
-    },
-    value: {
-      type: Object as import('vue').PropType<dayjs.Dayjs>,
-      default: null,
-    }
-  },
-});
-</script>

+ 0 - 48
src/components/dynamicf/Display/ShowTagList.vue

@@ -1,48 +0,0 @@
-<template>
-  <span v-if="!tags||tags.length==0">暂无</span>
-  <span v-else-if="small">
-    {{ tags[0] }}等{{tags.length}}个
-  </span>
-  <div v-else-if="tagsC" class="d-flex flex-row flex-wrap">
-    <a-tag v-for="(n, k) in tagsC" :key="k">{{n}}</a-tag>
-    <small class="text-primary" v-if="!expand && tags.length > maxCount" @click="expand=true">等{{tags.length}}个</small>
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent, PropType } from "vue";
-
-export default defineComponent({
-  name: "ShowImageList",
-  data() {
-    return {
-      expand: false,
-    };
-  },
-  computed: {
-    tagsC() {
-      if (!this.expand && this.maxCount < this.tags.length)
-        return this.tags.slice(0, this.maxCount)
-      else
-        return this.tags;
-    },
-    small() {
-      return this.size === 'small';
-    },
-  },
-  props: {
-    size: {
-      type: String,
-      default: '',
-    },
-    maxCount: {
-      type: Number,
-      default: 10,
-    },
-    tags: {
-      type: Object as PropType<Array<string>>,
-      default: null,
-    },
-  },
-});
-</script>

+ 0 - 73
src/components/dynamicf/Display/ShowValueOrNull.vue

@@ -1,73 +0,0 @@
-<template>
-  <span
-    :class="'vc-show-value'+(block? ' d-block' : '')+(clickable?' link':'')"
-    @click="onClick"
-  >
-    {{ prefix }}
-    <template v-if="(value && value!='') || value === 0">
-      <span v-if="typeof value === 'number'">{{ numericalPrecision > 0 ? (value as number).toFixed(numericalPrecision) : value }}</span>
-      <span v-else-if="typeof value === 'boolean'">{{ value ? '是' : '否' }}</span>
-      <span v-else-if="typeof value === 'object'">{{ JSON.stringify(value) }}</span>
-      <span v-else>{{ value }}</span>
-    </template>
-    <span v-else class="text-secondary"><i>{{ nullText }}</i></span>
-  </span>
-</template>
-
-<script lang="ts">
-import { defineComponent } from "vue";
-
-export default defineComponent({
-  name: "ShowValueOrNull",
-  data() {
-    return {
-      result: ''
-    }
-  },
-  emits: [ 'click' ],
-  props: {
-    block: {
-      default: false,
-      type: Boolean
-    },
-    clickable: {
-      default: false,
-      type: Boolean
-    },
-    prefix: {
-      default: '',
-      type: String
-    },
-    numericalPrecision: {
-      default: 0,
-      type: Number
-    },
-    nullText: {
-      default: '暂无',
-      type: String
-    },
-    record: {
-      default: null,
-    },
-    value: {
-      type: null,
-      default: null,
-    },
-  },
-  methods: {
-    onClick() {
-      if (this.clickable)
-        this.$emit('click', this.record);
-    },
-  },
-});
-</script>
-
-<style lang="scss">
-.vc-show-value {
-  &.link {
-    color: #008cff;
-    cursor: pointer;
-  }
-}
-</style>

+ 0 - 37
src/components/dynamicf/Display/StateRenderer.vue

@@ -1,37 +0,0 @@
-<template>
-  <span>
-    <a-badge 
-      v-if="currentState"
-      :status="currentState.badgeState" 
-      :color="currentState.badgeColor" 
-      :text="currentState.text"
-    />
-    <span v-else>未知状态:{{value}}</span>
-  </span>
-</template>
-
-<script lang="ts">
-import type { IDynamicFormItemSelectOption } from "@imengyu/vue-dynamic-form";
-import { defineComponent, type PropType } from "vue";
-
-export interface IStateOption extends IDynamicFormItemSelectOption {
-  badgeState?: 'success' | 'processing' | 'error' | 'default' | 'warning';
-  badgeColor?: string;
-}
-
-export default defineComponent({
-  props: {
-    value: {
-    },
-    stateValues: {
-      type: Object as PropType<Array<IStateOption>>,
-    },
-  },
-  computed: {
-    currentState() {
-      return (this.stateValues as IStateOption[])
-        .find(k => k.value === this.value || k.text === this.value);
-    },
-  },
-});
-</script>

+ 0 - 88
src/components/dynamicf/Dropdown/IdAsValueDropdown.ts

@@ -1,88 +0,0 @@
-import type { DataModel } from "@imengyu/js-request-transform";
-import type { SelectProps } from "ant-design-vue";
-import type { VNode } from "vue";
-
-/**
- * 通用下拉框返回结构定义
- */
-export interface DropdownValues<T> {
-  label: string,
-  value: number,
-  raw: T;
-}
-
-export type LoadDataFun<T extends DataModel> = (val: string | null) => Promise<DropdownValues<T>[]>;
-
-/**
- * IdAsValueDropdown 的公共接口
- */
-export interface IdAsValueDropdownInterface {
-  /**
-   * 获取某个ID的Lablel
-   * @param value 要获取的ID
-   */
-  getLableByValue(value: number): string;
-  /**
-   * 重新加载数据
-   * @param clearValue 是否需要清除选中数据,默认否
-   */
-  reload(clearValue?: boolean): void;
-}
-/**
- * IdAsValueDropdown 的公共接口
- */
-export interface IdAsValueDropdownProps<T extends DataModel> {
-  /**
-   * 允许清除
-   */
-  allowClear?: boolean,
-  /**
-   * 显示空?
-   */
-  showNull?: boolean,
-  /**
-   * 禁用
-   */
-  disabled?: boolean,
-  /**
-   * 多选?
-   */
-  multiple?: boolean,
-  /**
-   * 允许搜索
-   */
-  showSearch?: boolean,
-  placeholder?: string,
-  /**
-   * 未找到数据时的文案
-   */
-  notFoundContent?: string,
-  /**
-   * 初始化时加载数据
-   */
-  loadAtStart?: boolean,
-  /**
-   * 不使用后端筛选数据而是前端直接筛选
-   */
-  filterDirectly?: boolean,
-  /**
-   * 初始化时的搜索数据
-   */
-  intitialSearchValue?: Record<string, unknown>,
-  /**
-   * 加载数据回调
-   */
-  loadData: LoadDataFun<T>,
-  /**
-   * a-select 其他自定义参数
-   */
-  customProps?: SelectProps,
-  /**
-   * 是否自定义渲染option插槽
-   */
-  renderOption?: RenderOption;
-}
-export type RenderOption = (data: {
-  value: unknown,
-  label: string,
-}) => VNode;

+ 0 - 253
src/components/dynamicf/Dropdown/IdAsValueDropdown.vue

@@ -1,253 +0,0 @@
-<template>
-  <a-select
-    :value="valueV"
-    :mode="multiple ? 'multiple' : 'combobox'"
-    :allowClear="allowClear"
-    :showSearch="showSearch"
-    :disabled="disabled"
-    :placeholder="placeholder"
-    :default-active-first-option="false"
-    :notFoundContent="notFoundContent"
-    :options="data"
-    :filterOption="showSearch && filterDirectly ? filterOption : false"
-    @update:value="handleChange"
-    @search="handleSearch"
-    v-bind="customProps"
-    style="min-width: 150px"
-  >
-    <template v-if="renderOption" #option="data">
-      <VNodeRenderer :render="renderOption" :data="data" />
-    </template>
-    <a-select-option v-if="showNull" :value="null">(空)</a-select-option>
-  </a-select>
-</template>
-
-<script lang="ts">
-import VNodeRenderer from "@/components/VNodeRenderer.vue";
-import { type SelectProps } from "ant-design-vue";
-import { defineComponent, markRaw, type PropType, type VNode } from "vue";
-import { debounce } from 'lodash-es';
-import type { DropdownValues, LoadDataFun } from "./IdAsValueDropdown";
-import type { DataModel } from "@imengyu/js-request-transform";
-import { StringUtils } from "@imengyu/imengyu-utils";
-
-/**
- * IdAsValueDropdown 的公共接口
- */
-export interface IdAsValueDropdownInterface {
-  /**
-   * 获取某个ID的Lablel
-   * @param value 要获取的ID
-   */
-  getLableByValue(value: number): string;
-  /**
-   * 重新加载数据
-   * @param clearValue 是否需要清除选中数据,默认否
-   */
-  reload(clearValue?: boolean): void;
-}
-/**
- * IdAsValueDropdown 的公共接口
- */
-export interface IdAsValueDropdownProps<T extends DataModel> {
-  /**
-   * 允许清除
-   */
-  allowClear?: boolean,
-  /**
-   * 显示空?
-   */
-  showNull?: boolean,
-  /**
-   * 禁用
-   */
-  disabled?: boolean,
-  /**
-   * 多选?
-   */
-  multiple?: boolean,
-  /**
-   * 允许搜索
-   */
-  showSearch?: boolean,
-  placeholder?: string,
-  /**
-   * 未找到数据时的文案
-   */
-  notFoundContent?: string,
-  /**
-   * 初始化时加载数据
-   */
-  loadAtStart?: boolean,
-  /**
-   * 不使用后端筛选数据而是前端直接筛选
-   */
-  filterDirectly?: boolean,
-  /**
-   * 初始化时的搜索数据
-   */
-  intitialSearchValue?: Record<string, unknown>,
-  /**
-   * 加载数据回调
-   */
-  loadData: LoadDataFun<T>,
-  /**
-   * a-select 其他自定义参数
-   */
-  customProps?: SelectProps,
-  /**
-   * 是否自定义渲染option插槽
-   */
-  renderOption?: RenderOption<T>;
-}
-type RenderOption<T> = (data: {
-  value: unknown,
-  label: string,
-  raw: T
-}) => VNode;
-
-/**
- * 使用数据的ID作为value的下拉框包装
- */
-export default defineComponent({
-  name: "IdAsValueDropdown",
-  data() {
-    return {
-      valueV: null,
-      data: [] as DropdownValues<DataModel>[],
-      lastLoadValue: null,
-      handleSearch: markRaw(debounce((val: string) => {
-        if (!this.filterDirectly)
-          this.doLoadData(val);
-      }, 500)),
-    };
-  },
-  emits: [
-    "update:value",
-    "change",
-    "loaded",
-  ],
-  props: {
-    showNull: {
-      default: false,
-      type: Boolean
-    },
-    renderOption: {
-      default: null,
-      type: Function as PropType<RenderOption<DataModel>>
-    },
-    allowClear: {
-      default: false,
-      type: Boolean
-    },
-    multiple: {
-      default: false,
-      type: Boolean
-    },
-    disabled: {
-      default: false,
-      type: Boolean
-    },
-    showSearch: {
-      default: true,
-      type: Boolean
-    },
-    placeholder: {
-      default: "输入可进行搜索",
-      type: String
-    },
-    notFoundContent: {
-      default: "未找到数据,请换个搜索词再试",
-      type: String
-    },
-    loadAtStart: {
-      default: true,
-      type: Boolean
-    },
-    filterDirectly: {
-      default: true,
-      type: Boolean
-    },
-    value: {
-      default: null,
-    },
-    intitialSearchValue: {
-      default: null,
-      type: String
-    },
-    loadData: {
-      type: Function as PropType<LoadDataFun<DataModel>>,
-      default: null,
-    },
-    /**
-     * a-select 其他自定义参数
-     */
-    customProps: {
-      type: Object as PropType<SelectProps>,
-      default: null,
-    },
-  },
-  methods: {
-    handleChange(value: unknown) {
-      this.$emit("change", value);
-      this.$emit("update:value", value);
-    },
-    doLoadData(val: string | null) {
-      if (typeof this.loadData === "function") {
-        const oldValue = this.valueV;
-        this.valueV = null;
-        (this.loadData as LoadDataFun<DataModel>)(val).then((d) => {
-          this.data = d;
-          setTimeout(() => {
-            this.valueV = oldValue;
-            this.$emit("loaded");
-          }, 30);
-        });
-      }
-    },
-    filterOption(input: string, option: {
-      label: string;
-    }) {
-      return !this.filterDirectly || option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
-    },
-    getLableByValue(value: number) {
-      for (let i = 0; i < this.data.length; i++) {
-        if (this.data[i].value == value) {
-          return this.data[i].label;
-        }
-      }
-      return "";
-    },
-    reload(clearValue = false) {
-      if (clearValue) {
-        this.valueV = null;
-        this.handleChange(null);
-      }
-      this.data = [];
-      this.doLoadData(this.intitialSearchValue as string);
-    },
-  },
-  watch: {
-    loadData() {
-      this.doLoadData(this.intitialSearchValue);
-    },
-    intitialSearchValue(v) {
-      if (!this.filterDirectly && !StringUtils.isNullOrEmpty(v)) {
-        this.doLoadData(v);
-      }
-    },
-    value(v) {
-      this.valueV = v;
-    },
-  },
-  mounted() {
-    this.valueV = this.value;
-    setTimeout(() => {
-      if (this.loadAtStart) {
-        this.doLoadData(this.intitialSearchValue as string);
-      }
-    }, 300);
-  },
-  components: { VNodeRenderer }
-});
-</script>

+ 0 - 336
src/components/dynamicf/Dropdown/IdAsValueTreeDropdown.vue

@@ -1,336 +0,0 @@
-<template>
-  <div v-if="showDisplayValue" class="display-value" @click="handleDisplayValueClick">
-    <span>{{displayValue}}</span>
-  </div>
-  <a-tree-select
-    v-else
-    ref="selectRef"
-    style="min-width: 150px"
-    :defaultOpen="true"
-    :value="valueV"
-    :dropdown-style="dropdownStyle"
-    :notFoundContent="notFoundContent"
-    :tree-data="treeData"
-    :load-data="handleLoadData"
-    :treeDataSimpleMode="true"
-    :placeholder="placeholder"
-    :allow-clear="allowClear"
-    :multiple="multiple"
-    v-bind="customProps"
-    @blur="handleSelectBlur"
-    @update:value="handleChange"
-  />
-</template>
-
-<script lang="ts">
-import type { SelectProps } from "ant-design-vue";
-import { defineComponent, type PropType } from "vue";
-import type { TreeDataItem } from "../IdAsValueTree";
-
-export type LoadDataFun = (pid: string|number, level: number) => Promise<TreeDataItem[]>;
-export type CheckClickableFun = (item: TreeDataItem) => Promise<boolean>;
-
-export type GetDiaplayValue = (ref: IdAsValueTreeDropdownInterface) => string;
-export type GetRef = (ref: IdAsValueTreeDropdownInterface) => void;
-
-/**
- * IdAsValueTreeDropdown 的公共接口
- */
-export interface IdAsValueTreeDropdownInterface {
-  /**
-   * 获取某个ID的树(正排列)
-   * @param value 要获取的ID
-   */
-  getTree(value: number) : Array<TreeDataItem>;
-  /**
-   * 获取某个ID的Lablel
-   * @param value 要获取的ID
-   */
-  getLableByValue(value: number) : string;
-  /**
-   * 重新加载数据
-   */
-  reload(): void;
-}
-/**
- * IdAsValueTreeDropdown 的公共接口
- */
-export interface IdAsValueTreeDropdownProps {
-  /**
-   * 允许清除
-   */
-  allowClear?: boolean,
-  /**
-   * 多选?
-   */
-  multiple?: boolean,
-  dropdownStyle?: Record<string, unknown>,
-  disabled?: boolean,
-  placeholder?: string,
-  /**
-   * 未找到数据时的文案
-   */
-  notFoundContent?: string,
-  /**
-   * 初始化时加载数据
-   */
-  loadAtStart?: boolean,
-  /**
-   * 加载数据
-   */
-  loadData?: LoadDataFun,
-  /**
-   * 自定义检查条目是否可点击回调
-   */
-  checkClickable?: CheckClickableFun,
-  /**
-   * 获取显示数据回调
-   */
-  getDisplayValue?: GetDiaplayValue,
-  /**
-   * 是否在非激活时显示临时字符串(防止树形数据没有加载,而无法显示当前值)
-   */
-  showDisplayValueBeforeEdit?: boolean,
-  /**
-   * 子数据最大层级
-   */
-  maxLevel?: number,
-  /**
-   * 是否只有最后一级可以点击
-   */
-  onlyLastLevelClickable?: boolean,
-  /**
-   * a-select 其他自定义参数
-   */
-  customProps?: SelectProps,
-}
-
-/**
- * 使用数据的ID作为value的下拉框包装
- */
-export default defineComponent({
-  name: "IdAsValueTreeDropdown",
-  emits: [
-    'update:value',
-    'change',
-    'blur',
-  ],
-  props: {
-    allowClear: {
-      default: true,
-      type: Boolean
-    },
-    multiple: {
-      default: false,
-      type: Boolean
-    },
-    dropdownStyle: {
-      type: Object,
-      default: () => { return { maxHeight: '400px', overflow: 'auto' } }
-    },
-    disabled: {
-      default: false,
-      type: Boolean
-    },
-    placeholder: {
-      default: '请选择,输入可进行搜索',
-      type: String
-    },
-    notFoundContent: {
-      default: '未找到数据,请换个搜索词再试',
-      type: String
-    },
-    loadAtStart: {
-      default: true,
-      type: Boolean
-    },
-    value: {
-      default: null,
-    },
-    loadData: {
-      type: Function as PropType<LoadDataFun>,
-      default: null,
-    },
-    checkClickable: {
-      type: Function as PropType<CheckClickableFun>,
-      default: null,
-    },
-    getDisplayValue: {
-      type: Function as PropType<GetDiaplayValue>,
-      default: null,
-    },
-    defaultDisplayValue: {
-      type: String,
-      default: '',
-    },
-    showDisplayValueBeforeEdit: {
-      default: false,
-      type: Boolean
-    },
-    maxLevel: {
-      default: 0,
-      type: Number,
-    },
-    onlyLastLevelClickable: {
-      default: false,
-      type: Boolean
-    },
-    /**
-     * a-select 其他自定义参数
-     */
-    customProps: {
-      type: Object as PropType<SelectProps>,
-      default: null,
-    },
-  },
-  computed: {
-    displayValue() : string {
-      if (this.valueV != null && this.valueV != 0 && this.defaultDisplayValue != '')
-        return this.defaultDisplayValue;
-      if (this.getDisplayValue)
-        return (this.getDisplayValue as GetDiaplayValue)(this as IdAsValueTreeDropdownInterface); 
-      return '';
-    },
-  },
-  methods: {
-    handleChange(value: unknown) {
-      this.$nextTick(() => {
-        if(value != this.value) {
-          this.$emit('update:value', value); 
-          this.$emit('change', value); 
-        }
-      })
-    },
-    handleLoadData(treeNode: { dataRef: TreeDataItem }) {
-      return new Promise((resolve: (value?: unknown) => void) => {
-        const { id, level } = treeNode.dataRef;
-        this.doLoadData(id, level as number).then(() => resolve()).catch(() => resolve());
-      });
-    },
-    handleDisplayValueClick() {
-      this.showDisplayValue = false;
-      setTimeout(() => {
-        (this.$refs.selectRef as {
-          focus: () => void
-        }).focus();
-      }, 200);
-    },
-    handleSelectBlur() {
-      if(this.showDisplayValueBeforeEdit) {
-        if(this.valueV != null && this.valueV != 0 && this.defaultDisplayValue != '')
-          this.showDisplayValue = true;
-        else if (this.getLableByValue(this.valueV as number) === '') //只有没有在列表中搜索到数据时,才显示临时数据
-          this.showDisplayValue = true;
-      }
-    },
-    doLoadData(pid: string|number|null, level: number) {
-      const loadData = this.loadData;
-      if(typeof loadData === 'function') {
-        return (loadData as LoadDataFun)(pid as string, level).then((d) => {
-          for(let i = this.treeData.length - 1; i >= 0; i--)
-            if(this.treeData[i].pId == pid)
-              this.treeData.splice(i, 1);
-          d.forEach(h => {
-            h.level = level + 1;
-            if(this.maxLevel > 0 && h.level >= this.maxLevel)
-              h.isLeaf = true;
-            if(typeof this.checkClickable === 'function')
-              this.checkClickable(h).then((v: boolean) => h.selectable = v);
-            else if(this.maxLevel > 0) { 
-              if(h.level >= this.maxLevel)
-                h.selectable = false;
-              if(this.onlyLastLevelClickable) 
-                h.selectable = (h.level == this.maxLevel);
-            }
-            this.treeData.push(h)
-          });
-        });
-      } else 
-        return Promise.resolve();
-    },
-
-    /**
-     * 获取某个ID的树(正排列)
-     */
-    getTree(value: number) {
-      const result = new Array<TreeDataItem>();
-      let child : TreeDataItem|null = this.treeData.find((v) => v.id == value) as TreeDataItem;
-      while(child) {
-        result.unshift(child);
-        if(child.pId == 0) child = null;
-        else child = this.treeData.find((v) => v.id == (child as TreeDataItem).pId) as TreeDataItem;
-      }
-      return result;
-    },
-    /**
-     * 获取某个ID的Lablel
-     */
-    getLableByValue(value: number) {
-      const data = this.treeData;
-      for (let i = 0; i < data.length; i++) {
-        if(data[i].value == value) {
-          return data[i].title;
-        }
-      }
-      return '';
-    },
-    /**
-     * 重新加载数据
-     */
-    reload() {
-      this.treeData = [];
-      this.doLoadData(0, 0) 
-    },
-  },
-  watch: {
-    value(v) {
-      this.valueV = v;
-    },
-    showDisplayValueBeforeEdit(v, old) {
-      if(!old && v) {
-        this.showDisplayValue = true;
-      }
-    },
-  },
-  data() {
-    return {
-      showDisplayValue: false,
-      valueV: null as null|number|string,
-      treeData: [] as TreeDataItem[],
-    }
-  },
-  mounted() { 
-    this.valueV = this.value;
-    if(this.showDisplayValueBeforeEdit)
-      this.showDisplayValue = true;
-    setTimeout(() => { 
-      if(this.loadAtStart) {
-        this.treeData = [];
-        this.doLoadData(0, 0) ;
-      }
-    } , 300);
-  }
-});
-</script>
-
-<style lang="scss" scoped>
-.display-value {
-  min-width: 150px;
-  padding: 4px 11px;
-  color: rgba(0, 0, 0, 0.85);
-  font-size: 14px;
-  line-height: 1.5715;
-  background-color: #fff;
-  background-image: none;
-  border: 1px solid #d9d9d9;
-  border-radius: 2px;
-}
-//暗黑主题
-body[data-theme="dark"] {
-  .display-value {
-    color: #dedede;
-    background-color: #1f1f1f;
-    border: 1px solid #434343;
-  }
-}
-</style>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 109
src/components/dynamicf/Editor/EditorWrapper.vue


+ 0 - 127
src/components/dynamicf/IdAsValueTree.ts

@@ -1,127 +0,0 @@
-import type { SelectProps } from "ant-design-vue";
-
-export type LoadDataFun = (pid: string|number, level: number) => Promise<TreeNode[]>;
-export type CheckClickableFun = (item: TreeNode) => Promise<boolean>;
-
-export type GetDiaplayValue = (ref: IdAsValueTreeInterface) => string;
-export type GetRef = (ref: IdAsValueTreeInterface) => void;
-export interface TreeDataItem {
-  id: string | number;
-  pId?: number;
-  value: string | number;
-  title?: string;
-  isLeaf?: boolean;
-  selectable?: boolean;
-  checkable?: boolean;
-  disableCheckbox?: boolean;
-  disabled?: boolean;
-  level?: number;
-}
-
-export interface TreeNode {
-  id?: number;
-  pid?: number;
-  level?: number;
-
-  /**
-   * 当树为 checkable 时,设置独立节点是否展示 Checkbox
-   */
-  checkable?: boolean;
-  /**
-   * 节点的 class
-   */
-  class?: string;
-  /**
-   * 	禁掉 checkbox
-   */
-  disableCheckbox?: boolean;
-  /**
-   * 禁掉响应
-   */
-  disabled?: boolean;
-  /**
-   * 自定义图标。可接收组件,props 为当前节点 props
-   */
-  icon?: unknown;
-  /**
-   * 设置为叶子节点(设置了loadData时有效)
-   */
-  isLeaf?: boolean;
-  /**
-   * 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复!
-   */
-  key: string | number;
-  /**
-   * 设置节点是否可被选中
-   */
-  selectable?: boolean;
-  /**
-   * 节点的 style	
-   */
-  // eslint-disable-next-line @typescript-eslint/ban-types
-  style?: string|object;
-  /**
-   * 标题
-   */
-  title: string;
-
-  children?: TreeNode[],
-}
-
-/**
- * IdAsValueTree 的公共接口
- */
-export interface IdAsValueTreeInterface {
-  /**
-   * 获取某个ID的树(正排列)
-   * @param value 要获取的ID
-   */
-  getTree(value: number) : Array<TreeNode>;
-  /**
-   * 获取某个ID的Lablel
-   * @param value 要获取的ID
-   */
-  getLableByValue(value: number) : string;
-  /**
-   * 重新加载数据
-   */
-  reload(): void;
-}
-/**
- * IdAsValueTree 的公共接口
- */
-export interface IdAsValueTreeProps {
-  /**
-   * 允许清除
-   */
-  allowClear?: boolean,
-  /**
-   * 多选?
-   */
-  multiple?: boolean,
-  disabled?: boolean,
-  /**
-   * 初始化时加载数据
-   */
-  loadAtStart?: boolean,
-  /**
-   * 加载数据
-   */
-  loadData?: LoadDataFun,
-  /**
-   * 自定义检查条目是否可点击回调
-   */
-  checkClickable?: CheckClickableFun,
-  /**
-   * 子数据最大层级
-   */
-  maxLevel?: number,
-  /**
-   * 是否只有最后一级可以点击
-   */
-  onlyLastLevelClickable?: boolean,
-  /**
-   * a-select 其他自定义参数
-   */
-  customProps?: SelectProps,
-}

+ 0 - 179
src/components/dynamicf/IdAsValueTree.vue

@@ -1,179 +0,0 @@
-<template>
-  <div class="IdAsValueTree">
-    <a-tree
-      ref="selectRef"
-      style="min-width: 150px"
-      :defaultOpen="true"
-      v-model:expandedKeys="expandedKeys"
-      v-model:checkedKeys="checkedKeys"
-      checkable
-      :tree-data="treeData"
-      :load-data="handleLoadData"
-      :allow-clear="allowClear"
-      v-bind="customProps"
-    />
-  </div>
-</template>
-
-<script lang="ts">
-import type { TreeProps } from "ant-design-vue";
-import { defineComponent, type PropType } from "vue";
-import type { CheckClickableFun, LoadDataFun, TreeNode } from "./IdAsValueTree";
-
-/**
- * 使用数据的ID作为value的tree包装
- */
-export default defineComponent({
-  name: "IdAsValueTree",
-  emits: [
-    'update:value',
-    'change',
-    'blur',
-  ],
-  props: {
-    allowClear: {
-      default: true,
-      type: Boolean
-    },
-    disabled: {
-      default: false,
-      type: Boolean
-    },
-    loadAtStart: {
-      default: true,
-      type: Boolean
-    },
-    value: {
-      default: null,
-    },
-    loadData: {
-      type: Function as PropType<LoadDataFun>,
-      default: null,
-    },
-    checkClickable: {
-      type: Function as PropType<CheckClickableFun>,
-      default: null,
-    },
-    maxLevel: {
-      default: 0,
-      type: Number,
-    },
-    onlyLastLevelClickable: {
-      default: false,
-      type: Boolean
-    },
-    /**
-     * a-select 其他自定义参数
-     */
-    customProps: {
-      type: Object as PropType<TreeProps>,
-      default: null,
-    },
-  },
-  methods: {
-    handleChange() {
-      this.$nextTick(() => {
-        this.$emit('update:value', this.checkedKeys); 
-        this.$emit('change', this.checkedKeys); 
-      })
-    },
-    handleLoadData(treeNode: { dataRef: TreeNode }|null) {
-      return new Promise((resolve: (value?: unknown) => void) => {
-        this.doLoadData(treeNode?.dataRef || null).then(() => resolve()).catch(() => resolve());
-      });
-    },
-    doLoadData(dataRef: TreeNode|null) {
-      const { id, level } = dataRef || { id: 0, level: 0 };
-      const pid = id as number;
-      const loadData = this.loadData;
-      if(typeof loadData === 'function') {
-        return (loadData as LoadDataFun)(pid, level as number).then((d) => {
-          if (dataRef && !dataRef.children)
-            dataRef.children = [];
-          d.forEach(h => {
-            h.level = level as number + 1;
-            if(this.maxLevel > 0 && h.level >= this.maxLevel)
-              h.isLeaf = true;
-            if(typeof this.checkClickable === 'function')
-              this.checkClickable(h).then((v: boolean) => h.selectable = v);
-            else if(this.maxLevel > 0) { 
-              if(h.level >= this.maxLevel)
-                h.selectable = false;
-              if(this.onlyLastLevelClickable) 
-                h.selectable = (h.level == this.maxLevel);
-            }
-            if (dataRef)
-              dataRef.children?.push(h);
-            else
-              this.treeData.push(h);
-          });
-        });
-      } else 
-        return Promise.resolve();
-    },
-
-    /**
-     * 获取某个ID的树(正排列)
-     */
-    getTree(value: number) {
-      const result = new Array<TreeNode>();
-      let child : undefined|TreeNode = (this.treeData as TreeNode[]).find((v) => v.id == value);
-      while(child) {
-        result.unshift(child);
-        if(child.pid == 0) child = undefined;
-        else child = (this.treeData as TreeNode[]).find((v) => v.id == (child as TreeNode).pid);
-      }
-      return result;
-    },
-    /**
-     * 获取某个ID的Lablel
-     */
-    getLableByValue(value: number) {
-      const data = this.treeData;
-      for (let i = 0; i < data.length; i++) {
-        if(data[i].id == value) {
-          return data[i].title;
-        }
-      }
-      return '';
-    },
-    /**
-     * 重新加载数据
-     */
-    reload() {
-      this.treeData = [];
-    },
-  },
-  watch: {
-    value(v) {
-      this.checkedKeys = (v as string[]);
-    },
-    checkedKeys() {
-      this.handleChange();
-    },
-  },
-  data() {
-    return {
-      expandedKeys: [] as string[],
-      checkedKeys: [] as string[],
-      treeData: [] as TreeNode[],
-    }
-  },
-  mounted() { 
-    this.checkedKeys = (this.value as unknown as string[]) || [];
-    setTimeout(() => { 
-      if(this.loadAtStart) {
-        this.treeData = [];
-        this.handleLoadData(null);
-      }
-    } , 300);
-  }
-});
-</script>
-
-<style>
-.IdAsValueTree {
-  border: 1px solid #efefef;
-  padding: 10px;
-}
-</style>

+ 0 - 194
src/components/dynamicf/Map/AddressSercher.vue

@@ -1,194 +0,0 @@
-<script setup lang="ts">
-import { ref, computed, nextTick, watch, type Ref } from 'vue';
-import { SearchOutlined } from '@ant-design/icons-vue';
-import type { SelectProps } from 'ant-design-vue';
-import { waitTimeOut } from '@imengyu/imengyu-utils';
-import { ScrollRect } from '@imengyu/vue-scroll-rect';
-
-// 定义Props
-const props = defineProps<{
-  modelValue?: string;
-  disabled?: boolean;
-}>();
-
-// 定义Emits
-const emit = defineEmits<{
-  'update:modelValue': [value: string];
-  'choosedAddress': [address: AddressItem];
-}>();
-
-export interface AddressItem {
-  name: string;
-  lng: number;
-  lat: number;
-  address: string;
-}
-
-// 状态管理
-const inputValue = ref(props.modelValue);
-const addressList = ref<Array<AddressItem>>([]);
-const loading = ref(false);
-const openList = ref(false);
-
-// 监听modelValue变化
-watch(() => props.modelValue, (newValue) => {
-  inputValue.value = newValue ;
-});
-
-
-
-// 调用高德API搜索地址
-async function searchAddress() {
-  if (!inputValue.value?.trim()) return;
-  loading.value = true;
-  openList.value = true;
-
-  try {
-    const apiKey = '8fd09264c33678141f609588c432df0e';
-    const url = `https://restapi.amap.com/v3/place/text?key=${apiKey}&keywords=${encodeURIComponent(inputValue.value)}&citylimit=true&offset=10&page=1`;
-    
-    const response = await fetch(url);
-    const data = await response.json();
-    
-    if (data.status === '1' && data.pois && data.pois.length > 0) {
-      console.log('搜索到的地址:', data.pois);
-      
-      addressList.value = data.pois.map((item: any) => ({
-        name: item.name,
-        lng: parseFloat(item.location.split(',')[0]),
-        lat: parseFloat(item.location.split(',')[1]),
-        address: item.address ? `${item.cityname}${item.adname}${item.address} ${item.name}` : item.name
-      }));
-    } else {
-      addressList.value = [];
-    }
-  } catch (error) {
-    console.error('搜索地址失败:', error);
-    addressList.value = [];
-  } finally {
-    loading.value = false;
-  }
-}
-
-// 选择地址
-function handleSelectAddress(address: any) {
-  // 更新输入值
-  inputValue.value = address.address;
-  // 关闭下拉列表
-  openList.value = false;
-  
-  // 发送详细地址信息
-  emit('choosedAddress', {
-    name: address.name,
-    lng: address.lng,
-    lat: address.lat,
-    address: address.address
-  });
-}
-
-watch(inputValue, (newValue) => {
-  emit('update:modelValue', newValue ?? '');
-})
-
-// 处理输入变化
-function handleInputChange(value: string) {
-  inputValue.value = value;
-  emit('update:modelValue', value);
-}
-
-// 处理点击搜索按钮
-function handleSearch() {
-  searchAddress();
-}
-
-// 定义select组件的选项
-const selectOptions = computed<SelectProps['options']>(() => {
-  return addressList.value.map(item => ({
-    label: item.name,
-    value: item.address,
-    data: item
-  }));
-});
-</script>
-
-<template>
-  <div class="address-searcher">
-    <div class="input-wrapper">
-      <a-input
-        v-model:value="inputValue"
-        mode="combobox"
-        :options="selectOptions"
-        :disabled="props.disabled"
-        :show-search="true"
-        :default-active-first-option="false"
-        :filter-option="false"
-        :not-found-content="'暂无匹配的可选地址,可修改关键字扩大搜索范围'"
-        placeholder="请输入地址"
-        style="width: 100%;"
-        @select="(value: string, option: any) => handleSelectAddress(option?.data)"
-        @search="handleInputChange"
-      />
-    </div>
-    <a-popover placement="bottomRight" trigger="click" v-model:open="openList" >
-      <template #content>
-        <ScrollRect scroll="vertical">
-          <a-list class="list" size="small" itemLayout="vertical" :data-source="selectOptions">
-            <template #renderItem="{ item }">
-              <div class="list-item" @click="handleSelectAddress(item.data)">
-                <div class="list-item-content">
-                  <div class="list-item-title">{{ item.label }}</div>
-                  <div class="list-item-desc">{{ item.value }}</div>
-                </div>
-              </div>
-            </template>
-          </a-list>
-        </ScrollRect>
-      </template>
-      <template #title>
-        <span>模糊地址查询</span>
-      </template>
-      <a-button :disabled="props.disabled" type="primary" :loading="loading" @click="handleSearch">
-        <SearchOutlined />
-        搜索
-      </a-button>
-    </a-popover>
-  </div>
-</template>
-
-<style scoped lang="scss">
-.address-searcher {
-  position: relative;
-  display: flex;
-  align-items: center;
-  gap: 8px;
-
-  .input-wrapper {
-    flex: 1;
-  }
-}
-.list {
-  width: 50vw;
-  max-width: 600px;
-  max-height: 70vh;
-
-  .list-item {
-    padding: 8px 12px;
-    cursor: pointer;
-    &:hover {
-      background-color: #f5f5f5;
-    }
-  }
-  .list-item-content {
-    display: flex;
-    flex-direction: column;
-  }
-  .list-item-title {
-    font-size: 14px;
-    font-weight: 500;
-  }
-  .list-item-desc {
-    font-size: 12px;
-    color: #999;
-  }
-}
-</style>

BIN
src/components/dynamicf/Map/Maker.png


+ 0 - 112
src/components/dynamicf/Map/MapPointPicker.vue

@@ -1,112 +0,0 @@
-<template>
-  <div :style="{ width, height }" class="map-point-picker">
-    <img src="./Maker.png" />
-    <span class="lonlat">经纬度:{{ Number(center[0]).toFixed(6) }}, {{ Number(center[1]).toFixed(6) }}</span>
-    <el-amap
-      style="width: 100%"
-      :center="center"
-      :zoom="zoom"
-      :dragEnable="!disabled"
-      :zoomEnable="!disabled"
-      :rotateEnable="!disabled"
-      :keyboardEnable="!disabled"
-      :doubleClickZoom="!disabled"
-      :scrollWheel="!disabled"
-      @init="handleInit"
-      @update:center="handleUpdateCenter"
-      v-bind="$attrs"
-    >
-    </el-amap>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { onMounted, ref, watch, type PropType } from 'vue';
-import { defaultCenter } from './MapConfig';
-
-const props = defineProps({
-  disabled: {
-    type: Boolean,
-    default: false
-  },
-  modelValue: {
-    type: Object as PropType<(number|string)[]>,
-    default: () => defaultCenter.concat()
-  },
-  zoom: {
-    type: Number,
-    default: 12
-  },
-  width: {
-    type: [Number, String],
-    default: '100%'
-  },
-  height: {
-    type: [Number, String],
-    default: '300px'
-  }
-});
-
-const emit = defineEmits(['update:modelValue' ])
-const center = ref(defaultCenter);
-let map: any = null;
-
-function handleInit(mapRef: any) {
-  map = mapRef;
-}
-
-function loadCenter() {
-  center.value = [Number(props.modelValue[0]), Number(props.modelValue[1])];
-  if (isNaN(center.value[0])) center.value[0] = defaultCenter[0];
-  if (isNaN(center.value[1])) center.value[1] = defaultCenter[1];
-}
-
-watch(() => props.modelValue, (newVal) => {
-  loadCenter()
-})
-function handleUpdateCenter(newVal: (number|string)[]) {
-  emit('update:modelValue', newVal);
-}
-
-onMounted(() => {
-  loadCenter();
-})
-
-defineExpose({
-  moveTo(lonlat: (number|string)[], zoom?: number) {
-    map.setCenter(lonlat);
-    if (zoom) {
-      map.setZoom(zoom);
-    }
-  }
-})
-
-</script>
-
-<style scoped>
-div {
-  position: relative;
-  border: 1px solid #ccc;
-  border-radius: 10px;
-}
-.lonlat {
-  position: absolute;
-  top: 10px;
-  left: 10px;
-  font-size: 12px;
-  padding: 10px;
-  color: #333;
-  background-color: #fff;
-  z-index: 100;
-}
-img {
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, -50%);
-  width: 30px;
-  height: 30px;
-  pointer-events: none;
-  z-index: 2;
-}
-</style>

+ 0 - 69
src/components/dynamicf/NumberRange.vue

@@ -1,69 +0,0 @@
-<template>
-  <div class="vc-number-range d-flex flex-row align-items-center">
-    <a-input-number placeholder="最小值" :disabled="disabled" :value="realValue[0]" @update:value="(v: number) => onUpdateValue(v, 0)" v-bind="customProps" />
-    <span class="p-2">-</span>
-    <a-input-number placeholder="最大值" :disabled="disabled" :value="realValue[1]" @update:value="(v: number) => onUpdateValue(v, 1)" v-bind="customProps" />
-  </div>
-</template>
-
-<script lang="ts" setup>
-/**
- * 下拉框表单控件,用于解决 a-select 不能选择对象的问题
- */
-import { Form, type InputNumberProps } from 'ant-design-vue';
-import { type PropType, ref, watch, onMounted } from 'vue';
-
-const props = defineProps({
-  /**
-   * 是否禁用
-   */
-  disabled: {
-    type: Boolean,
-    default: false
-  },
-  /**
-   * 选择值
-   */
-  value: {
-  },
-  /**
-   * a-number-input 其他自定义参数
-   */
-  customProps: {
-    type: Object as PropType<InputNumberProps>,
-    default: null,
-  },
-});
-
-const emits = defineEmits([
-  'update:value',
-]);
-
-const realValue = ref<number[]>([]);
-const { onFieldChange } = Form.useInjectFormItemContext();
-
-watch(() => props.value, (v) => {
-  if ((v as number[])?.length == 2)
-    realValue.value = v as number[];
-  else {
-    if (realValue.value.length === 1 && realValue.value[0] === undefined) {
-      realValue.value = [];
-      emits('update:value', []);
-    } else {
-      realValue.value = [];
-    }
-  }
-});
-onMounted(() => {
-  realValue.value = (props.value as number[])?.length == 2 ? props.value as number[] : [];
-});
-
-function onUpdateValue(v : number, index: number) {
-  realValue.value[index] = v;
-  if (realValue.value.length < 2)
-    realValue.value.push(0);
-  emits('update:value', realValue.value);
-  onFieldChange();
-}
-
-</script>

+ 0 - 116
src/components/dynamicf/PasswordStrengthMeter.vue

@@ -1,116 +0,0 @@
-<template>
-  <div class="password-meter">
-    <div class="bar">
-      <div :class="'level'+level"></div>
-    </div>
-    <span :class="'level'+level">密码强度 {{levelString}}</span>
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue'
-import { checkPassWordSecrityLevel } from '@imengyu/imengyu-utils'
-
-/**
- * 密码强度显示组件
- */
-export default defineComponent({
-  props: {
-    password: {
-      type: String,
-      default: '',
-    }
-  },
-  data() {
-    return {
-      level: 0,
-      levelString: '',
-    }
-  },
-  watch: {
-    password(v: string) {
-      this.level = Math.floor((checkPassWordSecrityLevel(v) / 100) * 5);
-      switch(this.level) {
-        case 0: this.levelString = '非常弱'; break;
-        case 1: this.levelString =  '弱'; break;
-        case 2: this.levelString =  '中等'; break;
-        case 3: this.levelString =  '强'; break;
-        case 4: this.levelString =  '非常强'; break;
-      }
-    },
-  }
-})
-</script>
-
-<style lang="scss">
-.password-meter {
-  position: relative;
-  height: 20px;
-  margin: 10px 0;
-
-  .bar {
-    position: absolute;
-    left: 0;
-    top: 0;
-    bottom: 0;
-    right: 130px;
-    background-color: #e7e7e7;
-    border: 1px solid #a1a1a1;
-
-    div {
-      position: absolute;
-      left: 0;
-      top: 0;
-      bottom: 0;
-
-      &.level0 {
-        width: 0;
-        background-color: #000;
-      }
-      &.level1 {
-        width: 25%;
-        background-color: #ca410a;
-      }
-      &.level2 {
-        width: 50%;
-        background-color: #d8c40c;
-      }
-      &.level3 {
-        width: 75%;
-        background-color: #9ab814;
-      }
-      &.level4 {
-        width: 100%;
-        background-color: #2fbe0b;
-      } 
-    }
-  }
-  > span {
-    position: absolute;
-    top: 0;
-    bottom: 0;
-    width: 100px;
-    right: 0;
-    font-size: 12px;
-
-    &.level0 {
-      color: #646464;
-    }
-    &.level1 {
-      color: #a8380c;
-    }
-    &.level2 {
-      color: #af9f0e;
-    }
-    &.level3 {
-      color: #91ac18;
-    }
-    &.level4 {
-      color: #26920b;
-    }
-  }
-
-
-
-}
-</style>

+ 0 - 37
src/components/dynamicf/PasswordWithStrengthInput.vue

@@ -1,37 +0,0 @@
-<template>
-  <div>
-    <a-input 
-      :value="value"
-      @update:value="(v: string) => $emit('update:value', v)"
-      :disabled="disabled"
-      type="password"
-      v-bind="(item?.additionalProps as {})"
-    />
-    <PasswordStrengthMeter :password="(value as string)" />
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent, type PropType } from "vue";
-import PasswordStrengthMeter from "./PasswordStrengthMeter.vue";
-import type { IDynamicFormItem } from "@imengyu/vue-dynamic-form";
-
-export default defineComponent({
-  props: {
-    item: {
-      type: Object as PropType<IDynamicFormItem>,
-    },
-    disabled: {
-      type: Boolean
-    },
-    value: {},
-    additionalProps: {
-      type: Object as PropType<Record<string, unknown>>,
-    },
-  },
-  emits: [
-    "update:value"
-  ],
-  components: { PasswordStrengthMeter }
-});
-</script>

+ 0 - 29
src/components/dynamicf/RadioValue.ts

@@ -1,29 +0,0 @@
-import type { RadioGroupProps, RadioProps } from "ant-design-vue";
-
-export interface IDynamicFormItemRadioValueOption {
-  text: string,
-  value: unknown,
-}
-
-export interface SimpleRadioValueFormItemProps {
-  /**
-   * 是否禁用
-   */
-  disabled: boolean;
-  /**
-   * 选项数据
-   */
-  options: IDynamicFormItemRadioValueOption[];
-  /**
-   * 选择值
-   */
-  value: unknown;
-  /**
-   * a-radio 其他自定义参数
-   */
-  customProps: RadioProps;
-  /**
-   * a-radio 其他自定义参数
-   */
-  customGroupProps: RadioGroupProps;
-}

+ 0 - 77
src/components/dynamicf/RadioValue.vue

@@ -1,77 +0,0 @@
-<template>
-  <a-radio-group 
-    :value="selectValue"
-    @update:value="onUpdateValue"
-    :disabled="disabled"
-    v-bind="customGroupProps"
-  >
-    <a-radio 
-      v-for="it in options"
-      :key="it.text"
-      :value="it.text"
-      v-bind="customProps"
-    >
-      {{it.text}}
-    </a-radio>
-  </a-radio-group>
-</template>
-
-<script lang="ts" setup>
-/**
- * 下拉框表单控件,用于解决 a-select 不能选择对象的问题
- */
-import type { RadioGroupProps, RadioProps } from 'ant-design-vue';
-import { type PropType, ref, watch, onMounted } from 'vue';
-import type { IDynamicFormItemRadioValueOption } from './RadioValue';
-
-const props = defineProps({
-  /**
-   * 是否禁用
-   */
-  disabled: {
-    type: Boolean,
-    default: false
-  },
-  /**
-   * 选项数据
-   */
-  options: {
-    type: Object as PropType<IDynamicFormItemRadioValueOption[]>,
-    default: null,
-  },
-  value: {
-  },
-  customProps: {
-    type: Object as PropType<RadioProps>,
-    default: null,
-  },
-  customGroupProps: {
-    type: Object as PropType<RadioGroupProps>,
-    default: null,
-  },
-});
-
-const emits = defineEmits([
-  'update:value',
-]);
-
-const selectValue = ref<string|null>('');
-
-function setRadioValue() {
-  selectValue.value = props.options.find(k => (k.value === props.value))?.text || null;
-  if (selectValue.value === null)
-    selectValue.value = props.options.find(k => (typeof k.value === typeof props.value))?.text || null;
-}
-
-watch(() => props.value, () => {
-  setRadioValue();
-});
-onMounted(() => {
-  setRadioValue();
-});
-
-function onUpdateValue(v : unknown) {
-  emits('update:value', props.options.find(k => k.text === v)?.value);
-}
-
-</script>

+ 0 - 25
src/components/dynamicf/SelectValue.ts

@@ -1,25 +0,0 @@
-import type { SelectProps } from "ant-design-vue";
-
-export interface IDynamicFormItemSelectValueOption {
-  text: string,
-  value: unknown,
-}
-
-export interface SimpleSelectValueFormItemProps {
-  /**
-   * 是否禁用
-   */
-  disabled: boolean;
-  /**
-   * 选项数据
-   */
-  options: IDynamicFormItemSelectValueOption[];
-  /**
-   * 选择值
-   */
-  value: unknown;
-  /**
-   * a-select 其他自定义参数
-   */
-  customProps: SelectProps;
-}

+ 0 - 74
src/components/dynamicf/SelectValue.vue

@@ -1,74 +0,0 @@
-<template>
-  <a-select
-    :value="selectValue"
-    @update:value="onUpdateValue"
-    :disabled="disabled"
-    v-bind="customProps"
-  >
-    <a-select-option v-for="it in options" :key="it.text" :value="it.text">
-      {{it.text}}
-    </a-select-option>
-  </a-select>
-</template>
-
-<script lang="ts" setup>
-/**
- * 下拉框表单控件,用于解决 a-select 不能选择对象的问题
- */
-import type { SelectProps } from 'ant-design-vue/lib/vc-select';
-import { type PropType, ref, watch, onMounted } from 'vue';
-import type { IDynamicFormItemSelectValueOption } from './SelectValue';
-
-const props = defineProps({
-  /**
-   * 是否禁用
-   */
-  disabled: {
-    type: Boolean,
-    default: false
-  },
-  /**
-   * 选项数据
-   */
-  options: {
-    type: Object as PropType<IDynamicFormItemSelectValueOption[]>,
-    default: null,
-  },
-  /**
-   * 选择值
-   */
-  value: {
-  },
-  /**
-   * a-select 其他自定义参数
-   */
-  customProps: {
-    type: Object as PropType<SelectProps>,
-    default: null,
-  },
-});
-
-const emits = defineEmits([
-  'update:value',
-]);
-
-const selectValue = ref<string|null>('');
-
-function setSelectValue() {
-  selectValue.value = props.options.find(k => (k.value === props.value))?.text || null;
-  if (selectValue.value === null)
-    selectValue.value = props.options.find(k => (typeof k.value === typeof props.value))?.text || null;
-}
-
-watch(() => props.value, () => {
-  setSelectValue();
-});
-onMounted(() => {
-  setSelectValue();
-});
-
-function onUpdateValue(v : unknown) {
-  emits('update:value', props.options.find(k => k.text === v)?.value);
-}
-
-</script>

+ 0 - 189
src/components/dynamicf/Sign.vue

@@ -1,189 +0,0 @@
-<template>
-  <div class="sign">
-    <vue-esign v-show="showSign" ref="esign" :disabled="disabled" />
-    <div v-show="!showSign" class="history">
-      <img :src="modelValue" alt="历史签名" />
-    </div>
-    <div v-if="!disabled" class="footer">
-      <div v-if="state === 'default'" class="tip">
-        <InfoCircleOutlined />
-        <span>请在虚线框内签名</span>
-      </div>
-      <div v-else-if="state === 'error'" class="tip error">
-        <ExclamationCircleOutlined />
-        <span>签名上传失败,请尝试重新上传</span>
-      </div>
-      <div v-else-if="state === 'success'" class="tip success">
-        <CheckOutlined />
-        <span>已签名,若有问题可以点击“重签”重新签名</span>
-      </div>
-      <div>
-        <a-button size="small" shape="round" @click="clear">重签</a-button>
-        <a-button type="primary" shape="round" size="small" @click="confirm">确认</a-button>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { onMounted, ref, type PropType } from 'vue';
-import VueEsign from 'vue-esign';
-import { InfoCircleOutlined, ExclamationCircleOutlined, CheckOutlined } from '@ant-design/icons-vue';
-import { Form, message, Modal } from 'ant-design-vue';
-import type { UploadCoInterface } from './UploadImageFormItem';
-
-const props = defineProps({
-  disabled: { 
-    type: Boolean, 
-    default: false 
-  },
-  modelValue: {
-    type: String,
-    default: ''
-  },
-  /**
-   * 上传工厂类
-   */
-  uploadCo: {
-    type: Object as PropType<UploadCoInterface>,
-    default: null,
-  },
-});
-
-const emit = defineEmits(['update:modelValue']);
-const esign = ref();
-const state = ref<'default'|'error'|'success'>('default');
-const showSign = ref(true);
-
-const formItemContext = Form.useInjectFormItemContext();
-
-onMounted(() => {
-  if (props.modelValue) {
-    state.value = 'success';
-    showSign.value = false;
-  }
-})
-
-const clear = () => {
-  if (props.modelValue) {
-    Modal.confirm({
-      title: '确认清除签名?',
-      okText: '清除',
-      onOk: () => {
-        emit('update:modelValue', '');
-        formItemContext.onFieldChange();
-        esign.value.reset();
-        state.value = 'default';
-        showSign.value = true;
-      }
-    })
-  } else {
-    state.value = 'default';
-    esign.value.reset();
-    showSign.value = true;
-  }
-}
-const confirm = () => {
-  esign.value.generate().then((res: string) => {
-    if (props.uploadCo) {
-      //上传
-      return new Promise((resolve, reject) => {
-        const blob = base64ToBlob(res, 'image/png');
-        const file = new File([blob], 'image.png', { type: 'image/png' });
-        props.uploadCo.uploadRequest({
-          file: file,
-          filename: 'sign.png',
-          action: '',
-          headers: {}, 
-          withCredentials: true, 
-          method: 'post',
-          data: {},
-          onProgress: () => {},
-          onSuccess: (res) => {
-            resolve(res.url);
-          },
-          onError: (err) => {
-            reject(err);
-          },
-        })
-      }).then((res) => {
-        message.success('签名上传成功');
-        state.value = 'success';
-        formItemContext.onFieldChange();
-        showSign.value = false;
-        emit('update:modelValue', res);
-      }).catch((err) => {
-        state.value = 'error';
-        Modal.error({
-          title: '上传失败',
-          content: '签名上传失败,请尝试重新上传:' + err.message,
-        })
-      });
-    } else {
-      // 不上传,直接返回base64字符串
-      formItemContext.onFieldChange();
-      showSign.value = false;
-      emit('update:modelValue', res);
-    }
-  });
-}
-
-function base64ToBlob(base64: string, mimeType = 'image/png') {
-  const byteCharacters = atob(base64.split(',')[1]); // 去掉 data:image/png;base64, 前缀
-  const byteNumbers = new Array(byteCharacters.length);
-  for (let i = 0; i < byteCharacters.length; i++) {
-    byteNumbers[i] = byteCharacters.charCodeAt(i);
-  }
-  const byteArray = new Uint8Array(byteNumbers);
-  return new Blob([byteArray], { type: mimeType });
-}
-
-</script>
-
-<style lang="scss" scoped>
-
-.sign {
-  width: 100%;
-  height: 100%;
-  border: 1px dashed #ddd;
-  border-radius: 10px;
-  overflow: hidden;
-
-  .history {
-    position: relative;
-    margin-bottom: 10px;
-
-    img {
-      max-width: 100%;
-      height: 100%;
-      object-fit: contain;
-    }
-  }
-  .tip {
-    font-size: 12px;
-    color: #999;
-
-    &.error {
-      color: #bd2028;
-    }
-    &.success {
-      color: #198754;
-    }
-  }
-  .footer {
-    padding: 10px;
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    justify-content: space-between;
-
-    > div {
-      display: flex;
-      gap: 5px;
-    }
-  }
-}
-
-</style>
-
-

+ 0 - 99
src/components/dynamicf/SimpleEditDynamicStringList.vue

@@ -1,99 +0,0 @@
-<template>
-  <div>
-    <div>
-      <a-button @click="() => value?.push('')">
-        添加 <template #icon><PlusOutlined /></template>
-      </a-button>
-      <a-popconfirm
-        v-if="value && value.length > 0"
-        title="真的要清空所有条目吗?"
-        @confirm="value?.splice(0, value.length)"
-      >
-        <a-button class="ml-3" danger >
-          <template #icon>
-            <CloseSquareOutlined />
-          </template>
-            清空
-        </a-button>
-      </a-popconfirm>
-    </div>
-    <div class="mt-2 position-relative" v-for="(v, k) in value" :key="k">
-      <input 
-        class="display-inline-block ant-input" 
-        style="width:calc(100% - 100px)" 
-        v-model="value[k]" 
-        :maxlength="maxLength" 
-        :disabled="disabled" 
-        :placeholder="placeholder" />
-      <a-popconfirm
-        title="确定删除此条目吗?"
-        @confirm="value?.slice(value.indexOf(v), 1)"
-      >
-        <a-button class="ml-3">
-          <template #icon><DeleteOutlined /></template>
-        </a-button>
-      </a-popconfirm>
-    </div>
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent, type PropType } from 'vue'
-import { PlusOutlined, DeleteOutlined, CloseSquareOutlined } from '@ant-design/icons-vue'
-
-/**
- * 简单的字符串列表动态表单
- */
-export default defineComponent({
-  name: 'SimpleEditDynamicStringList',
-  components: {
-    PlusOutlined,
-    DeleteOutlined,
-    CloseSquareOutlined,
-  },  
-  emits: [
-    'update:value',
-    'blur',
-  ],
-  props: {
-    /**
-     * ID
-     */
-    id: {
-      required: false
-    },
-    /**
-     * 参数数组
-     */
-    value: {
-      type: Object as PropType<Array<string>>
-    },
-    /**
-     * 输入框 placeholder
-     */
-    placeholder: {
-      type: String,
-      default: '请输入参数值',
-    },
-    /**
-     * 输入框最大输入长度
-     */
-    maxLength: {
-      type: Number,
-      default: 50,
-    },
-    /**
-     * sf
-     */
-    disabled: {
-      type: Boolean,
-      default: false,
-    },
-  },
-  data() {
-    return {
-      deleteConfirm: false,
-    }
-  },
-})
-</script>

+ 0 - 128
src/components/dynamicf/SimpleListDynamicForm.vue

@@ -1,128 +0,0 @@
-<template>
-  <a-row :gutter="gutter">
-    <a-col :span="span" v-for="(item, key) in items" :key="key">
-      <div class="d-flex flex-row">
-        <div class="flex-grow-1">
-          <a-form-item v-if="item.type=='text'" :label="item.label" :labelCol="labelCol" :wrapperCol="wrapperCol" :name="item.name||item.key">
-            <a-input 
-              :required="item.required"
-              v-model:value="item.value"
-              :placeholder="item.placeholder||''"
-              :addon-before="item.prefix"
-              :addon-after="item.suffix"
-              :max-length="item.appenderParams?(item.appenderParams.maxLength):undefined"
-            />
-          </a-form-item>
-          <a-form-item v-else-if="item.type=='number'" :label="item.label" :labelCol="labelCol" :wrapperCol="wrapperCol" :name="item.name||item.key">
-            <a-input-number 
-              :required="item.required"
-              v-model:value="item.value"
-              :style="item.suffix&&item.suffix!=''?'':'width: 100%;'"
-              :min="item.appenderParams?(item.appenderParams.min):undefined"
-              :max="item.appenderParams?(item.appenderParams.max):undefined"
-              :step="item.appenderParams?(item.appenderParams.step):undefined"
-            />
-            <span v-if="item.suffix&&item.suffix!=''" class="ml-2">{{item.suffix}}</span>
-          </a-form-item>
-          <a-form-item v-if="item.type=='checkbox'" :label="item.label" :labelCol="labelCol" :wrapperCol="wrapperCol" :name="item.name||item.key">
-            <a-checkbox v-model:checked="item.value">{{item.label}}</a-checkbox>
-          </a-form-item>
-          <a-form-item v-else-if="item.type=='text-array'" :label="item.label" :labelCol="labelCol" :wrapperCol="wrapperCol" :name="item.name||item.key">
-            <SimpleEditDynamicStringList
-              :array="(item.value as string[])"
-              placeholder="请输入参数值"
-            />
-          </a-form-item>
-          <a-form-item v-else-if="item.type=='text-select'" :label="item.label" :labelCol="labelCol" :wrapperCol="wrapperCol" :name="item.name||item.key">
-            <a-select
-              :required="item.required"
-              v-model:value="item.value"
-              style="width: 100%"
-              :options="item.appenderParams?.options"
-            >
-            </a-select>
-          </a-form-item>
-        </div>
-        <a-popconfirm
-          v-if="canDelete"
-          title="确定删除此参数吗?"
-          @confirm="$emit('delete', item, key)"
-        >
-          <a-button class="ml-3">
-            <template #icon><DeleteOutlined /></template>
-          </a-button>
-        </a-popconfirm>
-      </div>
-    </a-col>
-  </a-row>
-</template>
-
-<script lang="ts">
-import { defineComponent, type PropType } from 'vue'
-import {  DeleteOutlined } from '@ant-design/icons-vue'
-import SimpleEditDynamicStringList from './SimpleEditDynamicStringList.vue';
-
-export interface SimpleListDynamicFormItem {
-  label: string;
-  value: unknown;
-  name: string;
-  key: string;
-  required?: boolean,
-  type: 'text'|'number'|'checkbox'|'radio'|'switch'|'text-array'|'text-select';
-  placeholder ?: string;
-  prefix ?: string;
-  suffix ?: string;
-  appenderParams ?: {
-    [index: string]: unknown
-  };
-}
-
-/**
- * 简单的列表动态表单
- */
-export default defineComponent({
-  name: 'SimpleListDynamicForm',
-  components: {
-    SimpleEditDynamicStringList,
-    DeleteOutlined,
-  },
-  props: {
-    /**
-     * 表单条目
-     */
-    items: {
-      required: true,
-      type: Object as PropType<SimpleListDynamicFormItem[]>
-    },
-    /**
-     * 是否可以删除表单条目
-     */
-    canDelete: {
-      default: true,
-      type: Boolean
-    },
-    span: {
-      default: 7,
-      type: Number
-    },
-    gutter: {
-      default: 30,
-    },
-    labelCol: {
-      default: () => { return { span: 10 } },
-      type: Object
-    },
-    wrapperCol: {
-      default: () => { return { span: 14 } },
-      type: Object
-    },
-    /**
-     * 是否显示删除按钮
-     */
-    showDelete: {
-      default: false,
-      type: Boolean
-    },
-  },
-})
-</script>

+ 0 - 42
src/components/dynamicf/SimpleSelectFormItem.ts

@@ -1,42 +0,0 @@
-import type { SelectProps } from "ant-design-vue";
-import type { VNode } from 'vue';
-
-export interface IDynamicFormItemSelectOption {
-  text: string,
-  value: string|number,
-  badgeState?: 'success'|'error'|'default'|'processing'|'warning',
-  badgeColor?: string,
-  raw?: unknown;
-}
-
-export interface IDynamicFormItemAdditionalOptions {
-  options: IDynamicFormItemSelectOption[],
-}
-
-export interface SimpleSelectFormItemProps {
-  /**
-   * 是否禁用
-   */
-  disabled?: boolean;
-  /**
-   * 选项数据
-   */
-  options: IDynamicFormItemSelectOption[];
-  /**
-   * 选择值
-   */
-  value?: unknown;
-  /**
-   * a-select 其他自定义参数
-   */
-  customProps?: SelectProps;
-  /**
-   * 是否自定义渲染option插槽
-   */
-  renderOption?: RenderOption;
-}
-
-export type RenderOption = (data: {
-  value: unknown,
-  label: string,
-}) => VNode;

+ 0 - 69
src/components/dynamicf/SimpleSelectFormItem.vue

@@ -1,69 +0,0 @@
-<template>
-  <a-select
-    :value="value"
-    @update:value="onUpdateValue"
-    :disabled="disabled"
-    v-bind="customProps"
-  >
-    <template v-if="renderOption" #option="data">
-      <VNodeRenderer :render="(renderOption as any)" :data="data" />
-    </template>
-    <a-select-option v-for="it in options" :key="it.value" v-bind="it">
-      <a-badge v-if="it.badgeColor" :color="it.badgeColor" :text="it.text" />
-      <a-badge v-else-if="it.badgeState" :status="it.badgeState" :text="it.text" />
-      <span v-else>{{it.text}}</span>
-    </a-select-option>
-  </a-select>
-</template>
-
-<script lang="ts" setup>
-/**
- * 简单下拉框表单控件
- */
-import VNodeRenderer from '@/components/VNodeRenderer.vue';
-import type { SelectProps } from 'ant-design-vue/lib/vc-select';
-import { type PropType } from 'vue';
-import type { IDynamicFormItemSelectOption, RenderOption } from './SimpleSelectFormItem';
-
-defineProps({
-  /**
-   * 是否禁用
-   */
-  disabled: {
-    type: Boolean,
-    default: false
-  },
-  /**
-   * 选项数据
-   */
-  options: {
-    type: Object as PropType<IDynamicFormItemSelectOption[]>,
-    default: null,
-  },
-  /**
-   * 选择值
-   */
-  value: {
-  },
-  /**
-   * a-select 其他自定义参数
-   */
-  customProps: {
-    type: Object as PropType<SelectProps>,
-    default: null,
-  },
-  renderOption: {
-    default: null,
-    type: Function as PropType<RenderOption>
-  },
-});
-
-const emits = defineEmits([
-  'update:value',
-]);
-
-function onUpdateValue(v : unknown) {
-  emits('update:value', v);
-}
-
-</script>

+ 0 - 156
src/components/dynamicf/UploadImageFormItem.ts

@@ -1,156 +0,0 @@
-import { StringUtils } from "@imengyu/imengyu-utils";
-import { message, type UploadProps } from "ant-design-vue";
-
-export interface UploadImageFormItemProps {
-  /**
-   * 是否禁用
-   */
-  disabled?: boolean;
-  /**
-   * 上传工厂类
-   */
-  uploadCo: UploadCoInterface;
-  /**
-   * 上传之前的自定义检查回调
-   * 如果返回false,将停止上传
-   */
-  beforeUpload?: (file: FileItem) => boolean;
-  /**
-   * 是否限制单图上传
-   */
-  single?: boolean;
-  /**
-   * single 为false时,限制最多上传图片的数量
-   */
-  maxCount?: number
-  /**
-   * 类样式
-   */
-   uploadClass?: unknown;
-  /**
-   * single 模式下图片显示大小
-   */
-  singleImageSize?: { width: number, height: number },
-  /**
-   * 参数,可以是单张 string,多张 string[]
-   */
-  value?: unknown;
-  /**
-   * a-upload 其他自定义参数
-   */
-  customProps?: UploadProps;
-}
-
-/**
- * 上传工厂接口
- */
-export interface UploadCoInterface {
-  /**
-    * 上传主函数。由 ant-upload 调用。
-    */
-  uploadRequest: (requestOption: AntUploadRequestOption) => void,
-  /**
-    * 获取上传返回后的URL函数。
-    */
-  getUrlByUploadResponse: (response: unknown) => string,
-}
-
-export interface FileItem {
-  uid: string;
-  name?: string;
-  status?: string;
-  response?: string;
-  url: string;
-  type: string;
-  size: number;
-  originFileObj?: unknown;
-}
-
-export interface FileInfo {
-  file: FileItem;
-  fileList: FileItem[];
-}
-
-export interface AntUploadRequestOption {
-  action: string|Promise<string>;
-  filename: string;
-  data : unknown;
-  file: File;
-  headers: { [index: string]: string; };
-  withCredentials: boolean;
-  method: string;
-  onProgress: (e: { percent: number }) => void;
-  onSuccess: (ret : { url: string, key: string }, xhr : XMLHttpRequest|null) => void;
-  onError: (err : Error|null|undefined, ret : unknown) => void;
-}
-
-/**
- * 上传图片大小检查组合代码。
- * @param limitSizeMB 限制大小MB.
- * @returns 
- */
-export function useBeforeUploadImageChecker(limitSizeMB = 32) : (file: FileItem) => boolean {
-  return (file) => {
-    const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
-    if (!isJpgOrPng) 
-      message.error('请选择图片文件!');
-    const isLt2M = file.size / 1024 / 1024 < limitSizeMB;
-    if (!isLt2M) 
-      message.error(`图片大小不能大于${limitSizeMB}MB!`);
-    return isJpgOrPng && isLt2M;
-  };
-}
-
-/**
- * 上传视频大小检查组合代码。
- * @param limitSizeMB 限制大小MB.
- * @returns 
- */
-export function useBeforeUploadAudioChecker(limitSizeMB = 256) : (file: FileItem) => boolean {
-  return (file) => {
-    const isVideo = file.type.startsWith('audio/');
-    if (!isVideo) 
-      message.error('请选择音频文件!');
-    const isLt2M = file.size / 1024 / 1024 < limitSizeMB;
-    if (!isLt2M) 
-      message.error(`音频大小不能大于${limitSizeMB}MB!`);
-    return isVideo && isLt2M;
-  };
-}
-
-
-/**
- * 上传视频大小检查组合代码。
- * @param limitSizeMB 限制大小MB.
- * @returns 
- */
-export function useBeforeUploadVideoChecker(limitSizeMB = 4096) : (file: FileItem) => boolean {
-  return (file) => {
-    const isVideo = file.type.startsWith('video/');
-    if (!isVideo) 
-      message.error('请选择视频文件!');
-    const isLt2M = file.size / 1024 / 1024 < limitSizeMB;
-    if (!isLt2M) 
-      message.error(`视频大小不能大于${limitSizeMB}MB!`);
-    return isVideo && isLt2M;
-  };
-}
-
-/**
- * 把字符串URL数组转为a-upload已上传的条目
- * @param arr URL数组
- */
-export function stringUrlsToUploadedItems(arr: string[]) : FileItem[] {
-  return arr.map((i, k) => {
-    if (!i)
-      return undefined;
-    return {
-      uid: k.toString(),
-      name: i ? decodeURIComponent(StringUtils.path.getFileName(i)) : '',
-      status: 'done',
-      url: i,
-      size: 0,
-      type: '',
-    }
-  }).filter((i) => i !== undefined);
-}

+ 0 - 218
src/components/dynamicf/UploadImageFormItem.vue

@@ -1,218 +0,0 @@
-<template>
-  <a-upload 
-    v-bind="customProps"
-    :disabled="disabled"
-    v-model:file-list="uploadSubImgList"
-    list-type="picture-card"
-    :class="uploadClass"
-    :max-count="maxCount"
-    :show-upload-list="!single"
-    :customRequest="handleUpload"
-    :beforeUpload="handleBeforeUpload"
-    @reject="handleUploadSubImgReject"
-    @change="handleUploadSubImgChange"
-  >
-    <template v-if="single">
-      
-      <div v-if="Boolean(value)" class="ant-upload-image">
-        <span v-if="!disabled">点击替换图片</span>
-        <a-image
-          :src="(value as string)"
-          alt="avatar"
-          :width="singleImageSize.width"
-          :height="singleImageSize.height"
-          :preview="false"
-          :fallback="failImage" 
-        />
-      </div>
-      <div v-else :style="{ width: singleImageSize.width, height: singleImageSize.height }">
-        <loading-outlined v-if="uploadingSubImg"></loading-outlined>
-        <plus-outlined v-else></plus-outlined>
-        <div class="ant-upload-text">上传</div>
-      </div>
-    </template>
-    <template v-else>
-      <loading-outlined v-if="uploadingSubImg"></loading-outlined>
-      <plus-outlined v-else></plus-outlined>
-      <div class="ant-upload-text">上传</div>
-    </template>
-  </a-upload>
-</template>
-
-<script lang="ts" setup>
-/**
- * 上传图片表单控件
- */
-import { 
-  stringUrlsToUploadedItems, type UploadCoInterface, 
-  type AntUploadRequestOption, type FileInfo, type FileItem 
-} from './UploadImageFormItem';
-import { message, type UploadProps } from 'ant-design-vue';
-import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
-import { type PropType, ref, onMounted, watch } from 'vue';
-import FailImage from '@/assets/images/imageFailed.png';
-
-const props = defineProps({
-  /**
-   * 是否禁用
-   */
-  disabled: {
-    type: Boolean,
-    default: false
-  },
-  /**
-   * 预览图加载失败时显示图片
-   */
-  failImage: {
-    type: String,
-    default: () => FailImage,
-  },
-  /**
-   * 上传工厂类
-   */
-  uploadCo: {
-    type: Object as PropType<UploadCoInterface>,
-    default: null,
-  },
-  /**
-   * 上传之前的自定义检查回调
-   * (file: FileItem) => boolean
-   * 如果返回false,将停止上传
-   */
-  beforeUpload: {
-    type: Function,
-    default: null,
-  },
-  /**
-   * 类样式
-   */
-  uploadClass: {},
-  /**
-   * 是否限制单图上传
-   */
-  single: {
-    type: Boolean,
-    default: false
-  },
-  /**
-   * single 为false时,限制最多上传图片的数量
-   */
-  maxCount: {
-    type: Number,
-    default: 0,
-  },
-  /**
-   * single 模式下图片显示大小
-   */
-  singleImageSize: {
-    type: Object as PropType<{ width: number, height: number }>,
-    default: () => ({ width: 100, height: 100 })
-  },
-  /**
-   * 参数,可以是单张 string,多张 string[]
-   */
-  value: {},
-  /**
-   * a-upload 其他自定义参数
-   */
-  customProps: {
-    type: Object as PropType<UploadProps>,
-    default: null,
-  },
-});
-
-const emits = defineEmits([
-  'update:value',
-]);
-
-const uploadSubImgList = ref<FileItem[]>([]);
-const uploadingSubImg = ref(false);
-
-onMounted(() => {
-  //将之前上传的图片包括URL设置到已上传列表中
-  if (!props.single) {
-    setTimeout(() => {
-      uploadSubImgList.value = stringUrlsToUploadedItems(props.value instanceof Array ? (props.value as string[] || []) : [])
-    }, 400);
-  }
-});
-
-const needRemoveItem : string[] = [];
-
-watch(() => props.value, () => {
-  if (!props.single) {
-    uploadSubImgList.value = stringUrlsToUploadedItems(props.value instanceof Array ? (props.value as string[] || []) : [])
-  }
-});
-watch(uploadSubImgList, (e) => {
-  setTimeout(() => {
-    if (needRemoveItem.length > 0) {
-      //移除不允许上传的项目
-      for (let i = 0; i < e.length; i++) {
-        if (needRemoveItem.includes(e[i].uid)) 
-          uploadSubImgList.value.splice(i, 1);
-      }
-      needRemoveItem.length = 0;
-    }
-  }, 300)
-});
-
-function handleBeforeUpload(file: FileItem) {
-  const result = props.beforeUpload?.(file) ?? true;
-  if (!result)
-    needRemoveItem.push(file.uid);
-  return result;
-}
-async function handleUpload(requestOption: AntUploadRequestOption) {
-  props.uploadCo?.uploadRequest(requestOption);
-}
-function handleUploadSubImgReject(e: FileInfo) {
-  console.log(e);
-}
-function handleUploadSubImgChange(info: FileInfo) {
-  if (info.file.status === 'uploading') {
-    uploadingSubImg.value = true;
-    return;
-  }
-  if (info.file.status === 'removed') {
-    if (props.single)
-      emits('update:value', '');
-    else
-      emits('update:value', (props.value as string[] || []).filter(url => url != info.file.url));
-    return;
-  }
-  if (info.file.status === 'done') {
-    const url = props.uploadCo?.getUrlByUploadResponse(info.file.response) || '';
-    if (props.single)
-      emits('update:value', url);
-    else
-      emits('update:value', (props.value as string[] || []).concat([ url ]));
-    uploadingSubImg.value = false;
-  }
-  if (info.file.status === 'error') {
-    uploadingSubImg.value = false;
-    message.error('上传失败!' + info.file.response);
-  }
-}
-</script>
-
-<style lang="scss">
-.ant-upload-image {
-  position: relative;
-  border: 1px solid #dddddd;
-  overflow: hidden;
-  border-radius: 8px;
-  text-align: center;
-
-  span {
-    position: absolute;
-    margin: 10px;
-    background-color: #e8e8e8;
-    border-radius: 8px;
-    padding: 5px 10px;
-    font-size: 12px;
-    z-index: 1;
-  }
-}
-
-</style>

+ 0 - 194
src/components/dynamicf/UploadVideoFormItem.vue

@@ -1,194 +0,0 @@
-<template>
-  <a-upload 
-    v-bind="customProps"
-    :disabled="disabled"
-    v-model:file-list="uploadSubImgList"
-    list-type="picture"
-    :class="uploadClass"
-    :max-count="single ? 1 : maxCount"
-    :customRequest="handleUpload"
-    :beforeUpload="handleBeforeUpload"
-    @change="handleUploadSubImgChange"
-    @reject="handleUploadSubImgReject"
-  >
-    <a-button v-if="!disabled">
-      <upload-outlined></upload-outlined>
-      上传
-    </a-button>
-    <span v-if="disabled && uploadSubImgList.length == 0">暂无</span>
-  </a-upload>
-</template>
-
-<script lang="ts" setup>
-/**
- * 上传图片表单控件
- */
-import { 
-  stringUrlsToUploadedItems, type UploadCoInterface, 
-  type AntUploadRequestOption, type FileInfo, type FileItem 
-} from './UploadImageFormItem';
-import { message, type UploadProps } from 'ant-design-vue';
-import { UploadOutlined } from '@ant-design/icons-vue';
-import { type PropType, ref, onMounted, watch } from 'vue';
-import FailImage from '@/assets/images/imageFailed.png';
-
-const props = defineProps({
-  /**
-   * 是否禁用
-   */
-  disabled: {
-    type: Boolean,
-    default: false
-  },
-  /**
-   * 预览图加载失败时显示图片
-   */
-  failImage: {
-    type: String,
-    default: () => FailImage,
-  },
-  /**
-   * 上传工厂类
-   */
-  uploadCo: {
-    type: Object as PropType<UploadCoInterface>,
-    default: null,
-  },
-  /**
-   * 上传之前的自定义检查回调
-   * (file: FileItem) => boolean
-   * 如果返回false,将停止上传
-   */
-  beforeUpload: {
-    type: Function,
-    default: null,
-  },
-  /**
-   * 类样式
-   */
-  uploadClass: {},
-  /**
-   * 是否限制单图上传
-   */
-  single: {
-    type: Boolean,
-    default: false
-  },
-  /**
-   * single 为false时,限制最多上传图片的数量
-   */
-  maxCount: {
-    type: Number,
-    default: 0,
-  },
-  /**
-   * single 模式下图片显示大小
-   */
-  singleImageSize: {
-    type: Object as PropType<{ width: number, height: number }>,
-    default: () => ({ width: 250, height: 150 })
-  },
-  /**
-   * 参数,可以是单张 string,多张 string[]
-   */
-  value: {},
-  /**
-   * a-upload 其他自定义参数
-   */
-  customProps: {
-    type: Object as PropType<UploadProps>,
-    default: null,
-  },
-});
-
-const emits = defineEmits([
-  'update:value',
-]);
-
-const uploadSubImgList = ref<FileItem[]>([]);
-const uploadingSubImg = ref(false);
-
-function loadValue() {
-uploadSubImgList.value = stringUrlsToUploadedItems(
-  props.value instanceof Array ? (props.value as string[] || []) : [
-    props.value as string
-  ])
-}
-
-onMounted(() => {
-  //将之前上传的图片包括URL设置到已上传列表中
-  setTimeout(loadValue, 400);
-});
-
-const needRemoveItem : string[] = [];
-
-watch(() => props.value, () => {
-  loadValue();
-});
-watch(uploadSubImgList, (e) => {
-  setTimeout(() => {
-    if (needRemoveItem.length > 0) {
-      //移除不允许上传的项目
-      for (let i = 0; i < e.length; i++) {
-        if (needRemoveItem.includes(e[i].uid)) 
-          uploadSubImgList.value.splice(i, 1);
-      }
-      needRemoveItem.length = 0;
-    }
-  }, 300)
-});
-
-function handleBeforeUpload(file: FileItem) {
-  const result = props.beforeUpload?.(file) ?? true;
-  if (!result)
-    needRemoveItem.push(file.uid);
-  return result;
-}
-function handleUpload(requestOption: AntUploadRequestOption) {
-  props.uploadCo?.uploadRequest(requestOption);
-}
-function handleUploadSubImgReject(e: FileInfo) {
-  console.log(e);
-  message.error('上传失败!' + e.file.response);
-}
-function handleUploadSubImgChange(info: FileInfo) {
-  if (info.file.status === 'uploading') {
-    uploadingSubImg.value = true;
-    return;
-  }
-  if (info.file.status === 'removed') {
-    if (props.single)
-      emits('update:value', '');
-    else
-      emits('update:value', (props.value as string[] || []).filter(url => url != info.file.url));
-    return;
-  }
-  if (info.file.status === 'done') {
-    const url = props.uploadCo?.getUrlByUploadResponse(info.file.response) || '';
-    if (props.single)
-      emits('update:value', url);
-    else
-      emits('update:value', (props.value as string[] || []).concat([ url ]));
-    uploadingSubImg.value = false;
-  }
-  if (info.file.status === 'error') {
-    uploadingSubImg.value = false;
-    message.error('上传失败!' + info.file.response);
-  }
-}
-</script>
-
-<style lang="scss">
-.ant-upload-video {
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-  align-items: center;
-  padding: 10px;
-  border: 1px solid #666666;
-  background-color: #e8e8e8;
-  border-radius: 8px;
-  text-align: center;
-}
-
-</style>

+ 0 - 6
src/components/dynamicf/WhiteSpace.ts

@@ -1,6 +0,0 @@
-/**
- * WhiteSpace 的公共接口
- */
-export interface WhiteSpaceProps {
-  height: number,
-}

+ 0 - 16
src/components/dynamicf/WhiteSpace.vue

@@ -1,16 +0,0 @@
-<template>
-  <div :style="{ height: height }"></div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from "vue";
-
-export default defineComponent({
-  props: {
-    height: {
-      type: [Number,String],
-      default: 0,
-    },
-  }
-});
-</script>

+ 0 - 28
src/components/dynamicf/WrapperRangePicker.vue

@@ -1,28 +0,0 @@
-<template>
-  <a-range-picker
-    :value="value"
-    @update:value="(v: unknown) => $emit('update:value', v)"
-    :showTime="showTime"
-    v-bind="additionalProps"
-  />
-</template>
-
-<script lang="ts">
-import { defineComponent, type PropType } from "vue";
-
-export default defineComponent({
-  props: {
-    additionalProps: {
-      type: Object as PropType<Record<string, unknown>>,
-    },
-    showTime: {
-      type: Boolean,
-    },
-    value: {
-    },
-  },
-  emist: [
-    'update:value'
-  ],
-});
-</script>

+ 0 - 24
src/components/dynamicf/WrapperTimeRangePicker.vue

@@ -1,24 +0,0 @@
-<template>
-  <a-time-range-picker
-    :value="value"
-    @update:value="(v: unknown) => $emit('update:value', v)"
-    v-bind="additionalProps"
-  />
-</template>
-
-<script lang="ts">
-import { defineComponent, type PropType } from "vue";
-
-export default defineComponent({
-  props: {
-    additionalProps: {
-      type: Object as PropType<Record<string, unknown>>,
-    },
-    value: {
-    },
-  },
-  emist: [
-    'update:value'
-  ],
-});
-</script>

+ 0 - 103
src/components/dynamicf/index.ts

@@ -1,103 +0,0 @@
-import { markRaw } from "vue";
-import { 
-  Alert, Checkbox, DatePicker, Form, 
-  FormItem, Image, Input, InputNumber, 
-  Rate, Switch, Textarea, TimePicker
-} from "ant-design-vue";
-import { DynamicFormItemRegistry, type IDynamicFormOptions, configDefaultDynamicFormOptions } from "@imengyu/vue-dynamic-form";
-import PasswordWithStrengthInput from "./PasswordWithStrengthInput.vue";
-import CheckBoxValue from "./CheckBoxValue.vue";
-import SimpleSelectFormItem from "./SimpleSelectFormItem.vue";
-import IdAsValueDropdown from "./Dropdown/IdAsValueDropdown.vue";
-import IdAsValueTreeDropdown from "./Dropdown/IdAsValueTreeDropdown.vue";
-import SelectValue from "./SelectValue.vue";
-import IdAsValueTree from "./IdAsValueTree.vue";
-import UploadImageFormItem from "./UploadImageFormItem.vue";
-import WrapperTimeRangePicker from "./WrapperTimeRangePicker.vue";
-import WrapperRangePicker from "./WrapperRangePicker.vue";
-import ActionRender from "./ActionRender.vue";
-import CheckBoxToInt from "./CheckBoxToInt.vue";
-import RadioValueVue from "./RadioValue.vue";
-import StateRendererVue from "./Display/StateRenderer.vue";
-import ShowDateOrNullVue from "./Display/ShowDateOrNull.vue";
-import ShowImageListVue from "./Display/ShowImageList.vue";
-import ShowValueOrNullVue from "./Display/ShowValueOrNull.vue";
-import CascaderFormItemVue from "./CascaderFormItem.vue";
-import SimpleEditDynamicStringListVue from "./SimpleEditDynamicStringList.vue";
-import WhiteSpaceVue from "./WhiteSpace.vue";
-import NumberRange from "./NumberRange.vue";
-import MapPointPicker from "./Map/MapPointPicker.vue";
-import EditorWrapper from "./Editor/EditorWrapper.vue";
-import UploadVideoFormItem from "./UploadVideoFormItem.vue";
-import AddressSercher from "./Map/AddressSercher.vue";
-import Sign from "./Sign.vue";
-
-export const defaultConfig = {
-  internalWidgets: {
-    Form: {
-      component: markRaw(Form),
-      propsMap: {
-        rules: 'rules',
-        wrapperCol: 'wrapperCol',
-        labelCol: 'labelCol',
-      },
-    },
-    FormItem: {
-      component: markRaw(FormItem),
-      propsMap: {
-        name: 'name',
-        wrapperCol: 'wrapperCol',
-        labelCol: 'labelCol',
-      },
-    },
-  },
-} as IDynamicFormOptions
-
-export function registerAllFormComponents() {
-  configDefaultDynamicFormOptions(defaultConfig);
-
-  DynamicFormItemRegistry
-
-    .register('text', markRaw(Input))
-    .register('password', markRaw(Input), { type: "password" })
-    .register('number', markRaw(InputNumber))
-    .register('text-area', markRaw(Textarea))
-    .register('password-with-strength', markRaw(PasswordWithStrengthInput))
-    .register('switch', markRaw(Switch), {}, 'checked')
-    .register('cascader', markRaw(CascaderFormItemVue))
-    .register('check-box', markRaw(Checkbox), {}, 'checked')
-    .register('check-box-int', markRaw(CheckBoxToInt))
-    .register('check-box-value', markRaw(CheckBoxValue))
-    .register('radio-value', markRaw(RadioValueVue))
-    .register('number-range', markRaw(NumberRange))
-    .register('rate', markRaw(Rate))
-    .register('select', markRaw(SimpleSelectFormItem))
-    .register('select-value', markRaw(SelectValue))
-    .register('select-id', markRaw(IdAsValueDropdown))
-    .register('select-tree-id', markRaw(IdAsValueTreeDropdown))
-    .register('tree-id', markRaw(IdAsValueTree))
-    .register('date', markRaw(DatePicker))
-    .register('time', markRaw(TimePicker))
-    .register('date-time', markRaw(DatePicker), { showTime: true })
-    .register('date-range', markRaw(WrapperRangePicker))
-    .register('time-range', markRaw(WrapperTimeRangePicker))
-    .register('date-time-range', markRaw(WrapperRangePicker), { showTime: true })
-    .register('single-image', markRaw(UploadImageFormItem), { single: true })
-    .register('mulit-image', markRaw(UploadImageFormItem))
-    .register('single-video', markRaw(UploadVideoFormItem), { single: true })
-    .register('mulit-video', markRaw(UploadVideoFormItem), { single: false })
-    .register('actions', markRaw(ActionRender))
-    .register('alert', markRaw(Alert))
-    .register('string-list', markRaw(SimpleEditDynamicStringListVue))
-    .register('static-image', markRaw(Image), {}, "src")
-    .register('static-state', markRaw(StateRendererVue))
-    .register('static-value', markRaw(ShowValueOrNullVue))
-    .register('static-date', markRaw(ShowDateOrNullVue))
-    .register('static-image-list', markRaw(ShowImageListVue), {}, "images")
-    .register('space', markRaw(WhiteSpaceVue))
-    .register('map-pick-point', markRaw(MapPointPicker), {}, 'modelValue')
-    .register('richtext', markRaw(EditorWrapper), {}, 'modelValue')
-    .register('address-sercher', markRaw(AddressSercher), {}, 'modelValue')
-    .register('sign', markRaw(Sign), {}, 'modelValue')
-
-}

+ 0 - 98
src/components/error/ErrorReporter.vue

@@ -1,98 +0,0 @@
-<template>
-  <div class="nana-error-report-container">
-    <Transition name="nana-scale-transform">
-      <div v-if="messageItems.length > 0" class="nana-error-report" @click="showError">
-        <img :src="ErrorIcon" />
-        <span>{{ errorCount }}</span>
-        <img :src="WarningIcon" /> 
-        <span>{{ warningCount }}</span>
-      </div>
-    </Transition>
-  </div>
-  <a-modal v-model:open="showModal" title="错误报告" width="60%" height="80%">
-    <a-alert
-      v-for="(item, index) in messageItems"
-      :key="index"
-      :message="item.message"
-      :type="item.type"
-      :description="item.detail"
-    />
-    <template #footer>
-      <a-button @click="messageItems = [];showModal = false">清空</a-button>
-      <a-button @click="showModal = false">关闭</a-button>
-    </template>
-  </a-modal>
-</template>
-
-<script setup lang="ts">
-import { onMounted, provide, ref } from 'vue';
-import WarningIcon from '../AlertIcons/warning.svg';
-import ErrorIcon from '../AlertIcons/error.svg';
-
-export interface ErrorReportItem {
-  time?: number,
-  message: string,
-  detail?: string,
-  type: 'error' | 'warning' | 'info',
-}
-export interface ErrorReportRef {
-  logError(info: ErrorReportItem): void;
-}
-
-const messageItems = ref<ErrorReportItem[]>([]);
-const errorCount = ref(0);
-const warningCount = ref(0);
-const showModal = ref(false);
-
-function logError(info: ErrorReportItem) {
-  messageItems.value.push(info);
-  if (info.type === 'error') 
-    errorCount.value++;
-  if (info.type === 'warning') 
-    warningCount.value++;
-}
-function showError() {
-  showModal.value = true;
-}
-
-provide<ErrorReportRef>("ErrorReporter", {
-  logError,
-});
-
-onMounted(() => {
-  (window as any).$error = logError;
-})
-</script>
-
-<style lang="scss">
-.nana-error-report-container {
-  position: fixed;
-  top: 2rem;
-  left: 0;
-  right: 0;
-  display: flex;
-  flex-direction: row;
-  justify-content: center;
-  pointer-events: none;
-  z-index: 100;
-
-  img {
-    width: 1rem;
-    height: 1rem;
-  }
-  span {
-    color: #333;
-  }
-}
-.nana-error-report {
-  display: flex;
-  flex-direction: row;
-  align-items: center; 
-  width: auto;
-  gap: 0.5rem;
-  background-color: #fff;
-  padding: 0.6rem;
-  border-radius: 0.5rem;
-  pointer-events: all;
-}
-</style>

+ 0 - 28
src/components/error/ErrorReporterIs.ts

@@ -1,28 +0,0 @@
-import { inject } from "vue";
-import type { ErrorReportItem, ErrorReportRef } from "./ErrorReporter.vue";
-import { RequestApiError } from "@imengyu/imengyu-utils/dist/request";
-
-export function useErrorReporter() {
-  const r = inject<ErrorReportRef>("ErrorReporter");
-  if (!r)
-    throw new Error("ErrorReporter is not provided"); 
-  return r;
-}
-
-export function formatError(err: any) {
-  if (err instanceof RequestApiError) 
-    return err.toStringDetail();
-  if (err instanceof Error) 
-    return err.message + '\n' + err.stack;
-  return '' + err;
-}
-export function logError(info: ErrorReportItem) {
-  (window as any).$error?.(info);
-}
-export function logErrorSimple(err: any, message: string) { 
-  logError({
-    type: 'error',
-    detail: formatError(err),
-    message,
-  });
-}

+ 2 - 3
src/components/parts/EmptyToRecord.vue

@@ -1,7 +1,6 @@
 <script setup lang="ts">
 import type { PropType } from 'vue';
-import SimplePageContentLoader from '../content/SimplePageContentLoader.vue';
-import type { ISimpleDataLoader } from '@/composeable/SimpleDataLoader';
+import { type ISimpleDataLoader, SimplePageContentLoader } from '@imengyu/imengyu-web-shared';
 
 const emit = defineEmits([ 'edit' ]);
 defineProps({
@@ -35,7 +34,7 @@ defineProps({
 <template>
   <SimplePageContentLoader :loader="loader">
     <a-result
-      v-if="!loader.content.value"
+      v-if="!loader?.content.value"
       status="404"
       :title="`${title}信息`"
       :subTitle="emptyText || `暂无${title}信息,快去补充`"

+ 1 - 1
src/components/parts/TitleDescBlock.vue

@@ -22,7 +22,7 @@
 
 <script setup lang="ts">
 import { ref } from 'vue';
-import SimpleRichHtml from '../display/SimpleRichHtml.vue';
+import { SimpleRichHtml } from '@imengyu/imengyu-web-shared';
 
 const props = defineProps({	
   title : {

+ 0 - 9
src/composeable/LoaderCommon.ts

@@ -1,9 +0,0 @@
-import type { Ref } from "vue";
-
-export type LoaderLoadType = 'loading' | 'finished' | 'nomore' | 'error';
-
-export interface ILoaderCommon<P> {
-  loadError: Ref<string>;
-  loadStatus: Ref<LoaderLoadType>;
-  loadData: (params?: P, refresh?: boolean) => Promise<void>;
-}

+ 0 - 18
src/composeable/MemorizeVar.ts

@@ -1,18 +0,0 @@
-import { onMounted, ref, watch } from "vue";
-
-export function memorizeVar<T>(key: string, defaultValue: T) {
-  const variable = ref<T>(defaultValue);
-
-  onMounted(() => {
-    const v = localStorage.getItem(key);
-    if (v)
-      variable.value = JSON.parse(v);
-  });
-  watch(variable, (newValue) => {
-    localStorage.setItem(key, JSON.stringify(newValue));
-  });
-
-  return { 
-    variable
-  };
-}

+ 0 - 65
src/composeable/SimpleDataLoader.ts

@@ -1,65 +0,0 @@
-import { onMounted, ref, type Ref } from "vue";
-import type { ILoaderCommon, LoaderLoadType } from "./LoaderCommon";
-
-export interface ISimpleDataLoader<T, P> extends ILoaderCommon<P> {
-  content: Ref<T|null>;
-  currentPromise: Ref<Promise<any>|null>;
-  getLastParams: () => P | undefined;
-}
-
-export function useSimpleDataLoader<T, P = any>(
-  loader: (params?: P) => Promise<T>,
-  loadWhenMounted = true,
-  emptyIfArrayEmpty = true,
-)  : ISimpleDataLoader<T, P>
- {
-
-  const content = ref<T|null>(null) as Ref<T|null>;
-  const loadStatus = ref<LoaderLoadType>('loading');
-  const loadError = ref('');
-  const currentPromise = ref<Promise<any>|null>(null);
-
-  let lastParams: P | undefined;
-
-  async function _loadData(params?: P) {
-    if (params)
-      lastParams = params;
-    loadStatus.value = 'loading';
-    try {
-      const res = (await loader(params ?? lastParams)) as T;
-      content.value = res;
-      if (Array.isArray(res) && emptyIfArrayEmpty && (res as any[]).length === 0)
-        loadStatus.value = 'nomore';
-      else
-        loadStatus.value = 'finished';
-      loadError.value = '';
-    } catch(e) {
-      loadError.value = '' + e;
-      loadStatus.value = 'error';
-      console.log(e);
-      
-    }
-  }
-
-  function loadData(params?: P) {
-    currentPromise.value = _loadData(params);
-    return currentPromise.value;
-  }
-
-  onMounted(() => {
-    if (loadWhenMounted) {
-      setTimeout(() => {
-        loadData();
-      }, (0.5 + Math.random()) * 500);
-    }
-  })
-
-  return {
-    content,
-    loadStatus,
-    loadError,
-    currentPromise,
-    loadData,
-    getLastParams: () => lastParams,
-  }
-}

+ 0 - 128
src/composeable/SimplePagerDataLoader.ts

@@ -1,128 +0,0 @@
-import { watch, ref, computed, type Ref } from "vue"
-import type { ILoaderCommon, LoaderLoadType } from "./LoaderCommon";
-
-export interface ISimplePageListLoader<T, P> extends ILoaderCommon<P> {
-  list: Ref<T[]>;
-  page: Ref<number>;
-  next: () => Promise<void>;
-  prev: () => Promise<void>;
-  total: Ref<number>;
-  totalPages: Ref<number>;
-}
-
-/**
- * 简单分页数据封装。
- * 
- * 该封装了分页数据的加载、分页、上一页、下一页等功能。当页码发生变化时,会自动调用加载函数。
- * 简单分页同时只能显示一页数据,重新加载会覆盖之前的数据。
- * 
- * 使用示例:
- * ```ts
- * const { data, page, total, loading } = useSimplePagerDataLoader(10, async (page, pageSize) => {
- *   const res = await fetch(`/api/data?page=${page}&pageSize=${pageSize}`);
- *   const data = await res.json();  
- *   return {
- *     data,
- *     page: res.page,
- *     total: res.total,
- *   };
- * });
- * ```
- *
- * @param pageSize 一页的数量
- * @param loader 加载函数
- * @returns 
- */
-export function useSimplePagerDataLoader<T, P = any>(
-  pageSize: number|Ref<number>, 
-  loader: (page: number, pageSize: number, params?: P) => Promise<{
-    data: T[],
-    total: number,
-  }>)  : ISimplePageListLoader<T, P>
-{
-  const page = ref(0);
-  const list = ref<T[]>([]) as Ref<T[]>;
-  const total = ref(0);
-  const totalPages = computed(() => Math.ceil(total.value / getPageSize()));
-  const loadStatus = ref<LoaderLoadType>('loading');
-  const loadError = ref('');
-
-  function getPageSize() {
-    return typeof pageSize == 'object'? pageSize.value : pageSize;
-  }
-  
-  watch(page, async () => {
-    await loadData(lastParams, false);
-  });
-
-  let lastParams: P | undefined;
-  let loading = false;
-
-  async function loadData(params?: P, refresh: boolean = false) {
-    if (loading) 
-      return;
-    if (params)
-      lastParams = params;
-    if (refresh) {
-      page.value = 1;
-    }
-    list.value = []; 
-    loadStatus.value = 'loading';
-    loading = true;
-
-    try {
-      const res = (await loader(page.value, getPageSize(), lastParams));
-      list.value = list.value.concat(res.data);
-      total.value = res.total;
-      loadStatus.value = res.data.length > 0 ? 'finished' : 'nomore';
-      loadError.value = '';
-      loading = false;
-    } catch(e) {
-      loadError.value = '' + e;
-      loadStatus.value = 'error';
-      loading = false;
-    }
-  }
-  /**
-   * 下一页
-   */
-  async function next() {
-    if (page.value > total.value)
-      return;
-    page.value++;
-    await loadData(lastParams, false);
-  }
-  /**
-   * 上一页
-   */
-  async function prev() {
-    if (page.value <= 1)
-      return;   
-    page.value--;
-    await loadData(lastParams, false);
-  }
-
-  return {
-    loadData,
-    next,
-    prev,
-    /**
-     * 数据
-     */
-    list,
-    /**
-     * 当前页码
-     */
-    page,
-    /**
-     * 总数据条数
-     */
-    total,
-    /**
-     * 总页数
-     */
-    totalPages,
-    loadError,
-    loadStatus,
-  }
-}

+ 0 - 14
src/composeable/WindowOnUnLoad.ts

@@ -1,14 +0,0 @@
-import { onBeforeUnmount, onMounted } from "vue";
-
-export function useWindowOnUnLoadConfirm() {
-  function callback(e: BeforeUnloadEvent) {
-    e.preventDefault();
-    return '确定要离开吗?';
-  }
-  onMounted(() => {
-    window.addEventListener('beforeunload', callback);
-  })
-  onBeforeUnmount(() => {
-    window.removeEventListener('beforeunload', callback);
-  });
-}

+ 5 - 22
src/main.ts

@@ -1,9 +1,5 @@
 import 'vue3-carousel/carousel.css'
-import 'ant-design-vue/dist/reset.css';
-import '@vuemap/vue-amap/dist/style.css'
-import 'nprogress/nprogress.css';
-import '@imengyu/vue-dynamic-form/dist/style.css'
-import '@imengyu/vue-scroll-rect/lib/vue-scroll-rect.css'
+import '@imengyu/imengyu-web-shared/lib/imengyu-web-shared.css'
 import '@vueup/vue-quill/dist/vue-quill.snow.css';
 import 'tinymce/tinymce';
 import 'tinymce/themes/silver/theme';
@@ -14,38 +10,25 @@ import { createPinia } from 'pinia'
 
 import App from './App.vue'
 import router from './router'
-import Antd from 'ant-design-vue';
 import { registryConvert } from '@/common/ConvertRgeistry'
-import VueAMap, {initAMapApiLoader} from '@vuemap/vue-amap';
+import { initAMapApiLoader } from '@vuemap/vue-amap';
 import NProgress from 'nprogress';
-import ScrollRect from '@imengyu/vue-scroll-rect'
 import { QuillEditor } from '@vueup/vue-quill'
-import { registerAllFormComponents } from './components/dynamicf';
+import ImengyuCommon from '@imengyu/imengyu-web-shared'
 
 initAMapApiLoader({
   key: '212b86dc49a5116a34e945d31da7ad14',
   securityJsCode: '46cae8205a707cfaf5801abcc4301566',
   plugins: ['AMap.MarkerCluster'],
-})
-NProgress.configure({
-  easing: 'ease',
-  speed: 500,
-  showSpinner: false,
-  trickleSpeed: 200,
 });
 
 const app = createApp(App)
 
 app.use(createPinia())
 app.use(router)
-app.use(Antd)
-app.use(VueAMap)
+app.use(ImengyuCommon, {})
 app.component('QuillEditor', QuillEditor);
-app.use(ScrollRect)
-
-app.mount('#app').$nextTick(() => {
-  registerAllFormComponents();
-})
+app.mount('#app');
 
 router.beforeEach((to, from, next) => {
   NProgress.start();

+ 2 - 3
src/pages/admin.vue

@@ -130,7 +130,7 @@
 import { computed, h, ref, watch } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { useAuthStore } from '@/stores/auth';
-import { useSimpleDataLoader } from '@/composeable/SimpleDataLoader';
+import { useSimpleDataLoader, memorizeVar } from '@imengyu/imengyu-web-shared';
 import { message, Modal } from 'ant-design-vue';
 import type { GetContentListItem } from '@/api/CommonContent';
 import useClipboard from 'vue-clipboard3';
@@ -138,8 +138,7 @@ import CommonContent, { GetContentListParams } from '@/api/CommonContent';
 import CommonListBlock, { type DropdownCommonItem } from '@/components/content/CommonListBlock.vue';
 import InheritorContent from '@/api/inheritor/InheritorContent';
 import AdminItemState from './components/AdminItemState.vue';
-import { memorizeVar } from '@/composeable/MemorizeVar';
-import { InfoCircleOutlined, ExclamationCircleOutlined, CheckOutlined } from '@ant-design/icons-vue';
+import { InfoCircleOutlined } from '@ant-design/icons-vue';
 
 const { toClipboard } = useClipboard();
 const router = useRouter();

+ 1 - 1
src/pages/admin/seminar.vue

@@ -52,7 +52,7 @@
 
 <script setup lang="ts">
 import { useRoute, useRouter } from 'vue-router';
-import { useSimpleDataLoader } from '@/composeable/SimpleDataLoader';
+import { useSimpleDataLoader } from '@imengyu/imengyu-web-shared';
 import EmptyToRecord from '@/components/parts/EmptyToRecord.vue';
 import InheritorContent, { InheritorWorkInfo } from '@/api/inheritor/InheritorContent';
 import { ArrowLeftOutlined } from '@ant-design/icons-vue';

+ 2 - 2
src/pages/admin/works.vue

@@ -40,11 +40,11 @@
 
 <script setup lang="ts">
 import { useRoute, useRouter } from 'vue-router';
-import { useSimpleDataLoader } from '@/composeable/SimpleDataLoader';
+import { useSimpleDataLoader } from '@imengyu/imengyu-web-shared';
 import EmptyToRecord from '@/components/parts/EmptyToRecord.vue';
-import InheritorContent, { InheritorWorkInfo } from '@/api/inheritor/InheritorContent';
 import { ArrowLeftOutlined } from '@ant-design/icons-vue';
 import { h } from 'vue';
+import InheritorContent, { InheritorWorkInfo } from '@/api/inheritor/InheritorContent';
 
 
 const router = useRouter();

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
src/pages/editor-test.vue


+ 1 - 1
src/pages/forms/form.vue

@@ -126,7 +126,7 @@
 <script setup lang="ts" generic="T extends DataModel, U extends DataModel">
 import { onMounted, ref, toRefs, type PropType, h, watch, computed } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
-import { useWindowOnUnLoadConfirm } from '@/composeable/WindowOnUnLoad';
+import { useWindowOnUnLoadConfirm } from '@imengyu/imengyu-web-shared';
 import { DynamicForm, type IDynamicFormOptions, type IDynamicFormRef } from '@imengyu/vue-dynamic-form';
 import { message, Modal, type FormInstance } from 'ant-design-vue';
 import { ArrowLeftOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';

+ 4 - 3
src/pages/forms/ich.vue

@@ -19,9 +19,10 @@ import Form from './form.vue';
 import InheritorContent, { IchExpandInfo, IchInfo } from '@/api/inheritor/InheritorContent';
 import CommonContent from '@/api/CommonContent';
 import type { IDynamicFormOptions, IDynamicFormRef } from '@imengyu/vue-dynamic-form';
-import type { SelectProps } from 'ant-design-vue';
-import { useBeforeUploadImageChecker, useBeforeUploadVideoChecker, type UploadImageFormItemProps } from '@/components/dynamicf/UploadImageFormItem';
-import type { AddressItem } from '@/components/dynamicf/Map/AddressSercher.vue';
+import { 
+  useBeforeUploadImageChecker, useBeforeUploadVideoChecker, 
+  type UploadImageFormItemProps, type AddressItem
+} from '@imengyu/imengyu-web-shared';
 import { useAuthStore } from '@/stores/auth';
 import { useAliOssUploadCo } from '@/common/upload/AliOssUploadCo';
 import { ExclamationCircleOutlined } from '@ant-design/icons-vue';

+ 1 - 1
src/pages/forms/inheritor.vue

@@ -16,7 +16,7 @@ import Form from './form.vue';
 import InheritorContent, { InheritorExpandInfo, InheritorInfo } from '@/api/inheritor/InheritorContent';
 import CommonContent from '@/api/CommonContent';
 import type { IDynamicFormOptions } from '@imengyu/vue-dynamic-form';
-import { useBeforeUploadImageChecker, useBeforeUploadVideoChecker, type UploadImageFormItemProps } from '@/components/dynamicf/UploadImageFormItem';
+import { useBeforeUploadImageChecker, useBeforeUploadVideoChecker, type UploadImageFormItemProps } from '@imengyu/imengyu-web-shared';
 import { useAuthStore } from '@/stores/auth';
 import { useAliOssUploadCo } from '@/common/upload/AliOssUploadCo';
 

+ 1 - 2
src/pages/forms/seminar.vue

@@ -17,10 +17,9 @@ import Form from './form.vue';
 import InheritorContent, { SeminarExpandInfo, SeminarInfo } from '@/api/inheritor/InheritorContent';
 import CommonContent from '@/api/CommonContent';
 import type { IDynamicFormOptions, IDynamicFormRef } from '@imengyu/vue-dynamic-form';
-import type { AddressItem } from '@/components/dynamicf/Map/AddressSercher.vue';
 import { useAuthStore } from '@/stores/auth';
 import { useAliOssUploadCo } from '@/common/upload/AliOssUploadCo';
-import { useBeforeUploadImageChecker, type UploadImageFormItemProps } from '@/components/dynamicf/UploadImageFormItem';
+import { useBeforeUploadImageChecker, type UploadImageFormItemProps, type AddressItem } from '@imengyu/imengyu-web-shared';
 import { useRoute } from 'vue-router';
 
 const authStore = useAuthStore();

+ 4 - 1
src/pages/forms/works.vue

@@ -17,7 +17,10 @@ import InheritorContent, { InheritorWorkInfo } from '@/api/inheritor/InheritorCo
 import CommonContent from '@/api/CommonContent';
 import type { IDynamicFormOptions } from '@imengyu/vue-dynamic-form';
 import type { SelectProps } from 'ant-design-vue';
-import { useBeforeUploadAudioChecker, useBeforeUploadImageChecker, useBeforeUploadVideoChecker, type UploadImageFormItemProps } from '@/components/dynamicf/UploadImageFormItem';
+import { 
+  useBeforeUploadAudioChecker, useBeforeUploadImageChecker,
+  useBeforeUploadVideoChecker, type UploadImageFormItemProps 
+} from '@imengyu/imengyu-web-shared';
 import { useRoute } from 'vue-router';
 import { useAuthStore } from '@/stores/auth';
 import { useAliOssUploadCo } from '@/common/upload/AliOssUploadCo';

+ 5 - 8
src/pages/inheritor.vue

@@ -169,18 +169,15 @@
 </template>
 
 <script setup lang="ts">
-import { onMounted, ref, watch } from 'vue';
+import { ref, watch } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
-import type { IchInfo, InheritorInfo, InheritorWorkInfo, PlanInfo, SeminarInfo } from '@/api/inheritor/InheritorContent';
+import { SettingsUtils } from '@imengyu/imengyu-utils';
+import { RequestApiError } from '@imengyu/imengyu-utils/dist/request';
+import type { InheritorInfo, InheritorWorkInfo, PlanInfo, SeminarInfo } from '@/api/inheritor/InheritorContent';
+import { ImageGrid, SimpleRichHtml, ShowValueOrNull, useSimpleDataLoader } from '@imengyu/imengyu-web-shared';
 import InheritorContent from '@/api/inheritor/InheritorContent';
-import ImageGrid from '@/components/content/ImageGrid.vue';
 import SimplePointedMap from '@/components/content/SimplePointedMap.vue';
-import SimpleRichHtml from '@/components/display/SimpleRichHtml.vue';
-import ShowValueOrNull from '@/components/dynamicf/Display/ShowValueOrNull.vue';
 import EmptyToRecord from '@/components/parts/EmptyToRecord.vue';
-import { SettingsUtils } from '@imengyu/imengyu-utils';
-import { useSimpleDataLoader } from '@/composeable/SimpleDataLoader';
-import { RequestApiError } from '@imengyu/imengyu-utils/dist/request';
 
 const router = useRouter();
 const route = useRoute();

+ 1 - 1
src/pages/login.vue

@@ -22,7 +22,7 @@
 </template>
 
 <script setup lang="ts">
-import type { SimpleSelectFormItemProps } from '@/components/dynamicf/SimpleSelectFormItem';
+import type { SimpleSelectFormItemProps } from '@imengyu/imengyu-web-shared';
 import { useAuthStore } from '@/stores/auth';
 import { waitTimeOut } from '@imengyu/imengyu-utils';
 import { DynamicForm, type IDynamicFormOptions, type IDynamicFormRef } from '@imengyu/vue-dynamic-form';