Browse Source

feat: IconPicker

kailong321200875 1 year ago
parent
commit
296685eced

+ 3 - 10
scripts/icon.ts

@@ -29,9 +29,9 @@ async function generateIcon() {
       //   name: 'useType',
       //   choices: [
       //     { key: 'local', value: 'local', name: 'Local' },
-      //     { key: 'onLine', value: 'onLine', name: 'OnLine' },
+      //     { key: 'onLine', value: 'onLine', name: 'OnLine' }
       //   ],
-      //   message: 'How to use icons?',
+      //   message: 'How to use icons?'
       // },
       {
         type: 'list',
@@ -39,16 +39,11 @@ async function generateIcon() {
         choices: choices,
         message: 'Select the icon set that needs to be generated?'
       }
-      // {
-      //   type: 'input',
-      //   name: 'output',
-      //   message: 'Select the icon set that needs to be generated?',
-      //   default: 'src/components/Icon/data',
-      // },
     ])
     // ↓命令行问答的答案
     .then(async (answers) => {
       const { iconSet } = answers
+      // const isOnLine = useType === 'onLine'
       const outputDir = path.resolve(process.cwd(), 'src/components/IconPicker/src/data')
       fs.ensureDir(outputDir)
       const genCollections = collections.filter((item) => [iconSet].includes(item.id))
@@ -67,8 +62,6 @@ async function generateIcon() {
           prefixSet.push(prefix)
         }
       }
-      // 将vite的缓存清空
-      // fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'))
       console.log(
         `✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`
       )

+ 159 - 2
src/components/IconPicker/src/IconPicker.vue

@@ -2,10 +2,167 @@
 import epIcons from './data/icons.ep'
 import antIcons from './data/icons.ant-design'
 import tIcons from './data/icons.tdesign'
+import { useDesign } from '@/hooks/web/useDesign'
+import { ElInput, ElPopover, ElScrollbar, ElTabs, ElTabPane, ElPagination } from 'element-plus'
+import { useAppStore } from '@/store/modules/app'
+import { computed, CSSProperties, ref, unref, watch } from 'vue'
+import { nextTick } from 'vue'
 
-console.log(epIcons, antIcons, tIcons)
+const modelValue = defineModel<string>()
+
+const appStore = useAppStore()
+
+const size = computed(() => appStore.getCurrentSize)
+
+const iconSize = computed(() => {
+  return size.value === 'small'
+    ? 'var(--el-component-size-small)'
+    : size.value === 'large'
+    ? 'var(--el-component-size-large)'
+    : 'var(--el-component-size)'
+})
+
+const iconWrapStyle = computed((): CSSProperties => {
+  return {
+    width: iconSize.value,
+    height: iconSize.value,
+    display: 'flex',
+    alignItems: 'center',
+    justifyContent: 'center',
+    boxShadow: '0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset',
+    position: 'relative',
+    left: '-1px',
+    cursor: 'pointer'
+  }
+})
+
+const { getPrefixCls } = useDesign()
+
+const prefixCls = getPrefixCls('icon-picker')
+
+const icons = [epIcons, antIcons, tIcons]
+
+const iconName = ref(icons[0].prefix)
+
+const currentIconNameIndex = computed(() => {
+  return icons.findIndex((item) => item.prefix === iconName.value)
+})
+
+const tabChange = () => {
+  currentPage.value = 1
+}
+
+const pageSize = ref(63)
+
+const currentPage = ref(1)
+
+const filterIcons = (icons: string[]) => {
+  const start = (currentPage.value - 1) * pageSize.value
+  const end = currentPage.value * pageSize.value
+  return icons.slice(start, end)
+}
+
+watch(
+  () => modelValue.value,
+  (val) => {
+    init(val)
+  },
+  {
+    immediate: true
+  }
+)
+
+async function init(icon?: string) {
+  if (!icon) return
+  const iconInfo = icon.split(':')
+  iconName.value = iconInfo[0]
+  const wrapIndex = icons.findIndex((item) => item.prefix === iconInfo[0])
+  // 查询当前icon的索引
+  const index = icons[wrapIndex].icons.findIndex((item) => item === icon)
+  // 计算当前icon的页码
+  await nextTick()
+  currentPage.value = Math.ceil((index + 1) / pageSize.value)
+}
+
+const popoverShow = () => {
+  init(unref(modelValue))
+}
+
+const iconSelect = (icon: string) => {
+  modelValue.value = icon
+}
 </script>
 
 <template>
-  <div> 2222 </div>
+  <div :class="prefixCls" class="flex justify-center items-center box">
+    <ElInput disabled v-model="modelValue" />
+    <ElPopover
+      placement="bottom"
+      trigger="click"
+      :width="450"
+      popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; height: 400px;"
+      @show="popoverShow"
+    >
+      <template #reference>
+        <div v-if="modelValue" :style="iconWrapStyle">
+          <Icon :icon="modelValue" />
+        </div>
+      </template>
+      <ElScrollbar class="h-[calc(100%-50px)]!">
+        <ElTabs tab-position="left" v-model="iconName" @tab-change="tabChange">
+          <ElTabPane v-for="item in icons" :key="item.name" :label="item.name" :name="item.prefix">
+            <div class="flex flex-wrap box-border">
+              <div
+                v-for="icon in filterIcons(item.icons)"
+                :key="icon"
+                :style="{
+                  width: iconSize,
+                  height: iconSize,
+                  display: 'flex',
+                  alignItems: 'center',
+                  justifyContent: 'center',
+                  cursor: 'pointer',
+                  border: `1px solid ${
+                    icon === modelValue ? 'var(--el-color-primary)' : 'var(--el-border-color)'
+                  }`,
+                  boxSizing: 'border-box',
+                  margin: '2px'
+                }"
+                @click="iconSelect(icon)"
+              >
+                <Icon
+                  :icon="icon"
+                  :color="icon === modelValue ? 'var(--el-color-primary)' : 'inherit'"
+                />
+              </div>
+            </div>
+          </ElTabPane>
+        </ElTabs>
+      </ElScrollbar>
+      <div
+        class="h-50px absolute bottom-0 left-0 flex items-center pl-[var(--el-popover-padding)] pr-[var(--el-popover-padding)]"
+      >
+        <ElPagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          :pager-count="5"
+          small
+          :page-sizes="[100, 200, 300, 400]"
+          layout="total, prev, pager, next, jumper"
+          :total="icons[currentIconNameIndex].icons.length"
+        />
+      </div>
+    </ElPopover>
+  </div>
 </template>
+
+<style lang="less" scoped>
+@prefix-cls: ~'@{namespace}-icon-picker';
+
+.@{prefix-cls} {
+  :deep(.@{elNamespace}-input__wrapper) {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+}
+</style>

+ 10 - 1
src/views/Components/IconPicker.vue

@@ -1,7 +1,16 @@
 <script setup lang="ts">
 import { IconPicker } from '@/components/IconPicker'
+import { ref } from 'vue'
+import { ContentWrap } from '@/components/ContentWrap'
+import { useI18n } from '@/hooks/web/useI18n'
+
+const { t } = useI18n()
+
+const currentIcon = ref('tdesign:book-open')
 </script>
 
 <template>
-  <IconPicker />
+  <ContentWrap :title="t('router.iconPicker')">
+    <IconPicker v-model="currentIcon" />
+  </ContentWrap>
 </template>

+ 2 - 2
types/components.d.ts

@@ -1,7 +1,7 @@
 declare module 'vue' {
   export interface GlobalComponents {
-    Icon: typeof import('../components/Icon/src/Icon.vue')['default']
-    Permission: typeof import('../components/Permission/src/Permission.vue')['default']
+    Icon: (typeof import('../components/Icon/src/Icon.vue'))['default']
+    Permission: (typeof import('../components/Permission/src/Permission.vue'))['default']
   }
 }