Browse Source

chore: @iconify/iconify -> unocss presetIcons

main
刘凯 1 year ago
parent
commit
99918fbd3b
  1. 68
      build/generate/icon/index.ts
  2. 1
      build/vite/optimize.ts
  3. 5
      package.json
  4. 8
      pnpm-lock.yaml
  5. 2
      src/components/Application/index.ts
  6. 3
      src/components/Application/src/AppLocalePicker.vue
  7. 69
      src/components/Application/src/AppSizePicker.vue
  8. 17
      src/components/Application/src/search/AppSearchFooter.vue
  9. 13
      src/components/Application/src/search/AppSearchKeyItem.vue
  10. 5
      src/components/Application/src/search/AppSearchModal.vue
  11. 3
      src/components/Basic/src/BasicArrow.vue
  12. 5
      src/components/Button/src/BasicButton.vue
  13. 3
      src/components/ContextMenu/src/ContextMenu.vue
  14. 3
      src/components/CronTab/src/CronTabInput.vue
  15. 3
      src/components/Cropper/src/CropperAvatar.vue
  16. 7
      src/components/Dropdown/src/Dropdown.vue
  17. 5
      src/components/Form/src/components/FileUpload.vue
  18. 7
      src/components/Form/src/components/UploadItemActions.vue
  19. 793
      src/components/Icon/data/icons.data.ts
  20. 5
      src/components/Icon/index.ts
  21. 102
      src/components/Icon/src/Icon.vue
  22. 19
      src/components/Icon/src/IconPicker.vue
  23. 8
      src/components/Icon/src/icons.ts
  24. 3
      src/components/Menu/src/components/MenuItemContent.vue
  25. 5
      src/components/SimpleMenu/src/SimpleSubMenu.vue
  26. 5
      src/components/SimpleMenu/src/components/SubMenuItem.vue
  27. 5
      src/components/Table/src/components/TableAction.vue
  28. 9
      src/components/Table/src/components/settings/ColumnSetting.vue
  29. 3
      src/components/Table/src/hooks/useRender.ts
  30. 5
      src/components/Tree/src/TreeIcon.ts
  31. 3
      src/components/Tree/src/components/TreeHeader.vue
  32. 3
      src/components/Upload/src/BasicUpload.vue
  33. 3
      src/layouts/default/header/components/ErrorAction.vue
  34. 4
      src/layouts/default/header/components/user-dropdown/DropMenuItem.vue
  35. 6
      src/layouts/default/header/components/user-dropdown/index.vue
  36. 3
      src/layouts/default/setting/index.vue
  37. 11
      src/layouts/default/sider/MixSider.vue
  38. 3
      src/layouts/default/tabs/components/TabContent.vue
  39. 6
      src/router/routes/modules/dashboard.ts
  40. 24
      src/views/base/profile/AccountBind.vue
  41. 20
      src/views/base/profile/data.ts
  42. 4
      src/views/dashboard/analysis/components/GrowCard.vue
  43. 8
      src/views/dashboard/analysis/data.ts
  44. 4
      src/views/dashboard/workbench/components/DynamicInfo.vue
  45. 64
      src/views/dashboard/workbench/components/ProjectCard.vue
  46. 43
      src/views/dashboard/workbench/components/QuickNav.vue
  47. 115
      src/views/dashboard/workbench/components/data.ts
  48. 13
      uno.config.ts

68
build/generate/icon/index.ts

@ -1,68 +0,0 @@
import path from 'node:path'
import fs from 'fs-extra'
import inquirer from 'inquirer'
import colors from 'picocolors'
import pkg from '../../../package.json'
async function generateIcon() {
const dir = path.resolve(process.cwd(), 'node_modules/@iconify/json')
const raw = await fs.readJSON(path.join(dir, 'collections.json'))
const collections = Object.entries(raw).map(([id, v]) => ({
...(v as any),
id,
}))
const choices = collections.map(item => ({ key: item.id, value: item.id, name: item.name }))
inquirer
.prompt([
{
type: 'list',
name: 'useType',
choices: [
{ key: 'local', value: 'local', name: 'Local' },
{ key: 'onLine', value: 'onLine', name: 'OnLine' },
],
message: 'How to use icons?',
},
{
type: 'list',
name: 'iconSet',
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, output, useType } = answers
const outputDir = path.resolve(process.cwd(), output)
await fs.ensureDir(outputDir)
const genCollections = collections.filter(item => [iconSet].includes(item.id))
const prefixSet: string[] = []
for (const info of genCollections) {
const data = await fs.readJSON(path.join(dir, 'json', `${info.id}.json`))
if (data) {
const { prefix } = data
const isLocal = useType === 'local'
const icons = Object.keys(data.icons).map(item => `${isLocal ? `${prefix}:` : ''}${item}`)
fs.writeFileSync(
path.join(output, 'icons.data.ts'),
`export default ${isLocal ? JSON.stringify(icons) : JSON.stringify({ prefix, icons })}`,
)
prefixSet.push(prefix)
}
}
await fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'))
console.log(`${colors.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`)
})
}
generateIcon()

1
build/vite/optimize.ts

@ -22,7 +22,6 @@ const include = [
'echarts/renderers', 'echarts/renderers',
'@vueuse/core', '@vueuse/core',
'@zxcvbn-ts/core', '@zxcvbn-ts/core',
'@iconify/iconify',
'vue-json-pretty', 'vue-json-pretty',
'ant-design-vue', 'ant-design-vue',
'ant-design-vue/es/style', 'ant-design-vue/es/style',

5
package.json

@ -32,13 +32,11 @@
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"prepare": "husky install", "prepare": "husky install"
"gen:icon": "esno ./build/generate/icon/index.ts"
}, },
"dependencies": { "dependencies": {
"@ant-design/colors": "^7.0.2", "@ant-design/colors": "^7.0.2",
"@ant-design/icons-vue": "^7.0.1", "@ant-design/icons-vue": "^7.0.1",
"@iconify/iconify": "^3.1.1",
"@videojs-player/vue": "^1.0.0", "@videojs-player/vue": "^1.0.0",
"@vue/runtime-core": "^3.3.8", "@vue/runtime-core": "^3.3.8",
"@vueuse/core": "^10.6.1", "@vueuse/core": "^10.6.1",
@ -76,7 +74,6 @@
"@commitlint/cli": "^18.4.4", "@commitlint/cli": "^18.4.4",
"@commitlint/config-conventional": "^18.4.4", "@commitlint/config-conventional": "^18.4.4",
"@iconify/json": "^2.2.164", "@iconify/json": "^2.2.164",
"@purge-icons/generated": "^0.10.0",
"@types/codemirror": "^5.60.15", "@types/codemirror": "^5.60.15",
"@types/crypto-js": "^4.2.1", "@types/crypto-js": "^4.2.1",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",

8
pnpm-lock.yaml

@ -11,9 +11,6 @@ dependencies:
'@ant-design/icons-vue': '@ant-design/icons-vue':
specifier: ^7.0.1 specifier: ^7.0.1
version: 7.0.1(vue@3.4.10) version: 7.0.1(vue@3.4.10)
'@iconify/iconify':
specifier: ^3.1.1
version: 3.1.1
'@videojs-player/vue': '@videojs-player/vue':
specifier: ^1.0.0 specifier: ^1.0.0
version: 1.0.0(@types/video.js@7.3.56)(video.js@7.21.5)(vue@3.4.10) version: 1.0.0(@types/video.js@7.3.56)(video.js@7.21.5)(vue@3.4.10)
@ -121,9 +118,6 @@ devDependencies:
'@iconify/json': '@iconify/json':
specifier: ^2.2.164 specifier: ^2.2.164
version: 2.2.168 version: 2.2.168
'@purge-icons/generated':
specifier: ^0.10.0
version: 0.10.0
'@types/codemirror': '@types/codemirror':
specifier: ^5.60.15 specifier: ^5.60.15
version: 5.60.15 version: 5.60.15
@ -2051,6 +2045,7 @@ packages:
resolution: {integrity: sha512-1nemfyD/OJzh9ALepH7YfuuP8BdEB24Skhd8DXWh0hzcOxImbb1ZizSZkpCzAwSZSGcJFmscIBaBQu+yLyWaxQ==} resolution: {integrity: sha512-1nemfyD/OJzh9ALepH7YfuuP8BdEB24Skhd8DXWh0hzcOxImbb1ZizSZkpCzAwSZSGcJFmscIBaBQu+yLyWaxQ==}
dependencies: dependencies:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
dev: true
/@iconify/json@2.2.168: /@iconify/json@2.2.168:
resolution: {integrity: sha512-NuimrvW7/BfOK97Kx0YMWG8UMcqdfc0GZ0mZfewA90Qvl3+dchLmbyvNXnFraUFrrS8Luie3P6R0+3gHE9DnIA==} resolution: {integrity: sha512-NuimrvW7/BfOK97Kx0YMWG8UMcqdfc0GZ0mZfewA90Qvl3+dchLmbyvNXnFraUFrrS8Luie3P6R0+3gHE9DnIA==}
@ -2061,6 +2056,7 @@ packages:
/@iconify/types@2.0.0: /@iconify/types@2.0.0:
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
dev: true
/@iconify/utils@2.1.14: /@iconify/utils@2.1.14:
resolution: {integrity: sha512-9pKIntkbLbjVVFxH32td21Am3AGGJfyI2KY2d8yDQxkZe4BBZtufJI8NgcamFn8B5QKLU9ai2VMo8OEov8jAtw==} resolution: {integrity: sha512-9pKIntkbLbjVVFxH32td21Am3AGGJfyI2KY2d8yDQxkZe4BBZtufJI8NgcamFn8B5QKLU9ai2VMo8OEov8jAtw==}

2
src/components/Application/index.ts

@ -1,7 +1,6 @@
import appLogo from './src/AppLogo.vue' import appLogo from './src/AppLogo.vue'
import appProvider from './src/AppProvider.vue' import appProvider from './src/AppProvider.vue'
import appSearch from './src/search/AppSearch.vue' import appSearch from './src/search/AppSearch.vue'
import appSizePicker from './src/AppSizePicker.vue'
import appLocalePicker from './src/AppLocalePicker.vue' import appLocalePicker from './src/AppLocalePicker.vue'
import appDarkModeToggle from './src/AppDarkModeToggle.vue' import appDarkModeToggle from './src/AppDarkModeToggle.vue'
import { withInstall } from '@/utils' import { withInstall } from '@/utils'
@ -11,6 +10,5 @@ export { useAppProviderContext } from './src/useAppContext'
export const AppLogo = withInstall(appLogo) export const AppLogo = withInstall(appLogo)
export const AppProvider = withInstall(appProvider) export const AppProvider = withInstall(appProvider)
export const AppSearch = withInstall(appSearch) export const AppSearch = withInstall(appSearch)
export const AppSizePicker = withInstall(appSizePicker)
export const AppLocalePicker = withInstall(appLocalePicker) export const AppLocalePicker = withInstall(appLocalePicker)
export const AppDarkModeToggle = withInstall(appDarkModeToggle) export const AppDarkModeToggle = withInstall(appDarkModeToggle)

3
src/components/Application/src/AppLocalePicker.vue

@ -3,7 +3,6 @@ import { computed, ref, unref, watchEffect } from 'vue'
import type { LocaleType } from '@/types/config' import type { LocaleType } from '@/types/config'
import type { DropMenu } from '@/components/Dropdown' import type { DropMenu } from '@/components/Dropdown'
import { Dropdown } from '@/components/Dropdown' import { Dropdown } from '@/components/Dropdown'
import { Icon } from '@/components/Icon'
import { useLocale } from '@/locales/useLocale' import { useLocale } from '@/locales/useLocale'
import { localeList } from '@/settings/localeSetting' import { localeList } from '@/settings/localeSetting'
@ -58,7 +57,7 @@ function handleMenuEvent(menu: DropMenu) {
@menu-event="handleMenuEvent" @menu-event="handleMenuEvent"
> >
<span class="flex cursor-pointer items-center"> <span class="flex cursor-pointer items-center">
<Icon icon="ion:language" /> <span class="i-ion:language" />
<span v-if="showText" class="ml-1">{{ getLocaleText }}</span> <span v-if="showText" class="ml-1">{{ getLocaleText }}</span>
</span> </span>
</Dropdown> </Dropdown>

69
src/components/Application/src/AppSizePicker.vue

@ -1,69 +0,0 @@
<script lang="ts" setup>
import { computed, ref, unref, watchEffect } from 'vue'
import type { AppSizeType } from '@/types/config'
import type { DropMenu } from '@/components/Dropdown'
import { Dropdown } from '@/components/Dropdown'
import { Icon } from '@/components/Icon'
import { sizeList } from '@/settings/sizeSetting'
import { useAppStore } from '@/store/modules/app'
const props = defineProps({
//
showText: { type: Boolean, default: true },
//
reload: { type: Boolean },
})
const appStore = useAppStore()
const selectedKeys = ref<string[]>([])
const getSizeText = computed(() => {
const key = selectedKeys.value[0]
if (!key)
return ''
return sizeList.find(item => item.event === key)?.text
})
watchEffect(() => {
selectedKeys.value = [unref(appStore.getComponentSize as AppSizeType)]
})
async function toggleSize(size: AppSizeType) {
appStore.setComponentSize(size)
selectedKeys.value = [size as string]
props.reload && location.reload()
}
function handleMenuEvent(menu: DropMenu) {
if (unref(appStore.getComponentSize) === menu.event)
return
toggleSize(menu.event as AppSizeType)
}
</script>
<template>
<Dropdown
placement="bottom"
:trigger="['click']"
:drop-menu-list="sizeList"
:selected-keys="selectedKeys"
overlay-class-name="app-locale-picker-overlay"
@menu-event="handleMenuEvent"
>
<span class="flex cursor-pointer items-center">
<Icon icon="mdi:format-size" />
<span v-if="showText" class="ml-1">{{ getSizeText }}</span>
</span>
</Dropdown>
</template>
<style lang="less">
.app-locale-picker-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
}
}
</style>

17
src/components/Application/src/search/AppSearchFooter.vue

@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import AppSearchKeyItem from './AppSearchKeyItem.vue'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -9,12 +8,20 @@ const { t } = useI18n()
<template> <template>
<div class="relative h-10 flex flex-shrink-0 items-center border-t-1 rounded-bl-2xl px-4 py-0 dark:border-light-100"> <div class="relative h-10 flex flex-shrink-0 items-center border-t-1 rounded-bl-2xl px-4 py-0 dark:border-light-100">
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ant-design:enter-outlined" /> <div :class="`${prefixCls}-item`">
<span class="i-ant-design:enter-outlined" />
</div>
<span>{{ t('component.app.toSearch') }}</span> <span>{{ t('component.app.toSearch') }}</span>
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-up-outline" /> <div :class="`${prefixCls}-item`">
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-down-outline" /> <span class="i-ion:arrow-up-outline" />
</div>
<div :class="`${prefixCls}-item`">
<span class="i-ion:arrow-down-outline" />
</div>
<span>{{ t('component.app.toNavigate') }}</span> <span>{{ t('component.app.toNavigate') }}</span>
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="mdi:keyboard-esc" /> <div :class="`${prefixCls}-item`">
<span class="i-mdi:keyboard-esc" />
</div>
<span>{{ t('common.closeText') }}</span> <span>{{ t('common.closeText') }}</span>
</div> </div>
</template> </template>

13
src/components/Application/src/search/AppSearchKeyItem.vue

@ -1,13 +0,0 @@
<script lang="ts" setup>
import { Icon } from '@/components/Icon'
defineProps({
icon: String,
})
</script>
<template>
<span :class="$attrs.class">
<Icon :icon="icon" />
</span>
</template>

5
src/components/Application/src/search/AppSearchModal.vue

@ -3,7 +3,6 @@ import { computed, nextTick, ref, unref, watch } from 'vue'
import { SearchOutlined } from '@ant-design/icons-vue' import { SearchOutlined } from '@ant-design/icons-vue'
import AppSearchFooter from './AppSearchFooter.vue' import AppSearchFooter from './AppSearchFooter.vue'
import { useMenuSearch } from './useMenuSearch' import { useMenuSearch } from './useMenuSearch'
import { Icon } from '@/components/Icon'
import vClickOutside from '@/directives/clickOutside' import vClickOutside from '@/directives/clickOutside'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { useRefs } from '@/hooks/core/useRefs' import { useRefs } from '@/hooks/core/useRefs'
@ -89,13 +88,13 @@ function handleClose() {
@click="handleEnter" @click="handleEnter"
> >
<div :class="`${prefixCls}-list__item-icon`"> <div :class="`${prefixCls}-list__item-icon`">
<Icon :icon="item.icon || 'mdi:form-select'" :size="20" /> <span :class="item.icon" class="text-[20px]" />
</div> </div>
<div :class="`${prefixCls}-list__item-text`"> <div :class="`${prefixCls}-list__item-text`">
{{ item.name }} {{ item.name }}
</div> </div>
<div :class="`${prefixCls}-list__item-enter`"> <div :class="`${prefixCls}-list__item-enter`">
<Icon icon="ant-design:enter-outlined" :size="20" /> <span class="i-ant-design:enter-outlined text-[20px]" />
</div> </div>
</li> </li>
</ul> </ul>

3
src/components/Basic/src/BasicArrow.vue

@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue'
import { Icon } from '@/components/Icon'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
const props = defineProps({ const props = defineProps({
@ -41,7 +40,7 @@ const getClass = computed(() => {
<template> <template>
<span :class="getClass"> <span :class="getClass">
<Icon icon="ion:chevron-forward" :style="$attrs.iconStyle" /> <span class="i-ion:chevron-forward" />
</span> </span>
</template> </template>

5
src/components/Button/src/BasicButton.vue

@ -4,7 +4,6 @@ import type { ComponentOptionsMixin } from 'vue'
import { computed, unref } from 'vue' import { computed, unref } from 'vue'
import { buttonProps } from './props' import { buttonProps } from './props'
import { useAttrs } from '@/hooks/core/useAttrs' import { useAttrs } from '@/hooks/core/useAttrs'
import { Icon } from '@/components/Icon'
defineOptions({ name: 'AButton', extends: Button as ComponentOptionsMixin, indeterminate: false }) defineOptions({ name: 'AButton', extends: Button as ComponentOptionsMixin, indeterminate: false })
const props = defineProps(buttonProps) const props = defineProps(buttonProps)
@ -49,9 +48,9 @@ const getBindValue = computed(() => ({ ...unref(attrs), ...props }))
<slot name="icon" /> <slot name="icon" />
</template> </template>
<template #default="data"> <template #default="data">
<Icon v-if="preIcon" :icon="preIcon" :size="iconSize" /> <span v-if="preIcon" :class="preIcon" :style="{ fontSize: `${iconSize}px` }" />
<slot v-bind="data || {}" /> <slot v-bind="data || {}" />
<Icon v-if="postIcon" :icon="postIcon" :size="iconSize" /> <span v-if="postIcon" :class="postIcon" :style="{ fontSize: `${iconSize}px` }" />
</template> </template>
</Button> </Button>
</template> </template>

3
src/components/ContextMenu/src/ContextMenu.vue

@ -3,7 +3,6 @@ import type { CSSProperties, FunctionalComponent } from 'vue'
import { computed, defineComponent, nextTick, onMounted, onUnmounted, ref, unref } from 'vue' import { computed, defineComponent, nextTick, onMounted, onUnmounted, ref, unref } from 'vue'
import { Divider, Menu } from 'ant-design-vue' import { Divider, Menu } from 'ant-design-vue'
import type { Axis, ContextMenuItem, ItemContentProps } from './typing' import type { Axis, ContextMenuItem, ItemContentProps } from './typing'
import { Icon } from '@/components/Icon'
const prefixCls = 'context-menu' const prefixCls = 'context-menu'
@ -32,7 +31,7 @@ const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
const { item } = props const { item } = props
return ( return (
<span class="inline-block w-full px-4" onClick={props.handler.bind(null, item)}> <span class="inline-block w-full px-4" onClick={props.handler.bind(null, item)}>
{props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />} {props.showIcon && item.icon && <span className={`mr-2 ${item.icon}`} />}
<span>{item.label}</span> <span>{item.label}</span>
</span> </span>
) )

3
src/components/CronTab/src/CronTabInput.vue

@ -5,7 +5,6 @@ import CronTabModal from './CronTabModal.vue'
import { cronEmits, cronProps } from './cron.data' import { cronEmits, cronProps } from './cron.data'
import { useModal } from '@/components/Modal' import { useModal } from '@/components/Modal'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { Icon } from '@/components/Icon'
const props = defineProps({ const props = defineProps({
...cronProps, ...cronProps,
@ -39,7 +38,7 @@ function showConfigModal() {
<Input v-model:value="editCronValue" :placeholder="placeholder" :disabled="disabled"> <Input v-model:value="editCronValue" :placeholder="placeholder" :disabled="disabled">
<template #addonAfter> <template #addonAfter>
<a class="cursor-pointer" :disabled="disabled ? 'disabled' : null" @click="showConfigModal"> <a class="cursor-pointer" :disabled="disabled ? 'disabled' : null" @click="showConfigModal">
<Icon class="relative right-0.5 top-0.25" icon="ant-design:setting-outlined" /> <span class="i-ant-design:setting-outlined relative right-0.5 top-0.25" />
<span>选择</span> <span>选择</span>
</a> </a>
</template> </template>

3
src/components/Cropper/src/CropperAvatar.vue

@ -8,7 +8,6 @@ import { useModal } from '@/components/Modal'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import type { ButtonProps } from '@/components/Button' import type { ButtonProps } from '@/components/Button'
import { Icon } from '@/components/Icon'
defineOptions({ name: 'CropperAvatar' }) defineOptions({ name: 'CropperAvatar' })
@ -66,7 +65,7 @@ defineExpose({ openModal: openModal.bind(null, true), closeModal })
<div :class="getClass" :style="getStyle"> <div :class="getClass" :style="getStyle">
<div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal()"> <div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal()">
<div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle"> <div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle">
<Icon icon="ant-design:cloud-upload-outlined" :size="getIconWidth" :style="getImageWrapperStyle" color="#d6d6d6" /> <span :style="{ ...getImageWrapperStyle, fontSize: `${getIconWidth}px` }" class="i-ant-design:cloud-upload-outlined text-[#d6d6d6]" />
</div> </div>
<Avatar v-if="sourceValue" :size="{ xs: 24, sm: 32, md: 40, lg: 64, xl: 80, xxl: 100 }" :src="sourceValue" alt="avatar" /> <Avatar v-if="sourceValue" :size="{ xs: 24, sm: 32, md: 40, lg: 64, xl: 80, xxl: 100 }" :src="sourceValue" alt="avatar" />
</div> </div>

7
src/components/Dropdown/src/Dropdown.vue

@ -3,7 +3,6 @@ import { computed } from 'vue'
import { Dropdown, Menu, Popconfirm } from 'ant-design-vue' import { Dropdown, Menu, Popconfirm } from 'ant-design-vue'
import { omit } from 'lodash-es' import { omit } from 'lodash-es'
import type { DropMenu } from './typing' import type { DropMenu } from './typing'
import { Icon } from '@/components/Icon'
import { isFunction } from '@/utils/is' import { isFunction } from '@/utils/is'
const props = defineProps({ const props = defineProps({
@ -67,15 +66,15 @@ const getAttr = (key: string | number) => ({ key })
<AMenuItem v-bind="getAttr(item.event)" :disabled="item.disabled" @click="handleClickMenu(item)"> <AMenuItem v-bind="getAttr(item.event)" :disabled="item.disabled" @click="handleClickMenu(item)">
<APopconfirm v-if="popconfirm && item.popConfirm" v-bind="getPopConfirmAttrs(item.popConfirm)" :disabled="item.disabled"> <APopconfirm v-if="popconfirm && item.popConfirm" v-bind="getPopConfirmAttrs(item.popConfirm)" :disabled="item.disabled">
<template v-if="item.popConfirm.icon" #icon> <template v-if="item.popConfirm.icon" #icon>
<Icon :icon="item.popConfirm.icon" /> <span :class="item.popConfirm.icon" />
</template> </template>
<div> <div>
<Icon v-if="item.icon" :icon="item.icon" /> <span v-if="item.icon" :class="item.icon" />
<span class="ml-1">{{ item.text }}</span> <span class="ml-1">{{ item.text }}</span>
</div> </div>
</APopconfirm> </APopconfirm>
<template v-else> <template v-else>
<Icon v-if="item.icon" :icon="item.icon" /> <span v-if="item.icon" :class="item.icon" />
<span class="ml-1">{{ item.text }}</span> <span class="ml-1">{{ item.text }}</span>
</template> </template>
</AMenuItem> </AMenuItem>

5
src/components/Form/src/components/FileUpload.vue

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Upload } from 'ant-design-vue' import { Upload } from 'ant-design-vue'
import { computed, reactive, ref, unref, watch } from 'vue' import { computed, reactive, ref, unref, watch } from 'vue'
import { Icon } from '@/components/Icon'
import { getAccessToken, getTenantId } from '@/utils/auth' import { getAccessToken, getTenantId } from '@/utils/auth'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
@ -286,14 +285,14 @@ function getFileName(path) {
> >
<template v-if="isImageMode"> <template v-if="isImageMode">
<div v-if="!isMaxCount"> <div v-if="!isMaxCount">
<Icon icon="ant-design:plus-outlined" /> <span class="i-ant-design:plus-outlined" />
<div class="ant-upload-text"> <div class="ant-upload-text">
{{ text }} {{ text }}
</div> </div>
</div> </div>
</template> </template>
<a-button v-else-if="buttonOpen" :disabled="isMaxCount || disabled"> <a-button v-else-if="buttonOpen" :disabled="isMaxCount || disabled">
<Icon icon="ant-design:upload-outlined" /> <span class="i-ant-design:upload-outlined" />
<span>{{ text }}</span> <span>{{ text }}</span>
</a-button> </a-button>
</Upload> </Upload>

7
src/components/Form/src/components/UploadItemActions.vue

@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, unref } from 'vue' import { computed, unref } from 'vue'
import { Icon } from '@/components/Icon'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
const props = defineProps({ const props = defineProps({
@ -76,15 +75,15 @@ function onDownload() {
<template> <template>
<div v-show="download" class="upload-download-handler"> <div v-show="download" class="upload-download-handler">
<a class="download" title="下载" @click="onDownload"> <a class="download" title="下载" @click="onDownload">
<Icon icon="ant-design:download" /> <span class="i-ant-design:download" />
</a> </a>
</div> </div>
<div v-show="mover && list.length > 1" class="upload-mover-handler"> <div v-show="mover && list.length > 1" class="upload-mover-handler">
<a title="向前移动" @click="onMoveForward"> <a title="向前移动" @click="onMoveForward">
<Icon icon="ant-design:arrow-left" /> <span class="i-ant-design:arrow-left" />
</a> </a>
<a title="向后移动" @click="onMoveBack"> <a title="向后移动" @click="onMoveBack">
<Icon icon="ant-design:arrow-right" /> <span class="i-ant-design:arrow-right" />
</a> </a>
</div> </div>
</template> </template>

793
src/components/Icon/data/icons.data.ts

@ -1,793 +0,0 @@
export default {
prefix: 'ant-design',
icons: [
'account-book-filled',
'account-book-outlined',
'account-book-twotone',
'aim-outlined',
'alert-filled',
'alert-outlined',
'alert-twotone',
'alibaba-outlined',
'align-center-outlined',
'align-left-outlined',
'align-right-outlined',
'alipay-circle-filled',
'alipay-circle-outlined',
'alipay-outlined',
'alipay-square-filled',
'aliwangwang-filled',
'aliwangwang-outlined',
'aliyun-outlined',
'amazon-circle-filled',
'amazon-outlined',
'amazon-square-filled',
'android-filled',
'android-outlined',
'ant-cloud-outlined',
'ant-design-outlined',
'apartment-outlined',
'api-filled',
'api-outlined',
'api-twotone',
'apple-filled',
'apple-outlined',
'appstore-add-outlined',
'appstore-filled',
'appstore-outlined',
'appstore-twotone',
'area-chart-outlined',
'arrow-down-outlined',
'arrow-left-outlined',
'arrow-right-outlined',
'arrow-up-outlined',
'arrows-alt-outlined',
'audio-filled',
'audio-muted-outlined',
'audio-outlined',
'audio-twotone',
'audit-outlined',
'backward-filled',
'backward-outlined',
'bank-filled',
'bank-outlined',
'bank-twotone',
'bar-chart-outlined',
'barcode-outlined',
'bars-outlined',
'behance-circle-filled',
'behance-outlined',
'behance-square-filled',
'behance-square-outlined',
'bell-filled',
'bell-outlined',
'bell-twotone',
'bg-colors-outlined',
'block-outlined',
'bold-outlined',
'book-filled',
'book-outlined',
'book-twotone',
'border-bottom-outlined',
'border-horizontal-outlined',
'border-inner-outlined',
'border-left-outlined',
'border-outer-outlined',
'border-outlined',
'border-right-outlined',
'border-top-outlined',
'border-verticle-outlined',
'borderless-table-outlined',
'box-plot-filled',
'box-plot-outlined',
'box-plot-twotone',
'branches-outlined',
'bug-filled',
'bug-outlined',
'bug-twotone',
'build-filled',
'build-outlined',
'build-twotone',
'bulb-filled',
'bulb-outlined',
'bulb-twotone',
'calculator-filled',
'calculator-outlined',
'calculator-twotone',
'calendar-filled',
'calendar-outlined',
'calendar-twotone',
'camera-filled',
'camera-outlined',
'camera-twotone',
'car-filled',
'car-outlined',
'car-twotone',
'caret-down-filled',
'caret-down-outlined',
'caret-left-filled',
'caret-left-outlined',
'caret-right-filled',
'caret-right-outlined',
'caret-up-filled',
'caret-up-outlined',
'carry-out-filled',
'carry-out-outlined',
'carry-out-twotone',
'check-circle-filled',
'check-circle-outlined',
'check-circle-twotone',
'check-outlined',
'check-square-filled',
'check-square-outlined',
'check-square-twotone',
'chrome-filled',
'chrome-outlined',
'ci-circle-filled',
'ci-circle-outlined',
'ci-circle-twotone',
'ci-outlined',
'ci-twotone',
'clear-outlined',
'clock-circle-filled',
'clock-circle-outlined',
'clock-circle-twotone',
'close-circle-filled',
'close-circle-outlined',
'close-circle-twotone',
'close-outlined',
'close-square-filled',
'close-square-outlined',
'close-square-twotone',
'cloud-download-outlined',
'cloud-filled',
'cloud-outlined',
'cloud-server-outlined',
'cloud-sync-outlined',
'cloud-twotone',
'cloud-upload-outlined',
'cluster-outlined',
'code-filled',
'code-outlined',
'code-sandbox-circle-filled',
'code-sandbox-outlined',
'code-sandbox-square-filled',
'code-twotone',
'codepen-circle-filled',
'codepen-circle-outlined',
'codepen-outlined',
'codepen-square-filled',
'coffee-outlined',
'column-height-outlined',
'column-width-outlined',
'comment-outlined',
'compass-filled',
'compass-outlined',
'compass-twotone',
'compress-outlined',
'console-sql-outlined',
'contacts-filled',
'contacts-outlined',
'contacts-twotone',
'container-filled',
'container-outlined',
'container-twotone',
'control-filled',
'control-outlined',
'control-twotone',
'copy-filled',
'copy-outlined',
'copy-twotone',
'copyright-circle-filled',
'copyright-circle-outlined',
'copyright-circle-twotone',
'copyright-outlined',
'copyright-twotone',
'credit-card-filled',
'credit-card-outlined',
'credit-card-twotone',
'crown-filled',
'crown-outlined',
'crown-twotone',
'customer-service-filled',
'customer-service-outlined',
'customer-service-twotone',
'dash-outlined',
'dashboard-filled',
'dashboard-outlined',
'dashboard-twotone',
'database-filled',
'database-outlined',
'database-twotone',
'delete-column-outlined',
'delete-filled',
'delete-outlined',
'delete-row-outlined',
'delete-twotone',
'delivered-procedure-outlined',
'deployment-unit-outlined',
'desktop-outlined',
'diff-filled',
'diff-outlined',
'diff-twotone',
'dingding-outlined',
'dingtalk-circle-filled',
'dingtalk-outlined',
'dingtalk-square-filled',
'disconnect-outlined',
'dislike-filled',
'dislike-outlined',
'dislike-twotone',
'dollar-circle-filled',
'dollar-circle-outlined',
'dollar-circle-twotone',
'dollar-outlined',
'dollar-twotone',
'dot-chart-outlined',
'double-left-outlined',
'double-right-outlined',
'down-circle-filled',
'down-circle-outlined',
'down-circle-twotone',
'down-outlined',
'down-square-filled',
'down-square-outlined',
'down-square-twotone',
'download-outlined',
'drag-outlined',
'dribbble-circle-filled',
'dribbble-outlined',
'dribbble-square-filled',
'dribbble-square-outlined',
'dropbox-circle-filled',
'dropbox-outlined',
'dropbox-square-filled',
'edit-filled',
'edit-outlined',
'edit-twotone',
'ellipsis-outlined',
'enter-outlined',
'environment-filled',
'environment-outlined',
'environment-twotone',
'euro-circle-filled',
'euro-circle-outlined',
'euro-circle-twotone',
'euro-outlined',
'euro-twotone',
'exception-outlined',
'exclamation-circle-filled',
'exclamation-circle-outlined',
'exclamation-circle-twotone',
'exclamation-outlined',
'expand-alt-outlined',
'expand-outlined',
'experiment-filled',
'experiment-outlined',
'experiment-twotone',
'export-outlined',
'eye-filled',
'eye-invisible-filled',
'eye-invisible-outlined',
'eye-invisible-twotone',
'eye-outlined',
'eye-twotone',
'facebook-filled',
'facebook-outlined',
'fall-outlined',
'fast-backward-filled',
'fast-backward-outlined',
'fast-forward-filled',
'fast-forward-outlined',
'field-binary-outlined',
'field-number-outlined',
'field-string-outlined',
'field-time-outlined',
'file-add-filled',
'file-add-outlined',
'file-add-twotone',
'file-done-outlined',
'file-excel-filled',
'file-excel-outlined',
'file-excel-twotone',
'file-exclamation-filled',
'file-exclamation-outlined',
'file-exclamation-twotone',
'file-filled',
'file-gif-outlined',
'file-image-filled',
'file-image-outlined',
'file-image-twotone',
'file-jpg-outlined',
'file-markdown-filled',
'file-markdown-outlined',
'file-markdown-twotone',
'file-outlined',
'file-pdf-filled',
'file-pdf-outlined',
'file-pdf-twotone',
'file-ppt-filled',
'file-ppt-outlined',
'file-ppt-twotone',
'file-protect-outlined',
'file-search-outlined',
'file-sync-outlined',
'file-text-filled',
'file-text-outlined',
'file-text-twotone',
'file-twotone',
'file-unknown-filled',
'file-unknown-outlined',
'file-unknown-twotone',
'file-word-filled',
'file-word-outlined',
'file-word-twotone',
'file-zip-filled',
'file-zip-outlined',
'file-zip-twotone',
'filter-filled',
'filter-outlined',
'filter-twotone',
'fire-filled',
'fire-outlined',
'fire-twotone',
'flag-filled',
'flag-outlined',
'flag-twotone',
'folder-add-filled',
'folder-add-outlined',
'folder-add-twotone',
'folder-filled',
'folder-open-filled',
'folder-open-outlined',
'folder-open-twotone',
'folder-outlined',
'folder-twotone',
'folder-view-outlined',
'font-colors-outlined',
'font-size-outlined',
'fork-outlined',
'form-outlined',
'format-painter-filled',
'format-painter-outlined',
'forward-filled',
'forward-outlined',
'frown-filled',
'frown-outlined',
'frown-twotone',
'fullscreen-exit-outlined',
'fullscreen-outlined',
'function-outlined',
'fund-filled',
'fund-outlined',
'fund-projection-screen-outlined',
'fund-twotone',
'fund-view-outlined',
'funnel-plot-filled',
'funnel-plot-outlined',
'funnel-plot-twotone',
'gateway-outlined',
'gif-outlined',
'gift-filled',
'gift-outlined',
'gift-twotone',
'github-filled',
'github-outlined',
'gitlab-filled',
'gitlab-outlined',
'global-outlined',
'gold-filled',
'gold-outlined',
'gold-twotone',
'golden-filled',
'google-circle-filled',
'google-outlined',
'google-plus-circle-filled',
'google-plus-outlined',
'google-plus-square-filled',
'google-square-filled',
'group-outlined',
'hdd-filled',
'hdd-outlined',
'hdd-twotone',
'heart-filled',
'heart-outlined',
'heart-twotone',
'heat-map-outlined',
'highlight-filled',
'highlight-outlined',
'highlight-twotone',
'history-outlined',
'home-filled',
'home-outlined',
'home-twotone',
'hourglass-filled',
'hourglass-outlined',
'hourglass-twotone',
'html5-filled',
'html5-outlined',
'html5-twotone',
'idcard-filled',
'idcard-outlined',
'idcard-twotone',
'ie-circle-filled',
'ie-outlined',
'ie-square-filled',
'import-outlined',
'inbox-outlined',
'info-circle-filled',
'info-circle-outlined',
'info-circle-twotone',
'info-outlined',
'insert-row-above-outlined',
'insert-row-below-outlined',
'insert-row-left-outlined',
'insert-row-right-outlined',
'instagram-filled',
'instagram-outlined',
'insurance-filled',
'insurance-outlined',
'insurance-twotone',
'interaction-filled',
'interaction-outlined',
'interaction-twotone',
'issues-close-outlined',
'italic-outlined',
'key-outlined',
'laptop-outlined',
'layout-filled',
'layout-outlined',
'layout-twotone',
'left-circle-filled',
'left-circle-outlined',
'left-circle-twotone',
'left-outlined',
'left-square-filled',
'left-square-outlined',
'left-square-twotone',
'like-filled',
'like-outlined',
'like-twotone',
'line-chart-outlined',
'line-height-outlined',
'line-outlined',
'link-outlined',
'linkedin-filled',
'linkedin-outlined',
'loading-3-quarters-outlined',
'loading-outlined',
'lock-filled',
'lock-outlined',
'lock-twotone',
'login-outlined',
'logout-outlined',
'mac-command-filled',
'mac-command-outlined',
'mail-filled',
'mail-outlined',
'mail-twotone',
'man-outlined',
'medicine-box-filled',
'medicine-box-outlined',
'medicine-box-twotone',
'medium-circle-filled',
'medium-outlined',
'medium-square-filled',
'medium-workmark-outlined',
'meh-filled',
'meh-outlined',
'meh-twotone',
'menu-fold-outlined',
'menu-outlined',
'menu-unfold-outlined',
'merge-cells-outlined',
'message-filled',
'message-outlined',
'message-twotone',
'minus-circle-filled',
'minus-circle-outlined',
'minus-circle-twotone',
'minus-outlined',
'minus-square-filled',
'minus-square-outlined',
'minus-square-twotone',
'mobile-filled',
'mobile-outlined',
'mobile-twotone',
'money-collect-filled',
'money-collect-outlined',
'money-collect-twotone',
'monitor-outlined',
'more-outlined',
'node-collapse-outlined',
'node-expand-outlined',
'node-index-outlined',
'notification-filled',
'notification-outlined',
'notification-twotone',
'number-outlined',
'one-to-one-outlined',
'ordered-list-outlined',
'paper-clip-outlined',
'partition-outlined',
'pause-circle-filled',
'pause-circle-outlined',
'pause-circle-twotone',
'pause-outlined',
'pay-circle-filled',
'pay-circle-outlined',
'percentage-outlined',
'phone-filled',
'phone-outlined',
'phone-twotone',
'pic-center-outlined',
'pic-left-outlined',
'pic-right-outlined',
'picture-filled',
'picture-outlined',
'picture-twotone',
'pie-chart-filled',
'pie-chart-outlined',
'pie-chart-twotone',
'play-circle-filled',
'play-circle-outlined',
'play-circle-twotone',
'play-square-filled',
'play-square-outlined',
'play-square-twotone',
'plus-circle-filled',
'plus-circle-outlined',
'plus-circle-twotone',
'plus-outlined',
'plus-square-filled',
'plus-square-outlined',
'plus-square-twotone',
'pound-circle-filled',
'pound-circle-outlined',
'pound-circle-twotone',
'pound-outlined',
'poweroff-outlined',
'printer-filled',
'printer-outlined',
'printer-twotone',
'profile-filled',
'profile-outlined',
'profile-twotone',
'project-filled',
'project-outlined',
'project-twotone',
'property-safety-filled',
'property-safety-outlined',
'property-safety-twotone',
'pull-request-outlined',
'pushpin-filled',
'pushpin-outlined',
'pushpin-twotone',
'qq-circle-filled',
'qq-outlined',
'qq-square-filled',
'qrcode-outlined',
'question-circle-filled',
'question-circle-outlined',
'question-circle-twotone',
'question-outlined',
'radar-chart-outlined',
'radius-bottomleft-outlined',
'radius-bottomright-outlined',
'radius-setting-outlined',
'radius-upleft-outlined',
'radius-upright-outlined',
'read-filled',
'read-outlined',
'reconciliation-filled',
'reconciliation-outlined',
'reconciliation-twotone',
'red-envelope-filled',
'red-envelope-outlined',
'red-envelope-twotone',
'reddit-circle-filled',
'reddit-outlined',
'reddit-square-filled',
'redo-outlined',
'reload-outlined',
'rest-filled',
'rest-outlined',
'rest-twotone',
'retweet-outlined',
'right-circle-filled',
'right-circle-outlined',
'right-circle-twotone',
'right-outlined',
'right-square-filled',
'right-square-outlined',
'right-square-twotone',
'rise-outlined',
'robot-filled',
'robot-outlined',
'rocket-filled',
'rocket-outlined',
'rocket-twotone',
'rollback-outlined',
'rotate-left-outlined',
'rotate-right-outlined',
'safety-certificate-filled',
'safety-certificate-outlined',
'safety-certificate-twotone',
'safety-outlined',
'save-filled',
'save-outlined',
'save-twotone',
'scan-outlined',
'schedule-filled',
'schedule-outlined',
'schedule-twotone',
'scissor-outlined',
'search-outlined',
'security-scan-filled',
'security-scan-outlined',
'security-scan-twotone',
'select-outlined',
'send-outlined',
'setting-filled',
'setting-outlined',
'setting-twotone',
'shake-outlined',
'share-alt-outlined',
'shop-filled',
'shop-outlined',
'shop-twotone',
'shopping-cart-outlined',
'shopping-filled',
'shopping-outlined',
'shopping-twotone',
'shrink-outlined',
'signal-filled',
'sisternode-outlined',
'sketch-circle-filled',
'sketch-outlined',
'sketch-square-filled',
'skin-filled',
'skin-outlined',
'skin-twotone',
'skype-filled',
'skype-outlined',
'slack-circle-filled',
'slack-outlined',
'slack-square-filled',
'slack-square-outlined',
'sliders-filled',
'sliders-outlined',
'sliders-twotone',
'small-dash-outlined',
'smile-filled',
'smile-outlined',
'smile-twotone',
'snippets-filled',
'snippets-outlined',
'snippets-twotone',
'solution-outlined',
'sort-ascending-outlined',
'sort-descending-outlined',
'sound-filled',
'sound-outlined',
'sound-twotone',
'split-cells-outlined',
'star-filled',
'star-outlined',
'star-twotone',
'step-backward-filled',
'step-backward-outlined',
'step-forward-filled',
'step-forward-outlined',
'stock-outlined',
'stop-filled',
'stop-outlined',
'stop-twotone',
'strikethrough-outlined',
'subnode-outlined',
'swap-left-outlined',
'swap-outlined',
'swap-right-outlined',
'switcher-filled',
'switcher-outlined',
'switcher-twotone',
'sync-outlined',
'table-outlined',
'tablet-filled',
'tablet-outlined',
'tablet-twotone',
'tag-filled',
'tag-outlined',
'tag-twotone',
'tags-filled',
'tags-outlined',
'tags-twotone',
'taobao-circle-filled',
'taobao-circle-outlined',
'taobao-outlined',
'taobao-square-filled',
'team-outlined',
'thunderbolt-filled',
'thunderbolt-outlined',
'thunderbolt-twotone',
'to-top-outlined',
'tool-filled',
'tool-outlined',
'tool-twotone',
'trademark-circle-filled',
'trademark-circle-outlined',
'trademark-circle-twotone',
'trademark-outlined',
'transaction-outlined',
'translation-outlined',
'trophy-filled',
'trophy-outlined',
'trophy-twotone',
'twitter-circle-filled',
'twitter-outlined',
'twitter-square-filled',
'underline-outlined',
'undo-outlined',
'ungroup-outlined',
'unlock-filled',
'unlock-outlined',
'unlock-twotone',
'unordered-list-outlined',
'up-circle-filled',
'up-circle-outlined',
'up-circle-twotone',
'up-outlined',
'up-square-filled',
'up-square-outlined',
'up-square-twotone',
'upload-outlined',
'usb-filled',
'usb-outlined',
'usb-twotone',
'user-add-outlined',
'user-delete-outlined',
'user-outlined',
'user-switch-outlined',
'usergroup-add-outlined',
'usergroup-delete-outlined',
'verified-outlined',
'vertical-align-bottom-outlined',
'vertical-align-middle-outlined',
'vertical-align-top-outlined',
'vertical-left-outlined',
'vertical-right-outlined',
'video-camera-add-outlined',
'video-camera-filled',
'video-camera-outlined',
'video-camera-twotone',
'wallet-filled',
'wallet-outlined',
'wallet-twotone',
'warning-filled',
'warning-outlined',
'warning-twotone',
'wechat-filled',
'wechat-outlined',
'weibo-circle-filled',
'weibo-circle-outlined',
'weibo-outlined',
'weibo-square-filled',
'weibo-square-outlined',
'whats-app-outlined',
'wifi-outlined',
'windows-filled',
'windows-outlined',
'woman-outlined',
'yahoo-filled',
'yahoo-outlined',
'youtube-filled',
'youtube-outlined',
'yuque-filled',
'yuque-outlined',
'zhihu-circle-filled',
'zhihu-outlined',
'zhihu-square-filled',
'zoom-in-outlined',
'zoom-out-outlined',
],
}

5
src/components/Icon/index.ts

@ -1,7 +1,4 @@
import Icon from './src/Icon.vue'
import SvgIcon from './src/SvgIcon.vue' import SvgIcon from './src/SvgIcon.vue'
import IconPicker from './src/IconPicker.vue' import IconPicker from './src/IconPicker.vue'
export { Icon, IconPicker, SvgIcon } export { IconPicker, SvgIcon }
export default Icon

102
src/components/Icon/src/Icon.vue

@ -1,102 +0,0 @@
<script lang="ts" setup>
import type { CSSProperties } from 'vue'
import { computed, nextTick, onMounted, ref, unref, watch } from 'vue'
import Iconify from '@iconify/iconify'
import SvgIcon from './SvgIcon.vue'
import { isString } from '@/utils/is'
import { propTypes } from '@/utils/propTypes'
defineOptions({ name: 'Icon' })
const props = defineProps({
// icon name
icon: propTypes.string,
// icon color
color: propTypes.string,
// icon size
size: {
type: [String, Number] as PropType<string | number>,
default: 16,
},
spin: propTypes.bool.def(false),
prefix: propTypes.string.def(''),
})
const SVG_END_WITH_FLAG = '|svg'
const elRef = ref<ElRef>(null)
const isSvgIcon = computed(() => props.icon?.endsWith(SVG_END_WITH_FLAG))
const getSvgIcon = computed(() => props.icon.replace(SVG_END_WITH_FLAG, ''))
const getIconRef = computed(() => `${props.prefix ? `${props.prefix}:` : ''}${props.icon}`)
async function update() {
if (unref(isSvgIcon))
return
const el = unref(elRef)
if (!el)
return
await nextTick()
const icon = unref(getIconRef)
if (!icon)
return
const svg = Iconify.renderSVG(icon, {})
if (svg) {
el.textContent = ''
el.appendChild(svg)
}
else {
const span = document.createElement('span')
span.className = 'iconify'
span.dataset.icon = icon
el.textContent = ''
el.appendChild(span)
}
}
const getWrapStyle = computed((): CSSProperties => {
const { size, color } = props
let fs = size
if (isString(size))
fs = Number.parseInt(size, 10)
return {
fontSize: `${fs}px`,
color,
display: 'inline-flex',
}
})
watch(() => props.icon, update, { flush: 'post' })
onMounted(update)
</script>
<template>
<SvgIcon v-if="isSvgIcon" :size="size" :name="getSvgIcon" class="anticon" :class="[$attrs.class]" :spin="spin" />
<span v-else ref="elRef" class="anticon app-iconify" :class="[$attrs.class, spin && 'app-iconify-spin']" :style="getWrapStyle" />
</template>
<style lang="less">
.app-iconify {
display: inline-block;
// vertical-align: middle;
&-spin {
svg {
animation: loadingCircle 1s infinite linear;
}
}
}
span.iconify {
display: block;
min-width: 1em;
min-height: 1em;
background-color: @iconify-bg-color;
border-radius: 100%;
}
</style>

19
src/components/Icon/src/IconPicker.vue

@ -3,9 +3,8 @@ import { ref, watch, watchEffect } from 'vue'
import { Empty, Input, Pagination, Popover } from 'ant-design-vue' import { Empty, Input, Pagination, Popover } from 'ant-design-vue'
import { useDebounceFn } from '@vueuse/core' import { useDebounceFn } from '@vueuse/core'
import svgIcons from 'virtual:svg-icons-names' import svgIcons from 'virtual:svg-icons-names'
import iconsData from '../data/icons.data'
import Icon from './Icon.vue'
import SvgIcon from './SvgIcon.vue' import SvgIcon from './SvgIcon.vue'
import { getIcons } from './icons'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { ScrollContainer } from '@/components/Container' import { ScrollContainer } from '@/components/Container'
@ -31,18 +30,6 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits(['change', 'update:value']) const emit = defineEmits(['change', 'update:value'])
function getIcons() {
const data = iconsData as any
const prefix: string = data?.prefix ?? ''
let result: string[] = []
if (prefix)
result = (data?.icons ?? []).map(item => `${prefix}:${item}`)
else if (Array.isArray(iconsData))
result = iconsData as string[]
return result
}
function getSvgIcons() { function getSvgIcons() {
return svgIcons.map((icon: string) => icon.replace('icon-', '')) return svgIcons.map((icon: string) => icon.replace('icon-', ''))
} }
@ -119,7 +106,7 @@ function handleSearchChange(e: ChangeEvent) {
> >
<!-- <Icon :icon="icon" :prefix="prefix" /> --> <!-- <Icon :icon="icon" :prefix="prefix" /> -->
<SvgIcon v-if="isSvgMode" :name="icon" /> <SvgIcon v-if="isSvgMode" :name="icon" />
<Icon v-else :icon="icon" /> <span v-else :class="icon" />
</li> </li>
</ul> </ul>
</ScrollContainer> </ScrollContainer>
@ -137,7 +124,7 @@ function handleSearchChange(e: ChangeEvent) {
<span v-if="isSvgMode && currentSelect" class="flex cursor-pointer items-center px-2 py-1"> <span v-if="isSvgMode && currentSelect" class="flex cursor-pointer items-center px-2 py-1">
<SvgIcon :name="currentSelect" /> <SvgIcon :name="currentSelect" />
</span> </span>
<Icon v-else :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" /> <span v-else :class="currentSelect || 'i-ion:apps-outline'" class="cursor-pointer px-4 py-2" />
</Popover> </Popover>
</template> </template>
</Input> </Input>

8
src/components/Icon/src/icons.ts

@ -0,0 +1,8 @@
import antDesignIconsJson from '@iconify/json/json/ant-design.json'
/**
* Get global icons, defaulting to ant-design.
*/
export function getIcons() {
return Object.keys(antDesignIconsJson.icons).map(item => `i-${antDesignIconsJson.prefix}:${item}`)
}

3
src/components/Menu/src/components/MenuItemContent.vue

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue'
import { contentProps } from '../props' import { contentProps } from '../props'
import { Icon } from '@/components/Icon'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -20,7 +19,7 @@ const getImg = computed(() => props.item?.img)
<template> <template>
<span :class="`${prefixCls}- flex items-center `"> <span :class="`${prefixCls}- flex items-center `">
<img v-if="getImg" :src="getImg" class="mr-2 h-18px w-18px align-top"> <img v-if="getImg" :src="getImg" class="mr-2 h-18px w-18px align-top">
<Icon v-if="getIcon" :icon="getIcon" :size="18" :class="`${prefixCls}-wrapper__icon mr-2`" /> <span v-if="getIcon" :class="`${prefixCls}-wrapper__icon mr-2 ${getIcon} text-[18px]`" />
{{ getI18nName }} {{ getI18nName }}
</span> </span>
</template> </template>

5
src/components/SimpleMenu/src/SimpleSubMenu.vue

@ -4,7 +4,6 @@ import MenuItem from './components/MenuItem.vue'
import SubMenu from './components/SubMenuItem.vue' import SubMenu from './components/SubMenuItem.vue'
import type { Menu } from '@/router/types' import type { Menu } from '@/router/types'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { Icon } from '@/components/Icon'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -56,7 +55,7 @@ function menuHasChildren(menuTreeItem: Menu): boolean {
<template> <template>
<MenuItem v-if="!menuHasChildren(item) && getShowMenu" :name="item.path" v-bind="$props" :class="getLevelClass"> <MenuItem v-if="!menuHasChildren(item) && getShowMenu" :name="item.path" v-bind="$props" :class="getLevelClass">
<img v-if="getImg" :src="getImg" class="h-16px w-16px align-top"> <img v-if="getImg" :src="getImg" class="h-16px w-16px align-top">
<Icon v-if="getIcon" :icon="getIcon" :size="16" /> <span v-if="getIcon" :class="getIcon" class="text-[16px]" />
<div v-if="collapsedShowTitle && getIsCollapseParent" class="collapse-title mt-1"> <div v-if="collapsedShowTitle && getIsCollapseParent" class="collapse-title mt-1">
{{ getI18nName }} {{ getI18nName }}
</div> </div>
@ -75,7 +74,7 @@ function menuHasChildren(menuTreeItem: Menu): boolean {
> >
<template #title> <template #title>
<img v-if="getImg" :src="getImg" class="h-16px w-16px align-top"> <img v-if="getImg" :src="getImg" class="h-16px w-16px align-top">
<Icon v-if="getIcon" :icon="getIcon" :size="16" /> <span v-if="getIcon" :class="getIcon" class="text-[16px]" />
<div v-if="collapsedShowTitle && getIsCollapseParent" class="collapse-title mt-2"> <div v-if="collapsedShowTitle && getIsCollapseParent" class="collapse-title mt-2">
{{ getI18nName }} {{ getI18nName }}

5
src/components/SimpleMenu/src/components/SubMenuItem.vue

@ -8,7 +8,6 @@ import { useSimpleRootMenuContext } from './useSimpleMenuContext'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { CollapseTransition } from '@/components/Transition' import { CollapseTransition } from '@/components/Transition'
import { Icon } from '@/components/Icon'
import { isBoolean, isObject } from '@/utils/is' import { isBoolean, isObject } from '@/utils/is'
import { mitt } from '@/utils/mitt' import { mitt } from '@/utils/mitt'
@ -240,7 +239,7 @@ provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
<template v-if="!getCollapse"> <template v-if="!getCollapse">
<div :class="`${prefixCls}-submenu-title`" :style="getItemStyle" @click.stop="handleClick"> <div :class="`${prefixCls}-submenu-title`" :style="getItemStyle" @click.stop="handleClick">
<slot name="title" /> <slot name="title" />
<Icon icon="eva:arrow-ios-downward-outline" :size="14" :class="`${prefixCls}-submenu-title-icon`" /> <span class="i-eva:arrow-ios-downward-outline text-[14px]" :class="`${prefixCls}-submenu-title-icon`" />
</div> </div>
<CollapseTransition> <CollapseTransition>
<ul v-show="state.opened" :class="prefixCls"> <ul v-show="state.opened" :class="prefixCls">
@ -269,7 +268,7 @@ provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
> >
<slot name="title" /> <slot name="title" />
</div> </div>
<Icon v-if="getParentSubMenu" icon="eva:arrow-ios-downward-outline" :size="14" :class="`${prefixCls}-submenu-title-icon`" /> <span v-if="getParentSubMenu" class="i-eva:arrow-ios-downward-outline text-[14px]" :class="`${prefixCls}-submenu-title-icon`" />
</div> </div>
<!-- eslint-disable-next-line --> <!-- eslint-disable-next-line -->
<template #content v-show="state.opened"> <template #content v-show="state.opened">

5
src/components/Table/src/components/TableAction.vue

@ -7,7 +7,6 @@ import { Divider, Tooltip } from 'ant-design-vue'
import { useTableContext } from '../hooks/useTableContext' import { useTableContext } from '../hooks/useTableContext'
import { ACTION_COLUMN_FLAG } from '../const' import { ACTION_COLUMN_FLAG } from '../const'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { Icon } from '@/components/Icon'
import type { ActionItem, TableActionType } from '@/components/Table' import type { ActionItem, TableActionType } from '@/components/Table'
import { PopConfirmButton } from '@/components/Button' import { PopConfirmButton } from '@/components/Button'
import { Dropdown } from '@/components/Dropdown' import { Dropdown } from '@/components/Dropdown'
@ -119,14 +118,14 @@ function onCellClick(e: MouseEvent) {
<template v-for="(action, index) in getActions" :key="`${index}-${action.label}`"> <template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
<Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)"> <Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
<PopConfirmButton v-bind="action"> <PopConfirmButton v-bind="action">
<Icon v-if="action.icon" :icon="action.icon" :class="{ 'mr-1': !!action.label }" /> <span v-if="action.icon" :class="{ 'mr-1': !!action.label, [action.icon]: action.icon }" />
<template v-if="action.label"> <template v-if="action.label">
{{ action.label }} {{ action.label }}
</template> </template>
</PopConfirmButton> </PopConfirmButton>
</Tooltip> </Tooltip>
<PopConfirmButton v-else v-bind="action"> <PopConfirmButton v-else v-bind="action">
<Icon v-if="action.icon" :icon="action.icon" :class="{ 'mr-1': !!action.label }" /> <span v-if="action.icon" :class="{ 'mr-1': !!action.label, [action.icon]: action.icon }" />
<template v-if="action.label"> <template v-if="action.label">
{{ action.label }} {{ action.label }}
</template> </template>

9
src/components/Table/src/components/settings/ColumnSetting.vue

@ -8,7 +8,6 @@ import Sortablejs from 'sortablejs'
import type Sortable from 'sortablejs' import type Sortable from 'sortablejs'
import type { BasicColumn, BasicTableProps, ColumnChangeParam } from '../../types/table' import type { BasicColumn, BasicTableProps, ColumnChangeParam } from '../../types/table'
import { useTableContext } from '../../hooks/useTableContext' import { useTableContext } from '../../hooks/useTableContext'
import { Icon } from '@/components/Icon'
import { ScrollContainer } from '@/components/Container' import { ScrollContainer } from '@/components/Container'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -350,8 +349,8 @@ function updateSortOption(column: BasicColumn) {
<template #title> <template #title>
{{ t('component.table.settingFixedLeft') }} {{ t('component.table.settingFixedLeft') }}
</template> </template>
<Icon <span
icon="line-md:arrow-align-left" class="i-line-md:arrow-align-left"
:class="[ :class="[
`${prefixCls}__fixed-left`, `${prefixCls}__fixed-left`,
{ {
@ -367,8 +366,8 @@ function updateSortOption(column: BasicColumn) {
<template #title> <template #title>
{{ t('component.table.settingFixedRight') }} {{ t('component.table.settingFixedRight') }}
</template> </template>
<Icon <span
icon="line-md:arrow-align-left" class="i-line-md:arrow-align-left"
:class="[ :class="[
`${prefixCls}__fixed-right`, `${prefixCls}__fixed-right`,
{ {

3
src/components/Table/src/hooks/useRender.ts

@ -4,7 +4,6 @@ import { Button, Tag } from 'ant-design-vue'
import TableImg from '../components/TableImg.vue' import TableImg from '../components/TableImg.vue'
import { isArray, isString } from '@/utils/is' import { isArray, isString } from '@/utils/is'
import { DictTag } from '@/components/DictTag' import { DictTag } from '@/components/DictTag'
import { Icon } from '@/components/Icon'
import { JsonPreview } from '@/components/CodeEditor' import { JsonPreview } from '@/components/CodeEditor'
export const useRender = { export const useRender = {
@ -107,7 +106,7 @@ export const useRender = {
*/ */
renderIcon: (text: string) => { renderIcon: (text: string) => {
if (text) if (text)
return h(Icon, { icon: text }) return h('span', { class: text })
}, },
/** /**
* 使JsonPreview组件 便JSON * 使JsonPreview组件 便JSON

5
src/components/Tree/src/TreeIcon.ts

@ -2,13 +2,12 @@ import type { FunctionalComponent, VNode } from 'vue'
import { h } from 'vue' import { h } from 'vue'
import { isString } from '@/utils/is' import { isString } from '@/utils/is'
import { Icon } from '@/components/Icon'
export const TreeIcon: FunctionalComponent = ({ icon }: { icon: VNode | string | undefined }) => { export const TreeIcon: FunctionalComponent = ({ icon }: { icon: VNode | string | undefined }) => {
if (!icon) if (!icon)
return null return null
if (isString(icon)) if (isString(icon))
return h(Icon, { icon, class: 'mr-1' }) return h('span', { class: `mr-1 ${icon}` })
return h(Icon) return h(icon)
} }

3
src/components/Tree/src/components/TreeHeader.vue

@ -4,7 +4,6 @@ import type { MenuProps } from 'ant-design-vue'
import { Dropdown, InputSearch, Menu, MenuDivider } from 'ant-design-vue' import { Dropdown, InputSearch, Menu, MenuDivider } from 'ant-design-vue'
import { useDebounceFn } from '@vueuse/core' import { useDebounceFn } from '@vueuse/core'
import { ToolbarEnum } from '../types/tree' import { ToolbarEnum } from '../types/tree'
import { Icon } from '@/components/Icon'
import { BasicTitle } from '@/components/Basic' import { BasicTitle } from '@/components/Basic'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { createBEM } from '@/utils/bem' import { createBEM } from '@/utils/bem'
@ -165,7 +164,7 @@ watch(
<InputSearch v-model:value="searchValue" :placeholder="t('common.searchText')" allow-clear /> <InputSearch v-model:value="searchValue" :placeholder="t('common.searchText')" allow-clear />
</div> </div>
<Dropdown v-if="toolbar" @click.prevent> <Dropdown v-if="toolbar" @click.prevent>
<Icon icon="ion:ellipsis-vertical" /> <span class="i-ion:ellipsis-vertical" />
<template #overlay> <template #overlay>
<Menu @click="handleMenuClick"> <Menu @click="handleMenuClick">
<template v-for="item in toolbarList" :key="item.value"> <template v-for="item in toolbarList" :key="item.value">

3
src/components/Upload/src/BasicUpload.vue

@ -5,7 +5,6 @@ import { omit } from 'lodash-es'
import { uploadContainerProps } from './props' import { uploadContainerProps } from './props'
import UploadModal from './UploadModal.vue' import UploadModal from './UploadModal.vue'
import UploadPreviewModal from './UploadPreviewModal.vue' import UploadPreviewModal from './UploadPreviewModal.vue'
import { Icon } from '@/components/Icon'
import { useModal } from '@/components/Modal' import { useModal } from '@/components/Modal'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { isArray } from '@/utils/is' import { isArray } from '@/utils/is'
@ -82,7 +81,7 @@ function handlePreviewDelete(url: string) {
</template> </template>
</template> </template>
<a-button @click="openPreviewModal"> <a-button @click="openPreviewModal">
<Icon icon="bi:eye" /> <span class="i-bi:eye" />
<template v-if="fileList.length && showPreviewNumber"> <template v-if="fileList.length && showPreviewNumber">
{{ fileList.length }} {{ fileList.length }}
</template> </template>

3
src/layouts/default/header/components/ErrorAction.vue

@ -2,7 +2,6 @@
import { computed } from 'vue' import { computed } from 'vue'
import { Badge, Tooltip } from 'ant-design-vue' import { Badge, Tooltip } from 'ant-design-vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { Icon } from '@/components/Icon'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useErrorLogStore } from '@/store/modules/errorLog' import { useErrorLogStore } from '@/store/modules/errorLog'
@ -26,7 +25,7 @@ function handleToErrorList() {
<template> <template>
<Tooltip :title="t('layout.header.tooltipErrorLog')" placement="bottom" :mouse-enter-delay="0.5" @click="handleToErrorList"> <Tooltip :title="t('layout.header.tooltipErrorLog')" placement="bottom" :mouse-enter-delay="0.5" @click="handleToErrorList">
<Badge :count="getCount" :offset="[0, 10]" :overflow-count="99"> <Badge :count="getCount" :offset="[0, 10]" :overflow-count="99">
<Icon icon="ion:bug-outline" /> <span class="i-ion:bug-outline" />
</Badge> </Badge>
</Tooltip> </Tooltip>
</template> </template>

4
src/layouts/default/header/components/user-dropdown/DropMenuItem.vue

@ -1,8 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { MenuItem } from 'ant-design-vue' import { MenuItem } from 'ant-design-vue'
import { computed, getCurrentInstance } from 'vue' import { computed, getCurrentInstance } from 'vue'
import { Icon } from '@/components/Icon'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
defineOptions({ name: 'DropdownMenuItem' }) defineOptions({ name: 'DropdownMenuItem' })
@ -21,7 +19,7 @@ const itemKey = computed(() => props.key || instance?.vnode?.props?.key)
<template> <template>
<MenuItem :key="itemKey"> <MenuItem :key="itemKey">
<span class="flex items-center"> <span class="flex items-center">
<Icon :icon="icon" class="mr-1" /> <span :class="icon" class="mr-1" />
<span>{{ text }}</span> <span>{{ text }}</span>
</span> </span>
</MenuItem> </MenuItem>

6
src/layouts/default/header/components/user-dropdown/index.vue

@ -83,13 +83,13 @@ function handleMenuClick(e: MenuInfo) {
<template #overlay> <template #overlay>
<Menu @click="handleMenuClick"> <Menu @click="handleMenuClick">
<MenuItem key="profile" :text="t('layout.header.accountCenter')" icon="ion:person-outline" /> <MenuItem key="profile" :text="t('layout.header.accountCenter')" icon="i-ion:person-outline" />
<MenuDivider v-if="getShowDoc" /> <MenuDivider v-if="getShowDoc" />
<MenuItem <MenuItem
v-if="getUseLockPage" key="lock" :text="t('layout.header.tooltipLock')" v-if="getUseLockPage" key="lock" :text="t('layout.header.tooltipLock')"
icon="ion:lock-closed-outline" icon="i-ion:lock-closed-outline"
/> />
<MenuItem key="logout" :text="t('layout.header.dropdownItemLoginOut')" icon="ion:power-outline" /> <MenuItem key="logout" :text="t('layout.header.dropdownItemLoginOut')" icon="i-ion:power-outline" />
</Menu> </Menu>
</template> </template>
</Dropdown> </Dropdown>

3
src/layouts/default/setting/index.vue

@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import SettingDrawer from './SettingDrawer' import SettingDrawer from './SettingDrawer'
import { Icon } from '@/components/Icon'
import { useDrawer } from '@/components/Drawer' import { useDrawer } from '@/components/Drawer'
@ -11,7 +10,7 @@ const [register, { openDrawer }] = useDrawer()
<template> <template>
<div @click="openDrawer(true)"> <div @click="openDrawer(true)">
<Icon class="h-4 w-4" icon="ion:settings-outline" /> <span class="i-ion:settings-outline h-4 w-4" />
<SettingDrawer @register="register" /> <SettingDrawer @register="register" />
</div> </div>
</template> </template>

11
src/layouts/default/sider/MixSider.vue

@ -9,7 +9,6 @@ import { useDragLine } from './useLayoutSider'
import type { Menu } from '@/router/types' import type { Menu } from '@/router/types'
import { ScrollContainer } from '@/components/Container' import { ScrollContainer } from '@/components/Container'
import { SimpleMenu } from '@/components/SimpleMenu' import { SimpleMenu } from '@/components/SimpleMenu'
import { Icon } from '@/components/Icon'
import { AppLogo } from '@/components/Application' import { AppLogo } from '@/components/Application'
import { useMenuSetting } from '@/hooks/setting/useMenuSetting' import { useMenuSetting } from '@/hooks/setting/useMenuSetting'
import { usePermissionStore } from '@/store/modules/permission' import { usePermissionStore } from '@/store/modules/permission'
@ -260,10 +259,10 @@ onClickOutside(wrap, () => {
:src="item.img" :src="item.img"
:class="[`${prefixCls}-module__icon`, getCollapsed ? 'w-16px h-16px' : 'w-20px h-20px']" :class="[`${prefixCls}-module__icon`, getCollapsed ? 'w-16px h-16px' : 'w-20px h-20px']"
> >
<Icon <span
v-else v-else
:class="`${prefixCls}-module__icon`" :size="getCollapsed ? 16 : 20" :class="`${prefixCls}-module__icon ${item.icon || (item.meta && item.meta.icon)}`"
:icon="item.icon || (item.meta && item.meta.icon)" :style="{ fontSize: `${getCollapsed ? 16 : 20}px` }"
/> />
<p :class="`${prefixCls}-module__name`"> <p :class="`${prefixCls}-module__name`">
{{ t(item.name) }} {{ t(item.name) }}
@ -282,8 +281,8 @@ onClickOutside(wrap, () => {
]" ]"
> >
<span class="text"> {{ title }}</span> <span class="text"> {{ title }}</span>
<Icon <span
:size="16" :icon="getMixSideFixed ? 'ri:pushpin-2-fill' : 'ri:pushpin-2-line'" class="pushpin" :class="getMixSideFixed ? 'i-ri:pushpin-2-fill' : 'i-ri:pushpin-2-line'"
@click="handleFixedMenu" @click="handleFixedMenu"
/> />
</div> </div>

3
src/layouts/default/tabs/components/TabContent.vue

@ -5,7 +5,6 @@ import { computed, unref } from 'vue'
import type { TabContentProps } from '../types' import type { TabContentProps } from '../types'
import { useTabDropdown } from '../useTabDropdown' import { useTabDropdown } from '../useTabDropdown'
import { Dropdown } from '@/components/Dropdown' import { Dropdown } from '@/components/Dropdown'
import { Icon } from '@/components/Icon'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -50,7 +49,7 @@ function handleContext(e) {
<span class="ml-1">{{ getTitle }}</span> <span class="ml-1">{{ getTitle }}</span>
</div> </div>
<span v-else :class="`${prefixCls}__extra-quick`" @click="handleContext"> <span v-else :class="`${prefixCls}__extra-quick`" @click="handleContext">
<Icon icon="ion:chevron-down" /> <span class="i-ion:chevron-down" />
</span> </span>
</Dropdown> </Dropdown>
</template> </template>

6
src/router/routes/modules/dashboard.ts

@ -11,7 +11,7 @@ const dashboard: AppRouteModule = {
redirect: '/dashboard/analysis', redirect: '/dashboard/analysis',
meta: { meta: {
orderNo: 10, orderNo: 10,
icon: 'ant-design:home-outlined', icon: 'i-ant-design:home-outlined',
title: t('routes.dashboard.dashboard'), title: t('routes.dashboard.dashboard'),
}, },
children: [ children: [
@ -22,7 +22,7 @@ const dashboard: AppRouteModule = {
meta: { meta: {
// affix: true, // affix: true,
title: t('routes.dashboard.analysis'), title: t('routes.dashboard.analysis'),
icon: 'ant-design:bar-chart-outlined', icon: 'i-ant-design:bar-chart-outlined',
}, },
}, },
{ {
@ -31,7 +31,7 @@ const dashboard: AppRouteModule = {
component: () => import('@/views/dashboard/workbench/index.vue'), component: () => import('@/views/dashboard/workbench/index.vue'),
meta: { meta: {
title: t('routes.dashboard.workbench'), title: t('routes.dashboard.workbench'),
icon: 'ant-design:appstore-outlined', icon: 'i-ant-design:appstore-outlined',
}, },
}, },
], ],

24
src/views/base/profile/AccountBind.vue

@ -1,11 +1,29 @@
<script lang="ts" setup> <script lang="ts" setup>
import { List } from 'ant-design-vue' import { List } from 'ant-design-vue'
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { accountBindList } from './data' import type { ListItem } from './data'
import { Icon } from '@/components/Icon'
import { CollapseContainer } from '@/components/Container/index' import { CollapseContainer } from '@/components/Container/index'
import { getUserProfileApi } from '@/api/base/profile' import { getUserProfileApi } from '@/api/base/profile'
const accountBindList: ListItem[] = [
{
key: '20',
title: '钉钉',
description: '当前未绑定钉钉账号',
extra: '绑定',
avatar: 'i-ri:dingding-fill',
color: '#2eabff',
},
{
key: '30',
title: '企业微信',
description: '当前未绑定企业微信',
extra: '绑定',
avatar: 'i-ri:wechat-line',
color: '#2eabff',
},
]
async function init() { async function init() {
const userInfo = await getUserProfileApi() const userInfo = await getUserProfileApi()
// TODO // TODO
@ -32,7 +50,7 @@ onMounted(async () => {
<List.Item> <List.Item>
<List.Item.Meta> <List.Item.Meta>
<template #avatar> <template #avatar>
<Icon v-if="item.avatar" class="text-4xl" :icon="item.avatar" :color="item.color" /> <span v-if="item.avatar" class="text-4xl" :class="item.avatar" :style="{ color: item.color }" />
</template> </template>
<template #title> <template #title>
{{ item.title }} {{ item.title }}

20
src/views/base/profile/data.ts

@ -100,26 +100,6 @@ export const secureSettingList: ListItem[] = [
}, },
] ]
// 账号绑定 list
export const accountBindList: ListItem[] = [
{
key: '20',
title: '钉钉',
description: '当前未绑定钉钉账号',
extra: '绑定',
avatar: 'ri:dingding-fill',
color: '#2eabff',
},
{
key: '30',
title: '企业微信',
description: '当前未绑定企业微信',
extra: '绑定',
avatar: 'ri:wechat-line',
color: '#2eabff',
},
]
// 新消息通知 list // 新消息通知 list
export const msgNotifyList: ListItem[] = [ export const msgNotifyList: ListItem[] = [
{ {

4
src/views/dashboard/analysis/components/GrowCard.vue

@ -2,7 +2,7 @@
import { Card, Tag } from 'ant-design-vue' import { Card, Tag } from 'ant-design-vue'
import { growCardList } from '../data' import { growCardList } from '../data'
import { CountTo } from '@/components/CountTo' import { CountTo } from '@/components/CountTo'
import { Icon } from '@/components/Icon' import { SvgIcon } from '@/components/Icon'
defineProps({ defineProps({
loading: { loading: {
@ -28,7 +28,7 @@ defineProps({
<div class="flex items-center justify-between px-4 py-4"> <div class="flex items-center justify-between px-4 py-4">
<CountTo prefix="$" :start-val="1" :end-val="item.value" class="text-2xl" /> <CountTo prefix="$" :start-val="1" :end-val="item.value" class="text-2xl" />
<Icon :icon="item.icon" :size="40" /> <SvgIcon :name="item.icon" :size="40" />
</div> </div>
<div class="flex justify-between p-2 px-4"> <div class="flex justify-between p-2 px-4">

8
src/views/dashboard/analysis/data.ts

@ -10,7 +10,7 @@ export interface GrowCardItem {
export const growCardList: GrowCardItem[] = [ export const growCardList: GrowCardItem[] = [
{ {
title: '访问数', title: '访问数',
icon: 'visit-count|svg', icon: 'visit-count',
value: 2000, value: 2000,
total: 120000, total: 120000,
color: 'green', color: 'green',
@ -18,7 +18,7 @@ export const growCardList: GrowCardItem[] = [
}, },
{ {
title: '成交额', title: '成交额',
icon: 'total-sales|svg', icon: 'total-sales',
value: 20000, value: 20000,
total: 500000, total: 500000,
color: 'blue', color: 'blue',
@ -26,7 +26,7 @@ export const growCardList: GrowCardItem[] = [
}, },
{ {
title: '下载数', title: '下载数',
icon: 'download-count|svg', icon: 'download-count',
value: 8000, value: 8000,
total: 120000, total: 120000,
color: 'orange', color: 'orange',
@ -34,7 +34,7 @@ export const growCardList: GrowCardItem[] = [
}, },
{ {
title: '成交数', title: '成交数',
icon: 'transaction|svg', icon: 'transaction',
value: 5000, value: 5000,
total: 50000, total: 50000,
color: 'purple', color: 'purple',

4
src/views/dashboard/workbench/components/DynamicInfo.vue

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Card, List } from 'ant-design-vue' import { Card, List } from 'ant-design-vue'
import { dynamicInfoItems } from './data' import { dynamicInfoItems } from './data'
import { Icon } from '@/components/Icon' import { SvgIcon } from '@/components/Icon'
const ListItem = List.Item const ListItem = List.Item
const ListItemMeta = List.Item.Meta const ListItemMeta = List.Item.Meta
@ -24,7 +24,7 @@ const ListItemMeta = List.Item.Meta
<!-- eslint-disable-next-line --> <!-- eslint-disable-next-line -->
<template #title> {{ item.name }} <span v-html="item.desc"> </span> </template> <template #title> {{ item.name }} <span v-html="item.desc"> </span> </template>
<template #avatar> <template #avatar>
<Icon :icon="item.avatar" :size="30" /> <SvgIcon :name="item.avatar" :size="30" />
</template> </template>
</ListItemMeta> </ListItemMeta>
</ListItem> </ListItem>

64
src/views/dashboard/workbench/components/ProjectCard.vue

@ -1,7 +1,65 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Card, CardGrid } from 'ant-design-vue' import { Card, CardGrid } from 'ant-design-vue'
import { groupItems } from './data'
import { Icon } from '@/components/Icon' interface GroupItem {
title: string
icon: string
color: string
desc: string
date: string
group: string
}
const groupItems: GroupItem[] = [
{
title: 'Github',
icon: 'i-carbon:logo-github',
color: '',
desc: '不要等待机会,而要创造机会。',
group: '开源组',
date: '2021-04-01',
},
{
title: 'Vue',
icon: 'i-ion:logo-vue',
color: '#3fb27f',
desc: '现在的你决定将来的你。',
group: '算法组',
date: '2021-04-01',
},
{
title: 'Html5',
icon: 'i-ion:logo-html5',
color: '#e18525',
desc: '没有什么才能比努力更重要。',
group: '上班摸鱼',
date: '2021-04-01',
},
{
title: 'Angular',
icon: 'i-ion:logo-angular',
color: '#bf0c2c',
desc: '热情和欲望可以突破一切难关。',
group: 'UI',
date: '2021-04-01',
},
{
title: 'React',
icon: 'i-bx:bxl-react',
color: '#00d8ff',
desc: '健康的身体是实现目标的基石。',
group: '技术牛',
date: '2021-04-01',
},
{
title: 'Js',
icon: 'i-ion:logo-javascript',
color: '#EBD94E',
desc: '路是走出来的,而不是空想出来的。',
group: '架构组',
date: '2021-04-01',
},
]
</script> </script>
<template> <template>
@ -14,7 +72,7 @@ import { Icon } from '@/components/Icon'
<CardGrid v-for="item in groupItems" :key="item.title" class="!w-full !md:w-1/3"> <CardGrid v-for="item in groupItems" :key="item.title" class="!w-full !md:w-1/3">
<span class="flex"> <span class="flex">
<Icon :icon="item.icon" :color="item.color" size="30" /> <span :class="item.icon" :style="{ color: item.color }" class="text-[30px]" />
<span class="ml-4 text-lg">{{ item.title }}</span> <span class="ml-4 text-lg">{{ item.title }}</span>
</span> </span>
<div class="text-secondary mt-2 h-10 flex"> <div class="text-secondary mt-2 h-10 flex">

43
src/views/dashboard/workbench/components/QuickNav.vue

@ -1,14 +1,51 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Card, CardGrid } from 'ant-design-vue' import { Card, CardGrid } from 'ant-design-vue'
import { navItems } from './data'
import { Icon } from '@/components/Icon' interface NavItem {
title: string
icon: string
color: string
}
const navItems: NavItem[] = [
{
title: '首页',
icon: 'i-ion:home-outline',
color: '#1fdaca',
},
{
title: '仪表盘',
icon: 'i-ion:grid-outline',
color: '#bf0c2c',
},
{
title: '组件',
icon: 'i-ion:layers-outline',
color: '#e18525',
},
{
title: '系统管理',
icon: 'i-ion:settings-outline',
color: '#3fb27f',
},
{
title: '权限管理',
icon: 'i-ion:key-outline',
color: '#4daf1bc9',
},
{
title: '图表',
icon: 'i-ion:bar-chart-outline',
color: '#00d8ff',
},
]
</script> </script>
<template> <template>
<Card title="快捷导航"> <Card title="快捷导航">
<CardGrid v-for="item in navItems" :key="item.title"> <CardGrid v-for="item in navItems" :key="item.title">
<span class="flex flex-col items-center"> <span class="flex flex-col items-center">
<Icon :icon="item.icon" :color="item.color" size="20" /> <span :class="item.icon" :style="{ color: item.color }" class="text-[20px]" />
<span class="text-md mt-2 truncate">{{ item.title }}</span> <span class="text-md mt-2 truncate">{{ item.title }}</span>
</span> </span>
</CardGrid> </CardGrid>

115
src/views/dashboard/workbench/components/data.ts

@ -1,18 +1,3 @@
interface GroupItem {
title: string
icon: string
color: string
desc: string
date: string
group: string
}
interface NavItem {
title: string
icon: string
color: string
}
interface DynamicInfoItem { interface DynamicInfoItem {
avatar: string avatar: string
name: string name: string
@ -20,137 +5,53 @@ interface DynamicInfoItem {
desc: string desc: string
} }
export const navItems: NavItem[] = [
{
title: '首页',
icon: 'ion:home-outline',
color: '#1fdaca',
},
{
title: '仪表盘',
icon: 'ion:grid-outline',
color: '#bf0c2c',
},
{
title: '组件',
icon: 'ion:layers-outline',
color: '#e18525',
},
{
title: '系统管理',
icon: 'ion:settings-outline',
color: '#3fb27f',
},
{
title: '权限管理',
icon: 'ion:key-outline',
color: '#4daf1bc9',
},
{
title: '图表',
icon: 'ion:bar-chart-outline',
color: '#00d8ff',
},
]
export const dynamicInfoItems: DynamicInfoItem[] = [ export const dynamicInfoItems: DynamicInfoItem[] = [
{ {
avatar: 'dynamic-avatar-1|svg', avatar: 'dynamic-avatar-1',
name: '威廉', name: '威廉',
date: '刚刚', date: '刚刚',
desc: '在 <a>开源组</a> 创建了项目 <a>Vue</a>', desc: '在 <a>开源组</a> 创建了项目 <a>Vue</a>',
}, },
{ {
avatar: 'dynamic-avatar-2|svg', avatar: 'dynamic-avatar-2',
name: '艾文', name: '艾文',
date: '1个小时前', date: '1个小时前',
desc: '关注了 <a>威廉</a> ', desc: '关注了 <a>威廉</a> ',
}, },
{ {
avatar: 'dynamic-avatar-3|svg', avatar: 'dynamic-avatar-3',
name: '克里斯', name: '克里斯',
date: '1天前', date: '1天前',
desc: '发布了 <a>个人动态</a> ', desc: '发布了 <a>个人动态</a> ',
}, },
{ {
avatar: 'dynamic-avatar-4|svg', avatar: 'dynamic-avatar-4',
name: 'XingyuV', name: 'XingyuV',
date: '2天前', date: '2天前',
desc: '发表文章 <a>如何编写一个Vite插件</a> ', desc: '发表文章 <a>如何编写一个Vite插件</a> ',
}, },
{ {
avatar: 'dynamic-avatar-5|svg', avatar: 'dynamic-avatar-5',
name: '皮特', name: '皮特',
date: '3天前', date: '3天前',
desc: '回复了 <a>杰克</a> 的问题 <a>如何进行项目优化?</a>', desc: '回复了 <a>杰克</a> 的问题 <a>如何进行项目优化?</a>',
}, },
{ {
avatar: 'dynamic-avatar-6|svg', avatar: 'dynamic-avatar-6',
name: '杰克', name: '杰克',
date: '1周前', date: '1周前',
desc: '关闭了问题 <a>如何运行项目</a> ', desc: '关闭了问题 <a>如何运行项目</a> ',
}, },
{ {
avatar: 'dynamic-avatar-1|svg', avatar: 'dynamic-avatar-1',
name: '威廉', name: '威廉',
date: '1周前', date: '1周前',
desc: '发布了 <a>个人动态</a> ', desc: '发布了 <a>个人动态</a> ',
}, },
{ {
avatar: 'dynamic-avatar-1|svg', avatar: 'dynamic-avatar-1',
name: '威廉', name: '威廉',
date: '2021-04-01 20:00', date: '2021-04-01 20:00',
desc: '推送了代码到 <a>Github</a>', desc: '推送了代码到 <a>Github</a>',
}, },
] ]
export const groupItems: GroupItem[] = [
{
title: 'Github',
icon: 'carbon:logo-github',
color: '',
desc: '不要等待机会,而要创造机会。',
group: '开源组',
date: '2021-04-01',
},
{
title: 'Vue',
icon: 'ion:logo-vue',
color: '#3fb27f',
desc: '现在的你决定将来的你。',
group: '算法组',
date: '2021-04-01',
},
{
title: 'Html5',
icon: 'ion:logo-html5',
color: '#e18525',
desc: '没有什么才能比努力更重要。',
group: '上班摸鱼',
date: '2021-04-01',
},
{
title: 'Angular',
icon: 'ion:logo-angular',
color: '#bf0c2c',
desc: '热情和欲望可以突破一切难关。',
group: 'UI',
date: '2021-04-01',
},
{
title: 'React',
icon: 'bx:bxl-react',
color: '#00d8ff',
desc: '健康的身体是实现目标的基石。',
group: '技术牛',
date: '2021-04-01',
},
{
title: 'Js',
icon: 'ion:logo-javascript',
color: '#EBD94E',
desc: '路是走出来的,而不是空想出来的。',
group: '架构组',
date: '2021-04-01',
},
]

13
uno.config.ts

@ -1,13 +1,24 @@
import { defineConfig, presetTypography, presetUno } from 'unocss' import { defineConfig, presetIcons, presetTypography, presetUno } from 'unocss'
import { getIcons } from './src/components/Icon/src/icons'
export default defineConfig({ export default defineConfig({
presets: [ presets: [
presetUno(), presetUno(),
presetTypography(), presetTypography(),
presetIcons({
extraProperties: {
'font-size': '16px',
'display': 'inline-block',
'vertical-align': 'middle',
},
}),
], ],
theme: { theme: {
colors: { colors: {
primary: '#0960bd', primary: '#0960bd',
}, },
}, },
safelist: [
...getIcons(),
],
}) })