Selaa lähdekoodia

[Feat 0000] ModelSelect模型选择器开发

houzekong 20 tuntia sitten
vanhempi
commit
c2ef6fc00b

+ 2 - 0
src/components/Form/src/componentMap.ts

@@ -61,6 +61,7 @@ import JSearchSelect from './jeecg/components/JSearchSelect.vue';
 import JAddInput from './jeecg/components/JAddInput.vue';
 import { Time } from '/@/components/Time';
 import JRangeNumber from './jeecg/components/JRangeNumber.vue';
+import ModelSelect from './jeecg/components/ModelSelect.vue';
 import UserSelect from './jeecg/components/userSelect/index.vue';
 import JRangeDate from './jeecg/components/JRangeDate.vue';
 import JRangeTime from './jeecg/components/JRangeTime.vue';
@@ -134,6 +135,7 @@ componentMap.set('JUpload', JUpload);
 componentMap.set('JSearchSelect', JSearchSelect);
 componentMap.set('JAddInput', JAddInput);
 componentMap.set('JRangeNumber', JRangeNumber);
+componentMap.set('ModelSelect', ModelSelect);
 componentMap.set('UserSelect', UserSelect);
 componentMap.set('RangeDate', JRangeDate);
 componentMap.set('RangeTime', JRangeTime);

+ 134 - 0
src/components/Form/src/jeecg/components/ModelSelect.vue

@@ -0,0 +1,134 @@
+<template>
+  <ASelect v-bind="attrs_" v-model:value="state" placeholder="请选择模型" @dropdown-visible-change="onDropdownVisibleChange">
+    <template #[item]="data" v-for="item in Object.keys($slots)">
+      <slot :name="item" v-bind="data || {}"></slot>
+    </template>
+    <template #dropdownRender>
+      <div v-if="getOptions.length" class="model-select-grid">
+        <div
+          v-for="opt in getOptions"
+          :key="opt.fileName"
+          class="model-select-grid-item"
+          :class="{ 'is-selected': state === opt.fileName }"
+          @click="handleSelect(opt)"
+        >
+          <div class="model-select-grid-thumb-wrap">
+            <AntImage v-if="hasOpened" :src="getFileAccessHttpUrl(opt.filePath)" :preview="true" width="64" class="model-select-thumb" />
+          </div>
+          <span class="model-select-name">{{ opt.fileName + deviceType }}</span>
+        </div>
+      </div>
+    </template>
+  </ASelect>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { Select as ASelect, Image as AntImage } from 'ant-design-vue';
+  import { useRuleFormItem } from '/@/hooks/component/useFormItem';
+  import { useAttrs } from '/@/hooks/core/useAttrs';
+  import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
+  import { propTypes } from '/@/utils/propTypes';
+  import { useModelFileStore } from '/@/store/modules/modelFile';
+  import { storeToRefs } from 'pinia';
+
+  type OptionsItem = { label: string; value: string; fileName: string; filePath: string };
+
+  export default defineComponent({
+    name: 'ModelSelect',
+    components: { ASelect, AntImage },
+    inheritAttrs: false,
+    props: {
+      value: propTypes.oneOfType([propTypes.string, propTypes.number]),
+      deviceType: propTypes.string.def(''),
+    },
+    emits: ['change', 'update:value'],
+    setup(props, { emit }) {
+      const emitData = ref<any[]>([]);
+      const attrs = useAttrs();
+      const hasOpened = ref(false);
+
+      const [state] = useRuleFormItem(props, 'value', 'change', emitData);
+
+      const modelFileStore = useModelFileStore();
+      const { modelFileData } = storeToRefs(modelFileStore);
+
+      const attrs_ = computed(() => {
+        const obj: any = { ...unref(attrs) };
+        if (obj && obj['onUpdate:value']) {
+          delete obj['onUpdate:value'];
+        }
+        return obj;
+      });
+
+      const getOptions = computed((): OptionsItem[] => {
+        const key = props.deviceType;
+        if (!key || !modelFileData.value) return [];
+        console.log('debug m', modelFileData.value);
+        const items = modelFileData.value[key.split('_')[0]];
+        if (!items) return [];
+        return items.map((item) => ({
+          label: item.fileName,
+          value: item.fileName,
+          fileName: item.fileName,
+          filePath: item.filePath,
+        }));
+      });
+
+      function onDropdownVisibleChange(visible: boolean) {
+        hasOpened.value = visible;
+      }
+
+      function handleSelect(opt: OptionsItem) {
+        state.value = opt.fileName;
+        emit('update:value', opt.fileName);
+      }
+
+      return { state, attrs_, getOptions, getFileAccessHttpUrl, hasOpened, onDropdownVisibleChange, handleSelect };
+    },
+  });
+</script>
+<style scoped>
+  .model-select-grid {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 8px;
+    padding: 8px;
+    max-height: 350px;
+    overflow-y: auto;
+  }
+  .model-select-grid-item {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 4px;
+    padding: 8px;
+    border: 1px solid #f0f0f0;
+    border-radius: 6px;
+    cursor: pointer;
+    transition:
+      border-color 0.2s,
+      box-shadow 0.2s;
+  }
+  .model-select-grid-item:hover {
+    border-color: #1890ff;
+    box-shadow: 0 2px 8px rgba(24, 144, 255, 0.15);
+  }
+  .model-select-grid-item.is-selected {
+    border-color: #1890ff;
+    background: #e6f7ff;
+  }
+  .model-select-grid-thumb-wrap {
+    line-height: 0;
+  }
+  .model-select-thumb {
+    border-radius: 4px;
+    border: 1px solid #f0f0f0;
+    cursor: pointer;
+  }
+  .model-select-name {
+    font-size: 12px;
+    color: #333;
+    text-align: center;
+    word-break: break-all;
+  }
+</style>

+ 1 - 0
src/components/Form/src/types/index.ts

@@ -154,4 +154,5 @@ export type ComponentType =
   | 'RangeNumber'
   | 'linkRecordSelect'
   | 'RangeTime'
+  | 'ModelSelect'
   | 'JRangeNumber';