添加bar组件

This commit is contained in:
2025-11-05 17:34:23 +08:00
parent 7314f23529
commit 3cb63ffca7
223 changed files with 23185 additions and 2 deletions

View File

@@ -0,0 +1,84 @@
## 0.4.12025-10-19
- fix: 修复上次因改使用官方svg导致web,app不显示问题
## 0.4.02025-10-15
-fix: 修复uniapp cli 小程序css depp报错问题
## 0.3.92025-10-15
- fix: 去掉版本判断
## 0.3.82025-09-22
- feat: 针对hbx4.81优化
## 0.3.72025-08-31
- fix: 修复uniapp 鸿蒙next不显示的问题
## 0.3.62025-07-16
- fix 因增加依赖,没有主动下载导致报错
- fix 修复鸿蒙加载文件失败的问题
## 0.3.52025-07-15
- chore 更新文档
## 0.3.42025-07-14
- chore: 更新文档
## 0.3.32025-07-14
- feat: ios使用真的加载json的方式
## 0.3.22025-07-08
- fix: 支付福小程序报错问题
## 0.3.12025-06-30
- fix: 微信小程序svg不显示的问题
## 0.3.02025-06-26
- chore: 更新文档
## 0.2.92025-05-30
- fix: 修复uniapp自定义图标的问题
## 0.2.82025-05-30
- fix: 修复自定义图标的问题
## 0.2.72025-05-10
- feat: 增加组件提示
## 0.2.62025-04-22
- feat: 颜色改为可选
## 0.2.52025-04-21
- chore: 更新文档
## 0.2.42025-04-21
- feat: 兼容鸿蒙next
## 0.2.32025-04-09
- feat: 如果是字体字符则直接显示
## 0.2.22025-03-20
- feat: 如果name不为字符字会报错加强处理
## 0.2.12025-03-06
- chore: 更新文档
## 0.2.02025-02-08
- chore: 更新文档
## 0.1.92025-02-07
- fix: 去掉app端字体默认宽度
## 0.1.82025-01-19
- fix: 修复nvue加载字体文体问题
## 0.1.72025-01-13
- chore: 去掉多余判断
## 0.1.62025-01-13
- fix: 修复因json文件错误导致ios报错问题
## 0.1.52025-01-02
- feat: hbx4.44会有css深度选择器报黄的问题
## 0.1.42025-01-02
- feat: uniapp x app 取消文本默认大小
## 0.1.32025-01-01
- feat: 增加lStyle
## 0.1.22024-11-22
- chore: 更新到hbx4.35
## 0.1.12024-10-23
- chore: 更新到hbx4.31
## 0.1.02024-10-07
- fix: ios 默认大小
## 0.0.92024-10-06
- fix: name有大写的情况
## 0.0.82024-09-29
- chore: 更新 非uvue app size使用css变量
## 0.0.72024-07-23
- fix: 更新 vue2 使用方法
## 0.0.62024-07-22
- fix: 修复vue3 h5不显示的问题
## 0.0.52024-07-22
- fix: 修复vue2小程序class不显示的问题
## 0.0.42024-07-21
- feat: 兼容uniappx
## 0.0.32023-09-05
- chore: 更新文档
- feat: 默认使用官方API
## 0.0.22023-08-13
- chore: 更新文档
## 0.0.12023-08-13
- 初次上传 可能存在BUG

View File

@@ -0,0 +1,21 @@
// $prefix: l !default;
/* #ifndef APP-NVUE || UNI-APP-X && APP */
@font-face {
font-family: $prefix;
src: url('https://tdesign.gtimg.com/icon/0.3.0/fonts/t.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
/* #endif */
/* #ifdef UNI-APP-X && APP */
@font-face {
font-family: $prefix;
/* #ifdef APP-HARMONY */
src: url('/uni_modules/lime-icon/hybrid/html/t3.ttf');
/* #endif */
/* #ifndef APP-HARMONY */
src: url('uni_modules/lime-icon/hybrid/html/t3.ttf');
/* #endif */
}
/* #endif */

View File

@@ -0,0 +1,27 @@
// #ifndef APP-ANDROID || APP-HARMONY
import iconList from '../../static/icons.json';
export const icons = ref<Map<string, any | null>>((iconList as UTSJSONObject).toMap())
// #endif
// #ifdef APP-ANDROID || APP-HARMONY
export const icons = ref<Map<string, any | null>>(new Map<string, any | null>())
if (icons.value.size == 0) {
uni.getFileSystemManager().readFile({
// #ifdef APP-HARMONY
filePath: '/uni_modules/lime-icon/static/icons.json',
// #endif
// #ifndef APP-HARMONY
filePath: 'uni_modules/lime-icon/static/icons.json',
// #endif
encoding: 'utf-8',
success: (res) => {
const obj = JSON.parseObject(res.data as string)
if (obj == null) return
icons.value = obj!.toMap();
},
fail(err) {
console.log('[lime-icon getFileSystemManager]', err)
}
} as ReadFileOptions);
}
// #endif

View File

@@ -0,0 +1,63 @@
// 公共前缀
@import '@/uni_modules/lime-style/index.scss';
@import './icon';
$use-css-var: true;
$prefix: l !default;
$icon: #{$prefix}-icon;
/* #ifdef APP-NVUE || UNI-APP-X && APP */
$icon-size: create-var(icon-size, 16px);
$icon-color: create-var(icon-color, $text-color-1);
/* #endif */
/* #ifndef APP-NVUE || UNI-APP-X && APP */
$icon-size: create-var(icon-size, 1em);
$icon-color: create-var(icon-color, currentColor);
:host {
display: inline-flex;
align-items: center;
justify-content: center;
}
/* #endif */
.#{$icon} {
/* #ifndef APP-NVUE || UNI-APP-X && APP */
font-family: $prefix;
display: inline-flex;
position: relative;
/* #endif */
&--font {
font-family: $prefix;
text-align: center;
// font-size: $icon-size;
// color: $icon-color;
/* #ifndef APP-NVUE || UNI-APP-X && APP */
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
// -webkit-background-clip: text;
// background-clip: text;
/* #endif */
}
&--image {
width: $icon-size;
height: $icon-size;
// background: red;
/* #ifndef APP-NVUE || UNI-APP-X && APP */
display: block;
/* #endif */
/* #ifdef WEB */
position: relative;
// ::deep(img) {
// z-index: -1;
// }
/* #endif */
}
}

View File

@@ -0,0 +1,189 @@
<template>
<text :key="iconCode" class="l-icon" :class="[classes, lClass]" :style="[styles, lStyle]" v-if="!isImage && !isIconify && !isSVG">{{iconCode}}</text>
<image class="l-icon" :class="[classes, lClass]" :style="[styles, lStyle]" v-else-if="(!isSVG && !hasIconify) && isImage" :src="iconUrl"></image>
<l-svg class="l-icon" :class="[classes, lClass]" :style="[styles, lStyle]" :color="color" :src="iconUrl" v-else :web="web" @error="imageError" @load="imageload"></l-svg>
</template>
<script lang="uts" setup>
/**
* LimeIcon 图标
* @description ICON集
* <br> 插件类型LIconComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?id=14057
* @property {String} name 图标名称
* @property {String} color 颜色
* @property {String} size 尺寸
* @property {String} prefix 字体图标前缀
* @property {Boolean} inherit 是否继承颜色
* @property {Boolean} web 原生 app(nvue,uvue) 是否使用web渲染
* @event {Function} click 点击事件
*/
import { addUnit } from '@/uni_modules/lime-shared/addUnit';
import { IconCollection } from './types';
import { icons } from './icons'
// defineOptions({
// name: 'l-icon'
// })
const name = 'l-icon'
const IconifyURL : string = 'https://api.iconify.design/';
const $iconsHost : string | null = uni.getStorageSync('$limeIconsHost') as string | null
const props = defineProps({
name: {
type: String,
default: '',
required: true,
// validator: (value: string) : boolean => {
// // 确保是字符串类型且不为空
// return typeof value == 'string' && value.trim().length > 0
// }
},
color: {
type: String,
// default: ''
},
size: {
type: [String, Number],
},
prefix: {
type: String,
default: ''
},
lClass: {
type: String,
default: ''
},
// 对安卓IOS无效
inherit: {
type: Boolean,
default: true
},
web: {
type: Boolean,
default: false
},
lStyle: {
type: [String, Object, Array],
default: ''
},
})
// const emits = defineEmits(['click'])
const $iconCollection = inject<IconCollection>('$iconCollection', {has: false, icons: new Map<string, any|null>()} as IconCollection)
// #ifndef APP-ANDROID
const innerName = computed(():string => props.name ?? '')
// #endif
// #ifdef APP-ANDROID
const innerName = computed(():string => props.name)
// #endif
const collectionIcon = computed(():string|null => {
return $iconCollection.icons.get(innerName.value) as string | null
})
const webviewRef = ref<UniWebViewElement | null>(null)
const hasHost = computed<boolean>(() : boolean => innerName.value.indexOf('/') != -1)
const isIconify = computed<boolean>(() : boolean => {
return !hasHost.value && innerName.value.includes(':')
})
const hasIconify = computed<boolean>(() : boolean => {
// #ifdef APP && uniVersion >= 4.82
return isIconify.value //&& props.color != null
// #endif
// #ifndef APP && uniVersion >= 4.82
return isIconify.value
// #endif
})
const isImage = computed<boolean>(() : boolean => {
// #ifdef APP && uniVersion >= 4.82
return /\.(jpe?g|png|gif|bmp|webp|tiff|svg?)$/i.test(innerName.value) || /^data:image\/(jpeg|png|gif|bmp|webp|tiff|svg)/.test(innerName.value) || isIconify.value
// #endif
// #ifndef APP && uniVersion >= 4.82
return /\.(jpe?g|png|gif|bmp|webp|tiff?)$/i.test(innerName.value) || /^data:image\/(jpeg|png|gif|bmp|webp|tiff);base64,/.test(innerName.value)
// #endif
})
const isSVG = computed<boolean>(():boolean => {
// #ifdef APP && uniVersion >= 4.82
// 由于base64 安卓image尺寸不对
if(innerName.value.startsWith('data:image/svg+xml')) return true
// if(props.color == null) return false
return (/\.svg$/i.test(innerName.value) || innerName.value.startsWith('data:image/svg+xml') || innerName.value.startsWith('<svg'))
// #endif
// #ifndef APP && uniVersion >= 4.82
return /\.svg$/i.test(innerName.value) || innerName.value.startsWith('data:image/svg+xml') || innerName.value.startsWith('<svg')
// #endif
})
const classes = computed<Map<string, any>>(() : Map<string, any> => {
const cls = new Map<string, any>()
cls.set(`${name}--font`, !isImage.value && !isIconify.value && !isSVG.value)
cls.set(`${name}--image`, isImage.value || isIconify.value || isSVG.value)
cls.set(props.prefix, props.prefix.length > 0)
cls.set(props.lClass, props.lClass.length > 0)
// #ifndef UNI-APP-X && APP
cls.set(`is-inherit`, (isIconify.value) && (props.color && props.color.length > 0 || props.inherit))
// #endif
return cls
})
const styles = computed<Map<string, any>>(() : Map<string, any> => {
const style = new Map<string, any>();
const size = addUnit(props.size)
// #ifdef APP
if ((props.color != '' && props.color != null) && !isImage.value && !isIconify.value) {
style.set('color', props.color!)
}
// #endif
// #ifndef APP
if(props.color) {
style.set('color', props.color!)
}
// #endif
if (size != null) {
if (isImage.value || isIconify.value || isSVG.value) {
style.set('height', size)
style.set('width', size)
} else {
style.set('font-size', size)
}
}
return style
})
const iconCode = computed<string>(() : string => {
return icons.value.get(innerName.value) as string | null ?? (/[^\x00-\x7F]/.test(innerName.value) ? innerName.value : '')
})
const isError = ref(false)
const cacheMap = new Map<string, string>()
const iconUrl = computed(():string => {
const hasIconsHost = $iconsHost != null && $iconsHost != ''
// const hasIconCollection = $iconCollection.has
if(isImage.value && !isIconify.value) {
return hasHost.value ? innerName.value : ($iconsHost ?? '') + innerName.value
} else if(isIconify.value) {
// 防止重绘
if(cacheMap.has(innerName.value) && !isError.value) {
return cacheMap.get(innerName.value)!
}
// 如果存在collectionIcon则使用
// 如果设置的路径加载失败 就使用网络地址 就使用iconify api
const _host = `${hasIconsHost ? $iconsHost : IconifyURL}`
const _icon =collectionIcon.value ?? _host + `${innerName.value}.svg`.replace(/:/g, '/')
cacheMap.set(innerName.value, _icon)
return _icon
} else if(isSVG.value) {
return (/\.svg$/i.test(innerName.value) && $iconsHost != null && !hasHost.value ? $iconsHost : '') + innerName.value.replace(/'/g, '"')
} else {
return ''
}
})
const imageError = () => {
isError.value = true
}
const imageload = () => {
isError.value = false
}
</script>
<style lang="scss">
@import './index.scss';
</style>

View File

@@ -0,0 +1,151 @@
<template>
<text class="l-icon" :class="[classes]" :style="[styles, lStyle]" v-if="!isImage && !isIconify && !isSVG" @click="$emit('click')">{{iconCode}}</text>
<image class="l-icon" :class="[classes]" :style="[styles, lStyle]" v-else-if="(!isSVG && !isIconify) && isImage" :src="iconUrl" @click="$emit('click')"></image>
<l-svg class="l-icon" :class="[classes]" :style="[styles, lStyle]" v-else :web="web" :color="color" :src="iconUrl" @error="imageError" @load="imageLoad" @click="$emit('click')"></l-svg>
</template>
<script lang="ts">
// @ts-nocheck
/**
* LimeIcon 图标
* @description ICON集
* @tutorial https://ext.dcloud.net.cn/plugin?id=14057
* @property {String} name 图标名称
* @property {String} color 颜色
* @property {String} size 尺寸
* @property {String} prefix 字体图标前缀
* @property {Boolean} inherit 是否继承颜色
* @property {Boolean} web 原生 app(nvue,uvue) 是否使用web渲染
* @event {Function} click 点击事件
*/
// @ts-nocheck
import { computed, defineComponent, ref, inject } from '@/uni_modules/lime-shared/vue';
import icons from '../../static/icons.json';
import { addUnit } from '@/uni_modules/lime-shared/addUnit';
import { isObject } from '@/uni_modules/lime-shared/isObject';
import IconProps from './props';
// #ifdef VUE2 && MP
import { getClassStr } from '@/uni_modules/lime-shared/getClassStr';
// #endif
// #ifdef APP-NVUE
import iconSrc from '@/uni_modules/lime-icon/hybrid/html/t3.ttf';
var domModule = weex.requireModule('dom');
domModule.addRule('fontFace', {
'fontFamily': "uniicons",
'src': "url('" + iconSrc + "')"
});
// #endif
const name = 'l-icon';
export default defineComponent({
name,
externalClasses: ['l-class'],
options: {
addGlobalClass: true,
virtualHost: true,
},
props: IconProps,
emits: ['click'],
setup(props, { emit }) {
const $iconCollection = inject('$iconCollection', null)
const { $limeIconsHost: $iconsHost } = uni as any
const IconifyURL = 'https://api.iconify.design/'
// const isAPP = uni.getSystemInfoSync().uniPlatform == 'app'
const innerName = computed(():string => props.name || '')
const hasHost = computed(() => `${innerName.value}`.indexOf('/') !== -1);
const isIconify = computed(() => !hasHost.value && `${innerName.value}`.includes(':'))
const collectionIcon = computed(() => isObject($iconCollection) && $iconCollection.icons[innerName.value])
const isImage = computed<boolean>(() : boolean => {
return /\.(jpe?g|png|gif|bmp|webp|tiff?)$/i.test(innerName.value) || /^data:image\/(jpeg|png|gif|bmp|webp|tiff);base64,/.test(innerName.value)
})
const isSVG = computed<boolean>(() : boolean => {
return /\.svg$/i.test(innerName.value) || innerName.value.startsWith('data:image/svg+xml') || innerName.value.startsWith('<svg')
})
const classes = computed(() => {
const { prefix } = props
const iconPrefix = prefix || name
const iconName = `${iconPrefix}-${innerName.value}`
const isFont = !isImage.value && !isIconify.value && !isSVG.value
const isImages = isImage.value || isIconify.value || isSVG.value
const cls = {
[iconPrefix]: !isImages && prefix,
[iconName]: !isImages,
[`${name}--image`]: isImages,
[`${name}--font`]: isFont,
// [`is-inherit`]: isIconify.value && (props.color || props.inherit)
}
// #ifdef VUE2 && MP
return getClassStr(cls)
// #endif
return cls
})
const iconCode = computed(() => {
const isImages = isImage.value || isIconify.value || isSVG.value
return (!isImages && icons[innerName.value]) || (/[^\x00-\x7F]/.test(innerName.value) ? innerName.value : '')
})
const isError = ref(false)
const cacheMap = new Map<string, string>()
const iconUrl = computed(() => {
const hasIconsHost = $iconsHost != null && $iconsHost != ''
// const hasIconCollection = $iconCollectiont != null
if (isImage.value) {
return hasHost.value ? innerName.value : ($iconsHost || '') + innerName.value
} else if (isIconify.value) {
// 防止重绘
if(cacheMap.has(innerName.value) && !isError.value) {
return cacheMap.get(innerName.value)!
}
// 如果存在collectionIcon则使用
// 如果设置的路径加载失败 就使用网络地址 就使用iconify api
// !isError.value &&
const _host = `${hasIconsHost ? $iconsHost : IconifyURL}`
const _icon = collectionIcon.value || _host + `${innerName.value}.svg`.replace(/:/g, '/')
cacheMap.set(innerName.value, _icon)
return _icon
} else if (isSVG.value) {
return (/\.svg$/i.test(innerName.value) && hasIconsHost && !hasHost.value ? $iconsHost : '') + innerName.value.replace(/'/g, '"')
} else {
return null
}
})
const styles = computed(() => {
const style : Record<string, any> = {
'color': props.color,
}
if (typeof props.size == 'number' || props.size) {
style['font-size'] = addUnit(props.size)
}
//#ifdef VUE2
// VUE2小程序最后一个值莫名的出现undefined
style['undefined'] = ''
// #endif
return style
})
const imageLoad = () => {
isError.value = false
}
const imageError = () => {
isError.value = true
}
return {
iconCode,
classes,
styles,
isImage,
isSVG,
isIconify,
iconUrl,
imageLoad,
imageError
}
}
})
</script>
<style lang="scss">
@import './index.scss';
</style>

View File

@@ -0,0 +1,31 @@
export const ariaProps = {
ariaHidden: Boolean,
ariaRole: String,
ariaLabel: String,
ariaLabelledby: String,
ariaDescribedby: String,
ariaBusy: Boolean,
// lStyle: String
}
export default {
...ariaProps,
lClass: String,
name: {
type: String,
required: true,
},
color: String,
size: [String, Number],
prefix: String,
// type: String,
inherit: {
type: Boolean,
default: true
},
web: {
type: Boolean,
default: true
},
lStyle:[String, Object, Array],
}

View File

@@ -0,0 +1,4 @@
export type IconCollection = {
has: boolean,
icons: Map<string, any|null>
}

View File

@@ -0,0 +1,229 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">Icon 图标</text>
<text class="demo-block__desc-text">iconify图标集超过15,0000个开源矢量图标</text>
<view class="demo-block__body">
<view class="demo-block card">
<text class="demo-block__title-text large">基础使用</text>
<view class="demo-block__body">
<view class="grid">
<view class="grid-item">
<l-icon class="dome-icon" name="earth" size="30px"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" name="animation-1" size="30px"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" name="object-storage" size="30px"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" name="animation" size="30px"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" name="brightness-1" size="30px"></l-icon>
</view>
</view>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">使用图片</text>
<view class="demo-block__body">
<view class="grid">
<view class="grid-item" v-for="item in imageList" :key="item">
<l-icon class="dome-icon" :key="item" :name="item" size="30px"></l-icon>
</view>
</view>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">iconify</text>
<view class="demo-block__body">
<view class="grid">
<view class="grid-item">
<l-icon class="dome-icon" size="30px" name="uil:12-plus" color="blue"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" size="30px" name="icon-park-outline:abdominal"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" size="30px" name="icon-park-outline:acoustic"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" size="30px" name="ph:tabs-bold"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" size="30px" name="ri:aliens-fill"></l-icon>
</view>
</view>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">自定义图标</text>
<view class="demo-block__body">
<view class="grid">
<view class="grid-item" style="--l-icon-color: rgba(255,25,0,1)">
<l-icon size="30px" prefix="keyicon" :name="`\uE6EF`"></l-icon>
</view>
<view class="grid-item">
<l-icon class="dome-icon" size="30px" :name="`\uE6EF`"></l-icon>
</view>
</view>
</view>
</view>
<!-- <view class="demo-block card">
<text class="demo-block__title-text large">内置图标</text>
<view class="demo-block__body">
<view style="height: 800rpx;">
<list-view style="flex:1; height: 800rpx;" v-if="iconsData.length > 0">
<list-item v-for="(group) in iconsData" :type="group">
<view class="grid">
<view class="grid-item" v-for="item in group" :key="item">
<l-icon class="dome-icon" :key="item" :name="item" @click="onClick(item)" size="30px"></l-icon>
</view>
</view>
</list-item>
</list-view>
</view>
</view>
</view> -->
</view>
</view>
</template>
<script lang="uts">
// @ts-nocheck
// #ifndef UNI-APP-X && APP
// import iconList from '../../static/icons.json';
// #endif
export default {
data() {
return {
show: false,
page: 0,
pageSize: 10,
iconsData: [] as string[][],
icons: [] as string[][],
customList: [
'add-circle',
// 'image-fill',
// 'image-fail-fill',
// 'close-circle-fill',
// 'close',
],
imageList: [
'https://fastly.jsdelivr.net/npm/@vant/assets/icon-demo.png',
"data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.999 2.999h12v-1h-12v1zM3.377 10.23l4.11-4.035v8.649h1.01V6.19l4.18 4.077.715-.7-5.05-4.926a.5.5 0 0 0-.7.001L2.66 9.532l.716.697z' fill='%23000' fill-opacity='.9'/%3E%3C/svg%3E",
"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjk3OTc4MjQzMDMyIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE3NzgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTUxOC40IDE0OS4yOTA2NjdjMTEyLjU5NzMzMy04MC43ODkzMzMgMjY3Ljg4MjY2Ny02OS4zOTczMzMgMzY4LjEyOCAzMiA1My44NjY2NjcgNTQuNTI4IDg0LjEzODY2NyAxMjguODUzMzMzIDg0LjEzODY2NyAyMDYuMzc4NjY2IDAgNzcuNTI1MzMzLTMwLjI5MzMzMyAxNTEuODUwNjY3LTg0LjA5NiAyMDYuMzM2bC0yOTQuNDIxMzM0IDI5OS41MmExMTAuOTc2IDExMC45NzYgMCAwIDEtODAuMjEzMzMzIDM0LjQ3NDY2NyAxMTAuNzIgMTEwLjcyIDAgMCAxLTc5LjkxNDY2Ny0zNC4xNzZMMTM3LjMyMjY2NyA1OTMuNzcwNjY3QzgzLjU2MjY2NyA1MzkuMjQyNjY3IDUzLjMzMzMzMyA0NjQuOTgxMzMzIDUzLjMzMzMzMyAzODcuNTQxMzMzczMwLjIyOTMzMy0xNTEuNzIyNjY3IDg0LjAxMDY2Ny0yMDYuMjcyYzEwMC4yMjQtMTAxLjM3NiAyNTUuNTMwNjY3LTExMi43NjggMzY4LjEyOC0zMS45Nzg2NjZsNi40NDI2NjcgNC43Nzg2NjYgNi40ODUzMzMtNC43Nzg2NjZ6IG0zMjIuNjAyNjY3IDc2Ljk3MDY2NmMtODQuNjI5MzMzLTg1LjU4OTMzMy0yMTkuMTU3MzMzLTg4LjY0LTMwNy4zMjgtNi45NTQ2NjZsLTIxLjc2IDIwLjEzODY2Ni0yMS43MTczMzQtMjAuMTM4NjY2Yy04OC4xOTItODEuNjg1MzMzLTIyMi43Mi03OC42MzQ2NjctMzA3LjMwNjY2NiA2LjkzMzMzMy00MS45MiA0Mi40OTYtNjUuNTU3MzMzIDEwMC42MDgtNjUuNTU3MzM0IDE2MS4yOCAwIDYwLjY5MzMzMyAyMy42MzczMzMgMTE4LjgwNTMzMyA2NS42IDE2MS4zNDRsMjk1LjA0IDMwMC40MTZjOS4wNDUzMzMgOS40NTA2NjcgMjEuMjY5MzMzIDE0LjcyIDMzLjk2MjY2NyAxNC43MiAxMi42OTMzMzMgMCAyNC45MTczMzMtNS4yNjkzMzMgMzQuMjYxMzMzLTE1LjA0TDg0MC45NiA1NDkuMDc3MzMzYzQyLjAwNTMzMy00Mi40OTYgNjUuNjg1MzMzLTEwMC42NTA2NjcgNjUuNjg1MzMzLTE2MS40MDggMC02MC43MzYtMjMuNjgtMTE4LjkxMi02NS42NjQtMTYxLjQwOHoiIGZpbGw9IiMxMTExMTEiIHAtaWQ9IjE3NzkiPjwvcGF0aD48L3N2Zz4="
],
color: 'blue'
}
},
created() {
},
methods: {
},
mounted() {
}
}
</script>
<style lang="scss">
$card-bg-color: var(--doc-card-bg-color, white);
// $page-bg-color: var(--doc-page-bg-color, #f5f5f5);
$title-color: var(--doc-title-color, #000000E6);
$summary-color: var(--doc-summary-color, #00000099);
@font-face {
font-family: keyicon;
src: url('https://at.alicdn.com/t/c/font_4741157_ul7wcp52yys.ttf');
}
.keyicon {
font-family: keyicon;
}
.alt {
// background-color: aqua;
color: blue
}
.dome-icon {
color: $title-color;
}
.grid {
display: flex;
// flex: 1;
flex-direction: row;
flex-wrap: wrap;
&-item {
display: flex;
// flex-direction: column;
flex-basis: 20%;
align-items: center;
padding-bottom: 10px;
padding-top: 10px;
&-text {
padding-top: 10rpx;
font-size: 22rpx;
opacity: 0.6;
}
}
}
.demo-block {
margin: 32px 10px 0;
overflow: visible;
&.card {
padding: 20rpx 20rpx;
background-color: $card-bg-color;
transition-property: background-color;
// transition-duration: 300ms;
margin-bottom: 20rpx !important;
}
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: $summary-color;
font-weight: 400;
font-size: 14px;
line-height: 16px;
&.large {
color: $title-color;
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: $title-color;
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: $summary-color;
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
margin: 0;
}
}
}
</style>

View File

@@ -0,0 +1,254 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">Icon 图标</text>
<text class="demo-block__desc-text">iconify图标集超过15,0000个开源矢量图标</text>
<view class="demo-block__body">
<view class="demo-block card">
<text class="demo-block__title-text large">基础使用</text>
<view class="demo-block__body">
<view class="grid">
<view class="grid-item">
<l-icon name="earth" size="30px"></l-icon>
</view>
<view class="grid-item">
<l-icon name="animation-1" size="30px"></l-icon>
</view>
<view class="grid-item">
<l-icon name="object-storage" size="30px"></l-icon>
</view>
<view class="grid-item">
<l-icon name="animation" size="30px"></l-icon>
</view>
<view class="grid-item">
<l-icon name="brightness-1" size="30px"></l-icon>
</view>
</view>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">使用图片</text>
<view class="demo-block__body">
<view class="grid">
<view class="grid-item" v-for="item in imageList" :key="item">
<l-icon :key="item" :name="item" size="30px"></l-icon>
</view>
</view>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">iconify</text>
<view class="demo-block__body">
<view class="grid">
<view class="grid-item">
<l-icon size="30px" name="uil:12-plus" color="blue"></l-icon>
</view>
<view class="grid-item">
<l-icon size="30px" name="icon-park-outline:abdominal"></l-icon>
</view>
<view class="grid-item">
<l-icon size="30px" name="icon-park-outline:acoustic"></l-icon>
</view>
<view class="grid-item">
<l-icon size="30px" name="ph:tabs-bold"></l-icon>
</view>
<view class="grid-item">
<l-icon size="30px" name="ri:aliens-fill"></l-icon>
</view>
</view>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">自定义图标</text>
<view class="demo-block__body">
<view class="grid">
<view class="grid-item">
<l-icon size="30px" prefix="keyicon" :name="`\uE6EF`"></l-icon>
</view>
<view class="grid-item">
<l-icon size="30px" :name="`\uE6EF`"></l-icon>
</view>
</view>
</view>
</view>
<!-- <view class="demo-block card">
<text class="demo-block__title-text large">内置图标</text>
<view class="demo-block__body">
<view style="height: 800rpx;">
<list-view style="flex:1; height: 800rpx;" v-if="iconsData.length > 0">
<list-item v-for="(group) in iconsData" :type="group">
<view class="grid">
<view class="grid-item" v-for="item in group" :key="item">
<l-icon :key="item" :name="item" @click="onClick(item)" size="30px"></l-icon>
</view>
</view>
</list-item>
</list-view>
</view>
</view>
</view> -->
</view>
</view>
</template>
<script>
export default {
data() {
return {
show: false,
page: 0,
pageSize: 10,
iconsData: [],
icons: [],
customList: [
'add-circle',
// 'image-fill',
// 'image-fail-fill',
// 'close-circle-fill',
// 'close',
],
imageList: [
'https://fastly.jsdelivr.net/npm/@vant/assets/icon-demo.png',
"data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.999 2.999h12v-1h-12v1zM3.377 10.23l4.11-4.035v8.649h1.01V6.19l4.18 4.077.715-.7-5.05-4.926a.5.5 0 0 0-.7.001L2.66 9.532l.716.697z' fill='%23000' fill-opacity='.9'/%3E%3C/svg%3E",
"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjk3OTc4MjQzMDMyIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE3NzgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTUxOC40IDE0OS4yOTA2NjdjMTEyLjU5NzMzMy04MC43ODkzMzMgMjY3Ljg4MjY2Ny02OS4zOTczMzMgMzY4LjEyOCAzMiA1My44NjY2NjcgNTQuNTI4IDg0LjEzODY2NyAxMjguODUzMzMzIDg0LjEzODY2NyAyMDYuMzc4NjY2IDAgNzcuNTI1MzMzLTMwLjI5MzMzMyAxNTEuODUwNjY3LTg0LjA5NiAyMDYuMzM2bC0yOTQuNDIxMzM0IDI5OS41MmExMTAuOTc2IDExMC45NzYgMCAwIDEtODAuMjEzMzMzIDM0LjQ3NDY2NyAxMTAuNzIgMTEwLjcyIDAgMCAxLTc5LjkxNDY2Ny0zNC4xNzZMMTM3LjMyMjY2NyA1OTMuNzcwNjY3QzgzLjU2MjY2NyA1MzkuMjQyNjY3IDUzLjMzMzMzMyA0NjQuOTgxMzMzIDUzLjMzMzMzMyAzODcuNTQxMzMzczMwLjIyOTMzMy0xNTEuNzIyNjY3IDg0LjAxMDY2Ny0yMDYuMjcyYzEwMC4yMjQtMTAxLjM3NiAyNTUuNTMwNjY3LTExMi43NjggMzY4LjEyOC0zMS45Nzg2NjZsNi40NDI2NjcgNC43Nzg2NjYgNi40ODUzMzMtNC43Nzg2NjZ6IG0zMjIuNjAyNjY3IDc2Ljk3MDY2NmMtODQuNjI5MzMzLTg1LjU4OTMzMy0yMTkuMTU3MzMzLTg4LjY0LTMwNy4zMjgtNi45NTQ2NjZsLTIxLjc2IDIwLjEzODY2Ni0yMS43MTczMzQtMjAuMTM4NjY2Yy04OC4xOTItODEuNjg1MzMzLTIyMi43Mi03OC42MzQ2NjctMzA3LjMwNjY2NiA2LjkzMzMzMy00MS45MiA0Mi40OTYtNjUuNTU3MzMzIDEwMC42MDgtNjUuNTU3MzM0IDE2MS4yOCAwIDYwLjY5MzMzMyAyMy42MzczMzMgMTE4LjgwNTMzMyA2NS42IDE2MS4zNDRsMjk1LjA0IDMwMC40MTZjOS4wNDUzMzMgOS40NTA2NjcgMjEuMjY5MzMzIDE0LjcyIDMzLjk2MjY2NyAxNC43MiAxMi42OTMzMzMgMCAyNC45MTczMzMtNS4yNjkzMzMgMzQuMjYxMzMzLTE1LjA0TDg0MC45NiA1NDkuMDc3MzMzYzQyLjAwNTMzMy00Mi40OTYgNjUuNjg1MzMzLTEwMC42NTA2NjcgNjUuNjg1MzMzLTE2MS40MDggMC02MC43MzYtMjMuNjgtMTE4LjkxMi02NS42NjQtMTYxLjQwOHoiIGZpbGw9IiMxMTExMTEiIHAtaWQ9IjE3NzkiPjwvcGF0aD48L3N2Zz4="
],
color: 'blue'
}
},
created() {
// #ifdef UNI-APP-X && APP
// const manager = uni.getFileSystemManager();
// manager.readFile({
// filePath: '/uni_modules/lime-icon/static/icons.json',
// encoding: 'utf-8',
// success: (res) => {
// const obj = JSON.parse(res.data as string)
// if(obj == null) return
// }
// } as ReadFileOptions);
// #endif
// let group : string[] = []
// let groups : string[][] = []
// let index : number = 0;
// const _icons = UTSJSONObject.assign({}, icons)
// _icons.toMap().forEach((_, key) => {
// if (index % 5 == 0) {
// group = []
// group.push(key)
// } else if (index % 5 == 2) {
// group.push(key)
// groups.push(group)
// } else {
// group.push(key)
// }
// // this.icons.push(key)
// index++
// })
// this.icons = groups
// this.loadData()
// console.log(`this.icons:::`, index, this.icons)
},
methods: {
},
mounted() {
}
}
</script>
<style lang="scss">
@font-face {
font-family: keyicon;
src: url('https://at.alicdn.com/t/c/font_4741157_ul7wcp52yys.ttf');
}
.keyicon {
font-family: keyicon;
}
.alt {
// background-color: aqua;
color: blue
}
.grid {
display: flex;
// flex: 1;
flex-direction: row;
flex-wrap: wrap;
&-item {
display: flex;
// flex-direction: column;
flex-basis: 20%;
align-items: center;
padding-bottom: 10px;
padding-top: 10px;
&-text {
padding-top: 10rpx;
font-size: 22rpx;
opacity: 0.6;
}
}
}
.demo-block {
margin: 32px 10px 0;
overflow: visible;
&.card {
padding: 20rpx 20rpx;
background-color: white;
margin-bottom: 20rpx !important;
}
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
margin: 0;
}
}
}
</style>

View File

@@ -0,0 +1,2 @@
const { generate } = require('./utils/generate.js');
generate()

Binary file not shown.

View File

@@ -0,0 +1,111 @@
// @ts-nocheck
import {IconCollection} from './components/l-icon/types'
// #ifndef UNI-APP-X
// #ifdef VUE3
import { reactive } from 'vue';
function definePlugin(options: any) {
return options
}
// #endif
// #ifdef VUE2
import Vue from 'vue'
let reactive = Vue.observable
type VueApp = any
// #endif
type UTSJSONObject = any//Record<string, any>
// #endif
let topApp : VueApp | null = null;
const _iconCollection = reactive<IconCollection>({
has: true,
// #ifdef UNI-APP-X
icons: new Map<string, any|null>()
// #endif
// #ifndef UNI-APP-X
icons: {}
// #endif
})
export function useIconHost(iconHost : string) {
// #ifdef UNI-APP-X
uni.setStorageSync('$limeIconsHost', iconHost)
// #endif
// #ifndef UNI-APP-X
uni.$limeIconsHost = iconHost
// #endif
}
let isInstall = false
export function useIconCollection(iconCollection: UTSJSONObject|null = {}) {
if(!isInstall) {
console.warn('[lime-icon]: useIconCollection 请先注册app.use(limeIcons, null, iconjson)')
return
}
// #ifdef UNI-APP-X
const map = (iconCollection as UTSJSONObject).toMap()
if(map.size != 0) {
uni.setStorageSync('$limeIconCollection', iconCollection)
_iconCollection.icons = map
}
// #endif
// #ifndef UNI-APP-X
if(Object.keys(iconCollection).length != 0) {
uni.setStorageSync('$limeIconCollection', iconCollection)
_iconCollection.icons = iconCollection
}
// #endif
}
function useProvide() {
if(topApp == null) return
isInstall = true
// #ifdef VUE3
topApp!.provide('$iconCollection', _iconCollection)
// #endif
// #ifndef VUE3
topApp.mixin({
provide: {
$iconCollection: _iconCollection
}
})
// #endif
}
// #ifdef VUE3
export const limeIcons = definePlugin({
install: (app: VueApp, iconHost: string | null, iconCollection: UTSJSONObject | null):void => {
topApp = app;
if(iconHost != null || iconHost != '') {
useIconHost(iconHost!)
}
if(iconCollection != null) {
useProvide()
useIconCollection(iconCollection)
}
}
})
// #endif
// #ifdef VUE2
export const limeIcons = {
install: (app: any, options: any[]) => {
topApp = app;
let [iconHost, iconCollection] = options
if(iconHost != null && typeof iconHost == 'object') {
iconCollection = iconHost
}
if(iconHost && iconHost != '') {
useIconHost(iconHost!)
}
if(iconCollection != null) {
useProvide()
useIconCollection(iconCollection)
}
}
}
// #endif

View File

@@ -0,0 +1,23 @@
// lime.config.js
module.exports = {
// 输入的文件目录自有的SVG如果没有则不需要
input: {
prefix: "my-icons",
dir: '/static/svg',
},
// 输出的配置
output: {
// 输出的文件目录
dir: '/static/icon',
// 输出的文件的格式如果是JSON则是一个图标合集
// file: 'icons.json',
// 如果是SVG则是每个图标做为单独的文件
file: '*.svg',
},
icons: [
'el:address-book',
'uil:12-plus',
'icon-park-outline:abdominal',
'icon-park-outline:acoustic'
]
}

View File

@@ -0,0 +1,110 @@
{
"id": "lime-icon",
"displayName": "lime-icon 图标 iconify 图标集合",
"version": "0.4.1",
"description": "lime-icon 图标插件可方便快捷按需的使用iconify图标iconify超过150,000个开源矢量图标插件内置tdesign icon。使用兼容uniapp/uniappx",
"keywords": [
"icon",
"iconify",
"图标集合",
"按需加载"
],
"repository": "",
"engines": {
"HBuilderX": "^3.8.7",
"uni-app": "^4.54",
"uni-app-x": "^4.61"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "",
"darkmode": "x",
"i18n": "x",
"widescreen": "x"
},
"uni_modules": {
"dependencies": [
"lime-style",
"lime-shared",
"lime-svg"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "x",
"aliyun": "x",
"alipay": "x"
},
"client": {
"uni-app": {
"vue": {
"vue2": "√",
"vue3": "√"
},
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"vue": "√",
"nvue": "-",
"android": {
"extVersion": "",
"minVersion": "21"
},
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√",
"alipay": "√",
"toutiao": "-",
"baidu": "-",
"kuaishou": "-",
"jd": "-",
"harmony": "-",
"qq": "-",
"lark": "-"
},
"quickapp": {
"huawei": "-",
"union": "-"
}
},
"uni-app-x": {
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"android": {
"extVersion": "",
"minVersion": "21"
},
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√"
}
}
}
}
}
}

View File

@@ -0,0 +1,245 @@
# lime-icon 图标
图标组件,方便快捷地使用[iconify](https://iconify.design/)图标集合提供超过150,000个开源矢量图标。支持自定义颜色、大小、前缀等属性还可以使用自定义图标和图标URL。
> 注意:插件依赖的`lime-svg`为收费插件若不需要svg功能删除svg插件即可。
## 文档链接
📚 组件详细文档请访问以下站点:
- [图标文档 - 站点1](https://limex.qcoon.cn/components/icon.html)
- [图标文档 - 站点2](https://limeui.netlify.app/components/icon.html)
- [图标文档 - 站点3](https://limeui.familyzone.top/components/icon.html)
## 安装方法
1. 在uni-app插件市场中搜索并导入`lime-icon`
2. 导入后可能需要重新编译项目
3. 在页面中使用`l-icon`组件(组件)或`lime-icon`(演示)
::: tip 注意🔔
本插件依赖的[【lime-svg】](https://ext.dcloud.net.cn/plugin?id=18519)是原生插件,如果购买(收费为6元)则需要自定义基座,才能使用,
若不需要删除即可
:::
## 代码演示
### 基础使用
使用`name`属性指定要显示的图标。👉️[【全部图标】](#全部图标)
```html
<l-icon name="circle" />
```
### 使用Iconify
到 [icones](https://icones.js.org/) 网站找到需要的图标,通过 `name` 属性来指定需要使用的图标
```html
<l-icon name="ri:account-box-fill" />
<l-icon name="icon-park-outline:acoustic" />
```
![](https://img-cdn-tx.dcloud.net.cn/stream/plugin_screens/263cfd20-39e6-11ee-b4f0-9bc760224a38_1.png?1735701321)
![](https://img-cdn-tx.dcloud.net.cn/stream/plugin_screens/263cfd20-39e6-11ee-b4f0-9bc760224a38_2.png?1735701324)
### 使用图标URL
```html
<l-icon name="https://fastly.jsdelivr.net/npm/@vant/assets/icon-demo.png"></l-icon>
```
### 图标颜色
通过 `color` 属性来设置图标的颜色。
```html
<l-icon name="ri:aliens-fill" color="#1989fa" />
<l-icon name="icon-park-outline:acoustic" color="#ee0a24" />
```
### 图标大小
通过 `size` 属性来设置图标的尺寸大小,可以指定任意 CSS 单位。
```html
<!-- 不指定单位,默认使用 px -->
<l-icon name="ri:aliens-fill" size="40" />
<!-- 指定使用 rpx 单位 -->
<l-icon name="ri:aliens-fill" size="34rpx" />
```
### 自定义图标
通过`prefix`设置iconfot图标类通过`name`传入`Unicode`字符
```html
<l-icon size="30px" prefix="keyicon" :name="`\uE6EF`" color="blue"></l-icon>
```
```css
@font-face {
font-family: keyicon;
src: url('https://at.alicdn.com/t/c/font_4741157_ul7wcp52yys.ttf');
}
.keyicon {
font-family: keyicon;
}
```
## 私有化iconify
默认会使用`iconify`的API如果你想私有化可按以下步骤来
### 第一步 安装
```cmd
yarn add @iconify/json @iconify/tools @iconify/utils
```
### 第二步 配置
- 需要在根目录新建一个`lime-icons.config.js`文件
```
// 在根目录新建一个lime-icons.config.js文件
// lime-icons.config.js
module.exports = {
// 输入的文件目录自有的SVG如果没有则不需要
input: {
prefix: "my-icons",
dir: '/static/svg',
},
// 输出的配置
output: {
// 输出的文件目录
dir: '/static/icons',
// 输出的文件的格式如果是JSON则是一个图标合集
// file: 'icons.json',
// 如果是SVG则是每个图标做为单独的文件
file: '*.svg',
},
// 指定使用的图标
icons: [
'el:address-book',
'uil:12-plus',
'icon-park-outline:abdominal',
'icon-park-outline:acoustic'
]
}
```
在终端执行脚本
```
node ./uni_modules/lime-icon/generate-icons.js
```
### ~~2、自动引入~~
~~如果使用的是`vue3`,通过配置 `vite.config.js` 达到自动引入~~
这个方法作废,因有些图标是动态的,在编译阶段不知道图标的名称无法捕获
```js
import uni from '@dcloudio/vite-plugin-uni';
import limeIcon from './uni_modules/lime-icon/vite-plugin';
import path from 'path'
export default defineConfig({
plugins: [uni(), limeIcon({
// 输出的配置
output: {
// 输出的文件目录
dir: path.join(__dirname, '/static/icons'),
// 输出的文件的格式如果是JSON则是生成一个图标合集 例如: /static/icons/icons.json
// file: 'icons.json',
// 如果是SVG则是每个图标做为单独的文件 例如: /static/icons/xx/xxx.svg
file: '*.svg',
},
// 可选
icons: []
})]
})
```
### 第三步 挂载图标地址
> 注意:如果使用了`iconify` 的API, 小程序需要去公众平台设置下载白名单 `https://api.iconify.design`
```js
// main.js | main.ts | main.uts
// 配置svg指定路径后期可上传到后端不占用本地空间如果使用的是`iconify`也可以不配置这一步
import {limeIcons} from '@/uni_modules/lime-icon'
// 第一个参数是icon host地址没有则填null
// 第二个参数是icons json合集没有则填null
// app.use(limeIcons, null, null)
// 示例1 配置icons地址
app.use(limeIcons, 'https://xxx.cn/static/icons', null)
// 示例2 配置icons集合json
import icons from './static/icons/icons.json'
app.use(limeIcons, null, icons)
```
## 快速预览
导入插件后,可以直接使用以下标签查看演示效果:
```html
<!-- 代码位于 uni_modules/lime-icon/components/lime-icon -->
<lime-icon />
```
## 插件标签说明
`l-icon`: 组件标签,用于实际开发中
`lime-icon`: 演示标签,用于查看示例效果
## Vue2使用说明
本插件使用了`composition-api`如需在Vue2项目中使用请按照[官方教程](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置。
关键配置代码在main.js中添加
```js
// vue2
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
// 配置svg指定路径后期可上传到后端不占用本地空间如果使用的是`iconify`也可以不配置这一步
import {limeIcons} from '@/uni_modules/lime-icon'
Vue.use(VueCompositionAPI)
// 示例1 配置icons地址
Vue.use(limeIcons, ['https://xxx.cn/static/icons', null])
// 示例2 配置icons集合json
import icons from './static/icons/icons.json'
Vue.use(limeIcons, [null, icons])
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --------------------------| ------------------------------------------------------------ | ---------------- | ------------ |
| name | 图标名称 | <em>string</em> | `` |
| color | 颜色 | <em>string</em> | `` |
| size | 尺寸 | <em>string</em> | `square` |
| prefix | 字体图标前缀 | <em>string</em> | `` |
| inherit | 是否继承颜色 | <em>boolean</em> | `true` |
| web | 原生`app(nvue,uvue)`是否使用web渲染 | <em>boolean</em> | `false` |
### Events
| 参数 | 说明 | 参数 |
| --------------------------| ------------------------------------------------------------ | ---------------- |
| click | 点击 | |
## 主题定制
组件提供了丰富的CSS变量用于自定义样式
| 名称 | 默认值 | 描述 |
| --- | --- | --- |
| --l-icon-size | <em>16px</em> | 图标大小 |
| --l-icon-color | <em></em> | 图标颜色仅icon-font生效 |
## 支持与赞赏
如果你觉得本插件解决了你的问题,可以考虑支持作者:
| 支付宝赞助 | 微信赞助 |
|------------|------------|
| ![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png) | ![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png) |

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
const path = require('path');
const fs = require("fs");
const rootPath = process.cwd(); // 获取根目录
const { importDirectory, blankIconSet } = require("@iconify/tools");
const { locate } = require('@iconify/json');
const { getIconData } = require('@iconify/utils');
const { encodeSvg, saveFile, customOptions, deleteDirectory } = require('./index.js')
async function fetchIconsData(icons) {
const collections = {}
for (const iconName of icons) {
const [collectionName, iconNameWithoutPrefix] = iconName.split(':');
const filename = locate(collectionName)
if(!fs.existsSync(filename)) {
continue
}
const icons = JSON.parse(fs.readFileSync(filename, 'utf8'))
if(!icons) {
continue
}
if(collectionName && iconNameWithoutPrefix) {
const iconData = getIconData(icons, iconNameWithoutPrefix);
if(iconData) {
if(!collections[collectionName]) {
collections[collectionName] = blankIconSet(collectionName);
}
collections[collectionName].setIcon(iconNameWithoutPrefix, iconData);
} else {
console.log(`Icon '${iconName}' not found in '${collectionName}' collection.`)
}
} else if(collectionName) {
if(!collections[collectionName]) {
collections[collectionName] = blankIconSet(collectionName)
}
Object.keys(icons.icons).forEach(iconName => {
const iconData = getIconData(icons, iconName)
if(iconData) {
collections[collectionName].setIcon(iconName, iconData)
} else {
console.log(`Icon '${iconName}' not found in '${collectionName}' collection.`)
}
})
}
}
return collections;
}
async function generate(config){
try {
if(!config) {
// 从配置文件中读取选项
const rootConfigPath = path.join(rootPath, 'lime-icons.config.js');
let configPath = ''
if(fs.existsSync(rootConfigPath)) {
configPath = rootConfigPath
} else {
configPath = path.dirname(__filename) + '/lime-icons.config.js'; // 配置文件路径
}
const configFile = fs.readFileSync(configPath, 'utf8');
config = eval(`(${configFile})`);
}
// 根据配置文件中的字段设置选项
const options = {
input: Object.assign({}, customOptions, config.input || {}), // 输入的文件目录
output: {
dir: config.output.dir || '/static', // 输出的文件目录
file: config.output.file || 'icons.json', // 输出的文件的格式,默认为 JSON
},
icons: config.icons || [], // 图标名称列表
};
// 先删除原来的
deleteDirectory(options.output.dir)
// 处理输入目录的逻辑
if (config.input.dir.startsWith('/')) {
options.input.dir = path.join(rootPath, config.input.dir);
} else if (config.input.dir.startsWith('./')) {
options.input.dir = path.join(__dirname, config.input.dir.slice(2));
}
let iconCollections = {}
// 异步地从目录中导入图标
if(fs.existsSync(options.input.dir)) {
const iconSet = await importDirectory(options.input.dir, options.input);
// 导出为 JSON 文件
iconCollections[options.input.prefix] = iconSet
}
// 获取指定图标的数据
if(options.icons.length) {
const iconCollection = await fetchIconsData(options.icons);
Object.assign(iconCollections, iconCollection)
}
if(/\.json$/i.test(options.output.file)) {
const collections = {}
Object.values(iconCollections).forEach((iconSet) => {
iconSet.forEach(iconName => {
// 将 SVG 转换为 Data URL
collections[iconSet.prefix + ':' + iconName] = `data:image/svg+xml;utf8,${encodeSvg(iconSet.toString(iconName))}`
})
})
await saveFile(`${options.output.dir}/${options.output.file}`, JSON.stringify(collections))
} else {
Object.values(iconCollections).forEach((iconSet) => {
iconSet.forEach(async iconName => {
await saveFile(`${options.output.dir}/${iconSet.prefix}/${iconName}.svg`, iconSet.toString(iconName))
})
})
}
} catch (error) {
console.error("导出图标集为 JSON 文件时出错:", error);
}
}
module.exports = {
generate
}

View File

@@ -0,0 +1,89 @@
const fs = require("fs");
const glob = require('glob');
const path = require("path");
const rootPath = process.cwd(); // 获取根目录
// https://bl.ocks.org/jennyknuth/222825e315d45a738ed9d6e04c7a88d0
function encodeSvg(svg) {
return svg
.replace(
"<svg",
~svg.indexOf("xmlns") ? "<svg" : '<svg xmlns="http://www.w3.org/2000/svg"'
)
.replace(/"/g, "'")
.replace(/%/g, "%25")
.replace(/#/g, "%23")
.replace(/{/g, "%7B")
.replace(/}/g, "%7D")
.replace(/</g, "%3C")
.replace(/>/g, "%3E");
}
function isDirectoryEmpty(path) {
const files = fs.readdirSync(path);
return files.length === 0;
}
function deleteFolderBFS(folderPath) {
const outputPath = /^\.|\/|\\/.test(folderPath) ? path.join(rootPath, folderPath): folderPath
if(!fs.existsSync(outputPath)) {
return
}
const queue = [outputPath];
while (queue.length > 0) {
const currentPath = queue.shift();
const currentStats = fs.statSync(currentPath);
if (currentStats.isDirectory()) {
const files = fs.readdirSync(currentPath);
for (const file of files) {
const filePath = path.join(currentPath, file);
const fileStats = fs.statSync(filePath);
if (fileStats.isDirectory()) {
queue.push(filePath);
} else {
fs.unlinkSync(filePath); // 删除文件
}
}
if(isDirectoryEmpty(currentPath)) {
fs.rmdirSync(currentPath);
}
}
}
}
// 保存
async function saveFile(file, data) {
const outputPath = /^(\.|\/|\\)/.test(file) ? path.join(rootPath, file) : file;
const outputDir = path.dirname(outputPath);
try {
// 创建文件夹
await fs.promises.mkdir(outputDir, {
recursive: true
});
// 使用 Promise 进行写入文件操作
await fs.promises.writeFile(outputPath, data, "utf8");
// console.log(`成功保存文件:${outputPath}`);
} catch (error) {
console.error("保存文件时出错:", error);
}
}
// 可选的选项对象
const customOptions = {
prefix: "l", // 为图标集设置前缀
includeSubDirs: true, // 启用扫描子目录中的文件(默认启用)
keyword: (fileName, defaultKeyword, iconSet) => {
// 根据文件名自定义关键字生成
// 返回关键字或 undefined 以跳过该文件
return defaultKeyword;
},
ignoreImportErrors: true, // 禁用未成功导入图标时的错误抛出(默认启用)
keepTitles: false, // 禁用在 SVG 中保留标题(默认禁用)
};
module.exports = {
encodeSvg,
saveFile,
deleteDirectory: deleteFolderBFS,
customOptions,
};

View File

@@ -0,0 +1,87 @@
const { readFileSync, existsSync } = require('fs');
const path = require('path');
const {generate} = require('./utils/generate')
// 插件的名称
const pluginName = 'vite-plugin-limeIcon';
// 要监听的组件的名称
const targetComponent = 'l-icon';
function parseAttributes(attributesStr) {
if (!attributesStr.includes("'")) {
return [attributesStr]
}
const regex = /'([^']+)'/g;
const matches = attributesStr.match(regex);
if (matches) {
const targetContent = matches.map(item => item.replace(/'/g, ''));
return targetContent
} else {
return [attributesStr]
}
}
function extractAttributes(content) {
const regex = /<l-icon\s*[^>]*(:?)name=["]([^"]+)["][^>]*>/g; // /<l-icon\s+(.*?)\s*\/?>/g //<l-icon\s+([^>]+)\s*\/?>/g;
let attributes = [];
const attributesSet = new Set(attributes);
let match;
while ((match = regex.exec(content)) !== null) {
const attributesStr = match[2];
const attributesList = parseAttributes(attributesStr);
for (const attribute of attributesList) {
attributesSet.add(attribute); // 添加新属性到Set中
}
}
attributes = [...attributesSet];
return attributes;
}
// 遍历每个文件并检查是否使用了目标组件
let iconCollections = {}
let files = {}
let timer = null
function processFile(file, options) {
const filePath = path.resolve(file);
const content = readFileSync(filePath, 'utf-8');
// 检查文件是否包含目标组件
if (content.includes(targetComponent) && (!file.includes('l-icon.vue') || !file.includes('l-icon.uvue')) && files[file] !== content) {
const icons = extractAttributes(content)
if(icons && icons.length) {
files[file] = content
iconCollections[file] = icons
}
Object.values(iconCollections).forEach(icons => {
if(options.icons) {
options.icons = options.icons.concat(icons);
} else {
options.icons = icons
}
})
clearTimeout(timer)
timer = setTimeout(() => {
options.icons = Array.from(new Set(options.icons))
generate(options)
},500)
}
}
// 插件的钩子函数
function vitePlugin(options = {}) {
const {useInDevelopment = false} = options
const isDev = process.env.NODE_ENV === 'development'
return {
name: pluginName,
transform(code, id) {
if (id.endsWith('.vue') && (useInDevelopment && isDev || !useInDevelopment && !isDev)) {
// 处理Vue文件
processFile(id, options);
}
},
};
}
module.exports = vitePlugin;