feat: 新增图片和图标

This commit is contained in:
奔跑的面条 2023-05-23 20:55:24 +08:00
parent 93ed31f093
commit e4db7cb8ff
28 changed files with 6807 additions and 464 deletions

View File

@ -33,6 +33,7 @@
"highlight.js": "^11.5.0", "highlight.js": "^11.5.0",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"keymaster": "^1.6.2", "keymaster": "^1.6.2",
"mitt": "^3.0.0",
"monaco-editor": "^0.33.0", "monaco-editor": "^0.33.0",
"naive-ui": "2.34.3", "naive-ui": "2.34.3",
"pinia": "^2.0.13", "pinia": "^2.0.13",

6289
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -71,14 +71,14 @@ const iconNames = [
] ]
const iconList = iconNames.map(name => ({ const iconList = iconNames.map(name => ({
...IconConfig, ...IconConfig,
category: ChatCategoryEnum.UNICONS, category: ChatCategoryEnum.COMMON,
categoryName: ChatCategoryEnumName.UNICONS, categoryName: ChatCategoryEnumName.COMMON,
package: PackagesCategoryEnum.ICONS, package: PackagesCategoryEnum.ICONS,
image: name, image: name,
icon: name, icon: name,
dataset: name, dataset: name,
title: name.replace('uim:', ''), title: name.replace('uim:', ''),
virtualComponent: './components/Icons/Icon' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置 redirectComponent: './components/Icons/Icon' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置
})) }))
export default iconList export default iconList

View File

@ -6,8 +6,8 @@ export const IconConfig: ConfigType = {
chartKey: 'VIcon', chartKey: 'VIcon',
conKey: 'VCIcon', conKey: 'VCIcon',
title: '图标', title: '图标',
category: ChatCategoryEnum.UNICONS, category: ChatCategoryEnum.COMMON,
categoryName: ChatCategoryEnumName.UNICONS, categoryName: ChatCategoryEnumName.COMMON,
package: PackagesCategoryEnum.ICONS, package: PackagesCategoryEnum.ICONS,
chartFrame: ChartFrameEnum.COMMON, chartFrame: ChartFrameEnum.COMMON,
image: 'icon.png' image: 'icon.png'

View File

@ -39,7 +39,7 @@ const iconList = iconNames.map(name => ({
icon: name, icon: name,
dataset: name, dataset: name,
title: name.replace('line-md:', ''), title: name.replace('line-md:', ''),
virtualComponent: './components/Icons/Icon' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置 redirectComponent: './components/Icons/Icon' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置
})) }))
export default iconList export default iconList

View File

@ -36,7 +36,6 @@ const iconNames = [
'wi:night-alt-cloudy-high', 'wi:night-alt-cloudy-high',
'wi:night-alt-hail', 'wi:night-alt-hail',
'wi:night-alt-lightning', 'wi:night-alt-lightning',
'wi:night-alt-lightning',
'wi:umbrella', 'wi:umbrella',
] ]
const iconList = iconNames.map(name => ({ const iconList = iconNames.map(name => ({
@ -48,7 +47,7 @@ const iconList = iconNames.map(name => ({
icon: name, icon: name,
dataset: name, dataset: name,
title: name.replace('wi:', ''), title: name.replace('wi:', ''),
virtualComponent: './components/Icons/Icon' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置 redirectComponent: './components/Icons/Icon' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置
})) }))
export default iconList export default iconList

View File

@ -1,12 +1,12 @@
export enum ChatCategoryEnum { export enum ChatCategoryEnum {
ML = 'MaterialLine', ML = 'MaterialLine',
UNICONS = 'Unicons', COMMON = 'Common',
WEATHER = "WEATHER" WEATHER = "WEATHER"
} }
export enum ChatCategoryEnumName { export enum ChatCategoryEnumName {
ML = '动画', ML = '动画',
UNICONS = '通用', COMMON = '通用',
WEATHER = "天气" WEATHER = "天气"
} }

View File

@ -1,5 +1,5 @@
import MaterialLine from './MaterialLine' import MaterialLine from './MaterialLine'
import Unicons from './Unicons' import Common from './Common'
import Weather from './Weather' import Weather from './Weather'
export const IconList = [...MaterialLine, ...Unicons, ...Weather] export const IconList = [...MaterialLine, ...Common, ...Weather]

View File

@ -1,67 +0,0 @@
import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
import { ImageConfig } from '@/packages/components/Informations/Mores/Image/index'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
import { setLocalStorage, getLocalStorage } from '@/utils'
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { StorageEnum } from '@/enums/storageEnum'
const StoreKey = StorageEnum.GO_USER_MEDIA_PHOTOS
/**
*
*/
type UploadCompletedEventType = {
fileName: string
url: string
}
const userPhotosList: ConfigType[] = getLocalStorage(StoreKey) || []
const uploadFile = (callback: Function | null = null) => {
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*' // 这里只允许图片类型
input.onchange = async () => {
if (!input.files || !input.files.length) return
const file = input.files[0]
const reader = new FileReader()
reader.onload = () => {
const eventObj: UploadCompletedEventType = { fileName: file.name, url: reader.result as string }
callback && callback(eventObj)
}
reader.readAsDataURL(file)
}
input.click()
}
const addConfig = {
...ImageConfig,
category: ChatCategoryEnum.MY,
categoryName: ChatCategoryEnumName.MY,
package: PackagesCategoryEnum.PHOTOS,
title: '上传新项',
image: 'upload.png',
virtualComponent: './components/Informations/Mores/Image', // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置
disabled: true,
clickHandle: (photoConfig: ConfigType) => {
uploadFile((e: UploadCompletedEventType) => {
// 和上传组件一样配置,更换标题,图片,预设数据
const newPhoto = {
...ImageConfig,
category: ChatCategoryEnum.MY,
categoryName: ChatCategoryEnumName.MY,
package: PackagesCategoryEnum.PHOTOS,
title: e.fileName,
image: e.url,
dataset: e.url,
virtualComponent: './components/Informations/Mores/Image' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置
}
userPhotosList.push(newPhoto)
setLocalStorage(StoreKey, userPhotosList)
const { getPackagesList } = usePackagesStore()
getPackagesList.Photos.splice(getPackagesList.Photos.length - 1, 0, newPhoto) // 插入到上传按钮前的位置
})
}
}
export default [...userPhotosList, addConfig]

View File

@ -0,0 +1,86 @@
import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
import { ImageConfig } from '@/packages/components/Informations/Mores/Image/index'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
import { setLocalStorage, getLocalStorage, goDialog } from '@/utils'
import { StorageEnum } from '@/enums/storageEnum'
import { FileTypeEnum } from '@/enums/fileTypeEnum'
import { backgroundImageSize } from '@/settings/designSetting'
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
const StoreKey = StorageEnum.GO_USER_MEDIA_PHOTOS
/**
*
*/
type UploadCompletedEventType = {
fileName: string
url: string
}
const userPhotosList: ConfigType[] = getLocalStorage(StoreKey) || []
const uploadFile = (callback: Function | null = null) => {
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*' // 这里只允许图片类型
input.onchange = async () => {
if (!input.files || !input.files.length) return
const file = input.files[0]
const { name, size, type } = file
if (size > 1024 * 1024 * backgroundImageSize) {
window['$message'].warning(`图片超出 ${backgroundImageSize}M 限制,请重新上传!`)
return false
}
if (type !== FileTypeEnum.PNG && type !== FileTypeEnum.JPEG && type !== FileTypeEnum.GIF) {
window['$message'].warning('文件格式不符合,请重新上传!')
return false
}
const reader = new FileReader()
reader.onload = () => {
const eventObj: UploadCompletedEventType = { fileName: name, url: reader.result as string }
callback && callback(eventObj)
}
reader.readAsDataURL(file)
}
input.click()
}
const addConfig = {
...ImageConfig,
category: ChatCategoryEnum.PRIVATE,
categoryName: ChatCategoryEnumName.PRIVATE,
package: PackagesCategoryEnum.PHOTOS,
title: '点击上传图片',
image: 'upload.png',
redirectComponent: './components/Informations/Mores/Image', // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置
disabled: true,
clickHandle: (photoConfig: ConfigType) => {
goDialog({
message: `图片需小于 ${backgroundImageSize}M 且只暂存在浏览器中,请自行对接后端接口!`,
transformOrigin: 'center',
onPositiveCallback: () => {
uploadFile((e: UploadCompletedEventType) => {
// 和上传组件一样配置,更换标题,图片,预设数据
const packagesStore = usePackagesStore()
const newPhoto = {
...ImageConfig,
category: ChatCategoryEnum.PRIVATE,
categoryName: ChatCategoryEnumName.PRIVATE,
package: PackagesCategoryEnum.PHOTOS,
title: e.fileName,
image: e.url,
dataset: e.url,
redirectComponent: './components/Informations/Mores/Image' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置
}
userPhotosList.unshift(newPhoto)
// 存储在本地数据中
setLocalStorage(StoreKey, userPhotosList)
// 插入到上传按钮前的位置
packagesStore.addPhotos(newPhoto, 1)
})
}
})
}
}
export default [addConfig, ...userPhotosList]

View File

@ -2,10 +2,12 @@ import { PackagesCategoryEnum } from '@/packages/index.d'
import { ImageConfig } from '@/packages/components/Informations/Mores/Image/index' import { ImageConfig } from '@/packages/components/Informations/Mores/Image/index'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d' import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
// 远程共享库(调接口获取图像列表)
const imageList = [ const imageList = [
{ imageName: 'carousel1', imageUrl: 'https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel1.jpeg' }, { imageName: 'carousel1', imageUrl: 'https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel1.jpeg' },
{ imageName: 'carousel2', imageUrl: 'https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel2.jpeg' } { imageName: 'carousel2', imageUrl: 'https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel2.jpeg' }
] ]
const photoConfigList = imageList.map(i => ({ const photoConfigList = imageList.map(i => ({
...ImageConfig, ...ImageConfig,
category: ChatCategoryEnum.SHARE, category: ChatCategoryEnum.SHARE,
@ -14,7 +16,7 @@ const photoConfigList = imageList.map(i => ({
image: i.imageUrl, image: i.imageUrl,
dataset: i.imageUrl, dataset: i.imageUrl,
title: i.imageName, title: i.imageName,
virtualComponent: './components/Informations/Mores/Image' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置 redirectComponent: './components/Informations/Mores/Image' // 虚拟组件路径,尾部不跟 /’,相对于 /packages/index.ts 文件的位置
})) }))
export default photoConfigList export default photoConfigList

View File

@ -1,9 +1,9 @@
export enum ChatCategoryEnum { export enum ChatCategoryEnum {
MY = 'My', PRIVATE = 'Private',
SHARE = 'Share' SHARE = 'Share'
} }
export enum ChatCategoryEnumName { export enum ChatCategoryEnumName {
MY = '我的', PRIVATE = '私有图',
SHARE = '共享' SHARE = '共享'
} }

View File

@ -1,4 +1,4 @@
import My from './My' import Private from './Private'
import Share from './Share' import Share from './Share'
export const PhotoList = [...Share, ...My] export const PhotoList = [...Private, ...Share]

View File

@ -15,20 +15,34 @@ export enum ChartFrameEnum {
// 组件配置 // 组件配置
export type ConfigType = { export type ConfigType = {
// 组件 key
key: string key: string
// 画布组件 key
chartKey: string chartKey: string
// 右侧设置面板组件 key
conKey: string conKey: string
// 标题
title: string title: string
// 分类
category: string category: string
// 分类名称
categoryName: string categoryName: string
// 所属包
package: string package: string
// 归类
chartFrame?: ChartFrameEnum chartFrame?: ChartFrameEnum
// 预览图
image: string image: string
virtualComponent?: string // 虚拟组件Path指定后创建该组件时从指定路径创建 // 从指定路径创建创建该组件
dataset?: any // 组件预设的 dataset 值 redirectComponent?: string
disabled?: boolean // 禁用的 // 组件预设的 dataset 值(图片/图标)
clickHandle?: Function // 单击事件 dataset?: any
icon?: string // 图标 // 禁用 拖拽或双击生成组件
disabled?: boolean
// 图标
icon?: string
// 自定义单击事件
clickHandle?: Function
} }
// 数据请求 // 数据请求

View File

@ -21,9 +21,9 @@ export let packagesList: PackagesType = {
[PackagesCategoryEnum.CHARTS]: ChartList, [PackagesCategoryEnum.CHARTS]: ChartList,
[PackagesCategoryEnum.INFORMATIONS]: InformationList, [PackagesCategoryEnum.INFORMATIONS]: InformationList,
[PackagesCategoryEnum.TABLES]: TableList, [PackagesCategoryEnum.TABLES]: TableList,
[PackagesCategoryEnum.DECORATES]: DecorateList,
[PackagesCategoryEnum.PHOTOS]: PhotoList, [PackagesCategoryEnum.PHOTOS]: PhotoList,
[PackagesCategoryEnum.ICONS]: IconList, [PackagesCategoryEnum.ICONS]: IconList
[PackagesCategoryEnum.DECORATES]: DecorateList
} }
/** /**
@ -31,9 +31,10 @@ export let packagesList: PackagesType = {
* @param targetData * @param targetData
*/ */
export const createComponent = async (targetData: ConfigType) => { export const createComponent = async (targetData: ConfigType) => {
const { virtualComponent, category, key } = targetData const { redirectComponent, category, key } = targetData
const componentPath = virtualComponent // redirectComponent 是给图片组件库和图标组件库使用的
? `${virtualComponent}/config.ts` const componentPath = redirectComponent
? `${redirectComponent}/config.ts`
: `./components/${targetData.package}/${category}/${key}/config.ts` : `./components/${targetData.package}/${category}/${key}/config.ts`
const chart = await import(/* @vite-ignore */ componentPath) const chart = await import(/* @vite-ignore */ componentPath)
return new chart.default() return new chart.default()
@ -78,8 +79,8 @@ export const fetchConfigComponent = (dropData: ConfigType) => {
*/ */
export const fetchImages = async (targetData?: ConfigType) => { export const fetchImages = async (targetData?: ConfigType) => {
if (!targetData) return '' if (!targetData) return ''
// 判断图片是否为 url是则直接返回该 url // 正则判断图片是否为 url是则直接返回该 url
if (/^(?:https?):\/\/[^\s/.?#].[^\s]*/.test(targetData.image)) return targetData.image if (/^(http|https):\/\/([\w.]+\/?)\S*/.test(targetData.image)) return targetData.image
// 新数据动态处理 // 新数据动态处理
const { image, package: targetDataPackage } = targetData const { image, package: targetDataPackage } = targetData
// 兼容旧数据 // 兼容旧数据

View File

@ -61,14 +61,14 @@ import {
Pulse as PulseIcon, Pulse as PulseIcon,
Folder as FolderIcon, Folder as FolderIcon,
FolderOpen as FolderOpenIcon, FolderOpen as FolderOpenIcon,
Image as ImageIcon, ImageOutline as ImageIcon,
Images as ImagesIcon, Images as ImagesIcon,
List as ListIcon, List as ListIcon,
EyeOutline as EyeOutlineIcon, EyeOutline as EyeOutlineIcon,
EyeOffOutline as EyeOffOutlineIcon, EyeOffOutline as EyeOffOutlineIcon,
Albums as AlbumsIcon, Albums as AlbumsIcon,
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,
Airplane as AirPlaneIcon AirplaneOutline as AirPlaneOutlineIcon
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import { import {
@ -244,7 +244,7 @@ const ionicons5 = {
// 分析 // 分析
AnalyticsIcon, AnalyticsIcon,
// 飞机 // 飞机
AirPlaneIcon AirPlaneOutlineIcon
} }
const carbon = { const carbon = {

View File

@ -1,8 +1,9 @@
import { PackagesType, ConfigType } from '@/packages/index.d' import { PackagesType, ConfigType } from '@/packages/index.d'
export { ConfigType } export { ConfigType }
export { PackagesType } export { PackagesType }
export interface PackagesStoreType { export interface PackagesStoreType {
packagesList: PackagesType packagesList: PackagesType,
} newPhoto?: ConfigType
}

View File

@ -1,16 +1,23 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { PackagesStoreType, PackagesType } from './packagesStore.d' import { ConfigType, PackagesStoreType, PackagesType } from './packagesStore.d'
import { packagesList } from '@/packages/index' import { packagesList } from '@/packages/index'
// 组件 pakages // 组件 packages
export const usePackagesStore = defineStore({ export const usePackagesStore = defineStore({
id: 'usePackagesStore', id: 'usePackagesStore',
state: (): PackagesStoreType => ({ state: (): PackagesStoreType => ({
packagesList: Object.freeze(packagesList) packagesList: Object.freeze(packagesList),
}), newPhoto: undefined
getters: { }),
getPackagesList(): PackagesType { getters: {
return this.packagesList getPackagesList(): PackagesType {
} return this.packagesList
} }
}) },
actions: {
addPhotos(newPhoto: ConfigType, index: number) {
this.newPhoto = newPhoto
this.packagesList.Photos.splice(index, 0, newPhoto)
}
}
})

View File

@ -8,8 +8,8 @@
<!-- 每一项组件的渲染 --> <!-- 每一项组件的渲染 -->
<div <div
class="item-box" class="item-box"
v-for="(item, index) in menuOptions" v-for="item in menuOptions"
:key="index" :key="item.title"
draggable draggable
@dragstart="!item.disabled && dragStartHandle($event, item)" @dragstart="!item.disabled && dragStartHandle($event, item)"
@dragend="!item.disabled && dragendHandle" @dragend="!item.disabled && dragendHandle"
@ -55,7 +55,7 @@ import omit from 'lodash/omit'
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
defineProps({ const props = defineProps({
menuOptions: { menuOptions: {
type: Array as PropType<ConfigType[]>, type: Array as PropType<ConfigType[]>,
default: () => [] default: () => []
@ -97,7 +97,7 @@ const dblclickHandle = async (item: ConfigType) => {
componentInstall(item.conKey, fetchConfigComponent(item)) componentInstall(item.conKey, fetchConfigComponent(item))
// //
let newComponent: CreateComponentType = await createComponent(item) let newComponent: CreateComponentType = await createComponent(item)
if (item.virtualComponent) { if (item.redirectComponent) {
item.dataset && (newComponent.option.dataset = item.dataset) item.dataset && (newComponent.option.dataset = item.dataset)
newComponent.chartConfig.title = item.title newComponent.chartConfig.title = item.title
} }
@ -125,6 +125,10 @@ watch(
} }
} }
) )
watch(() => props.menuOptions, (n) => {
console.log(n)
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -177,7 +181,7 @@ $halfCenterHeight: 50px;
overflow: hidden; overflow: hidden;
.list-img { .list-img {
height: 100px; height: 100px;
width: 140px; max-width: 140px;
border-radius: 6px; border-radius: 6px;
@extend .go-transition; @extend .go-transition;
} }
@ -208,6 +212,9 @@ $halfCenterHeight: 50px;
.item-box { .item-box {
width: $halfItemWidth; width: $halfItemWidth;
max-width: $maxItemWidth; max-width: $maxItemWidth;
.list-img {
max-width: 76px;
}
} }
.list-center { .list-center {
height: $halfCenterHeight; height: $halfCenterHeight;

View File

@ -23,8 +23,11 @@ import { ref, watch, computed, reactive } from 'vue'
import { ConfigType } from '@/packages/index.d' import { ConfigType } from '@/packages/index.d'
import { useSettingStore } from '@/store/modules/settingStore/settingStore' import { useSettingStore } from '@/store/modules/settingStore/settingStore'
import { loadAsyncComponent } from '@/utils' import { loadAsyncComponent } from '@/utils'
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { PackagesCategoryEnum } from '@/packages/index.d'
const ChartsItemBox = loadAsyncComponent(() => import('../ChartsItemBox/index.vue')) const ChartsItemBox = loadAsyncComponent(() => import('../ChartsItemBox/index.vue'))
const packagesStore = usePackagesStore()
const props = defineProps({ const props = defineProps({
selectOptions: { selectOptions: {
@ -61,7 +64,7 @@ let packages = reactive<{
saveSelectOptions: {} saveSelectOptions: {}
}) })
const selectValue = ref<string>() const selectValue = ref<string>('all')
// //
const setSelectOptions = (categorys: any) => { const setSelectOptions = (categorys: any) => {
@ -79,7 +82,6 @@ watch(
if (!newData) return if (!newData) return
newData.list.forEach((e: ConfigType) => { newData.list.forEach((e: ConfigType) => {
const value: ConfigType[] = (packages.categorys as any)[e.category] const value: ConfigType[] = (packages.categorys as any)[e.category]
// @ts-ignore
packages.categorys[e.category] = value && value.length ? [...value, e] : [e] packages.categorys[e.category] = value && value.length ? [...value, e] : [e]
packages.categoryNames[e.category] = e.categoryName packages.categoryNames[e.category] = e.categoryName
packages.categorys['all'].push(e) packages.categorys['all'].push(e)
@ -100,6 +102,16 @@ watch(
} }
) )
watch(
() => packagesStore.newPhoto,
newPhoto => {
if (!newPhoto) return
const newPhotoCategory = newPhoto.category
packages.categorys[newPhotoCategory].splice(1, 0, newPhoto)
packages.categorys['all'].splice(1, 0, newPhoto)
}
)
// //
const clickItemHandle = (key: string) => { const clickItemHandle = (key: string) => {
packages.selectOptions = packages.categorys[key] packages.selectOptions = packages.categorys[key]

View File

@ -71,7 +71,7 @@ import { ref, onUnmounted } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { createComponent } from '@/packages' import { createComponent } from '@/packages'
import { ConfigType, CreateComponentType } from '@/packages/index.d' import { ConfigType, CreateComponentType } from '@/packages/index.d'
import { themeColor, MenuOptionsType } from '../../hooks/useAside.hook' import { themeColor } from '../../hooks/useLayout.hook'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { ChartModeEnum, ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' import { ChartModeEnum, ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore' import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
@ -158,7 +158,7 @@ const selectChartHandle = async (item: ConfigType) => {
componentInstall(item.conKey, fetchConfigComponent(item)) componentInstall(item.conKey, fetchConfigComponent(item))
// //
let newComponent: CreateComponentType = await createComponent(item) let newComponent: CreateComponentType = await createComponent(item)
if (item.virtualComponent) { if (item.redirectComponent) {
item.dataset && (newComponent.option.dataset = item.dataset) item.dataset && (newComponent.option.dataset = item.dataset)
newComponent.chartConfig.title = item.title newComponent.chartConfig.title = item.title
} }

View File

@ -1,13 +1,12 @@
import { shallowReactive, ref } from 'vue' import { ref, watch, computed } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { renderLang, renderIcon } from '@/utils' import { renderLang, renderIcon } from '@/utils'
import { themeColor, setItem, getCharts } from './useLayout.hook' import { themeColor, setItem, getCharts } from './useLayout.hook'
import { PackagesCategoryEnum, PackagesCategoryName, PackagesType } from '@/packages/index.d' import { PackagesCategoryEnum, PackagesCategoryName, ConfigType } from '@/packages/index.d'
// 图表
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore' import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
// 图标 // 图标
const { AirPlaneIcon, ImageIcon, BarChartIcon } = icon.ionicons5 const { AirPlaneOutlineIcon, ImageIcon, BarChartIcon } = icon.ionicons5
const { TableSplitIcon, RoadmapIcon, SpellCheckIcon, GraphicalDataFlowIcon } = icon.carbon const { TableSplitIcon, RoadmapIcon, SpellCheckIcon, GraphicalDataFlowIcon } = icon.carbon
// 图表 // 图表
@ -15,12 +14,9 @@ export type MenuOptionsType = {
key: string key: string
icon: ReturnType<typeof renderIcon> icon: ReturnType<typeof renderIcon>
label: ReturnType<typeof renderLang> label: ReturnType<typeof renderLang>
list: PackagesType list: ConfigType[]
} }
const { getPackagesList } = usePackagesStore()
const menuOptions: MenuOptionsType[] = []
const packagesListObj = { const packagesListObj = {
[PackagesCategoryEnum.CHARTS]: { [PackagesCategoryEnum.CHARTS]: {
icon: renderIcon(RoadmapIcon), icon: renderIcon(RoadmapIcon),
@ -34,53 +30,66 @@ const packagesListObj = {
icon: renderIcon(TableSplitIcon), icon: renderIcon(TableSplitIcon),
label: PackagesCategoryName.TABLES label: PackagesCategoryName.TABLES
}, },
[PackagesCategoryEnum.DECORATES]: {
icon: renderIcon(GraphicalDataFlowIcon),
label: PackagesCategoryName.DECORATES
},
[PackagesCategoryEnum.PHOTOS]: { [PackagesCategoryEnum.PHOTOS]: {
icon: renderIcon(ImageIcon), icon: renderIcon(ImageIcon),
label: PackagesCategoryName.PHOTOS label: PackagesCategoryName.PHOTOS
}, },
[PackagesCategoryEnum.ICONS]: { [PackagesCategoryEnum.ICONS]: {
icon: renderIcon(AirPlaneIcon), icon: renderIcon(AirPlaneOutlineIcon),
label: PackagesCategoryName.ICONS label: PackagesCategoryName.ICONS
},
[PackagesCategoryEnum.DECORATES]: {
icon: renderIcon(GraphicalDataFlowIcon),
label: PackagesCategoryName.DECORATES
} }
} }
// 处理列表 export const useAsideHook = () => {
const handlePackagesList = () => { const packagesStore = usePackagesStore()
for (const val in getPackagesList) { const menuOptions: MenuOptionsType[] = []
menuOptions.push({
key: val, // 处理列表
// @ts-ignore const handlePackagesList = () => {
icon: packagesListObj[val].icon, for (const val in packagesStore.getPackagesList) {
// @ts-ignore menuOptions.push({
label: packagesListObj[val].label, key: val,
// @ts-ignore // @ts-ignore
list: getPackagesList[val] icon: packagesListObj[val].icon,
}) // @ts-ignore
label: packagesListObj[val].label,
// @ts-ignore
list: packagesStore.getPackagesList[val]
})
}
}
handlePackagesList()
// 记录选中值
let beforeSelect: string = menuOptions[0]['key']
const selectValue = ref<string>(menuOptions[0]['key'])
// 选中的对象值
const selectOptions = ref(menuOptions[0])
// 点击 item
const clickItemHandle = (key: string, item: any) => {
selectOptions.value = item
// 处理折叠
if (beforeSelect === key) {
setItem(ChartLayoutStoreEnum.CHARTS, !getCharts.value, false)
} else {
setItem(ChartLayoutStoreEnum.CHARTS, true, false)
}
beforeSelect = key
}
return {
getCharts,
BarChartIcon,
themeColor,
selectOptions,
selectValue,
clickItemHandle,
menuOptions
} }
} }
handlePackagesList()
// 记录选中值
let beforeSelect: string = menuOptions[0]['key']
const selectValue = ref<string>(menuOptions[0]['key'])
// 选中的对象值
const selectOptions = ref(menuOptions[0])
// 点击 item
const clickItemHandle = (key: string, item: any) => {
selectOptions.value = item
// 处理折叠
if (beforeSelect === key) {
setItem(ChartLayoutStoreEnum.CHARTS, !getCharts.value, false)
} else {
setItem(ChartLayoutStoreEnum.CHARTS, true, false)
}
beforeSelect = key
}
export { getCharts, BarChartIcon, themeColor, selectOptions, selectValue, clickItemHandle, menuOptions }

View File

@ -1,123 +1,105 @@
<template> <template>
<!-- 左侧所有组件的展示列表 --> <!-- 左侧所有组件的展示列表 -->
<content-box <content-box class="go-content-charts" :class="{ scoped: !getCharts }" title="组件" :depth="1" :backIcon="false">
class="go-content-charts" <template #icon>
:class="{ scoped: !getCharts }" <n-icon size="14" :depth="2">
title="组件" <bar-chart-icon></bar-chart-icon>
:depth="1" </n-icon>
:backIcon="false" </template>
>
<template #icon> <template #top-right>
<n-icon size="14" :depth="2"> <charts-search v-show="getCharts" :menuOptions="menuOptions"></charts-search>
<bar-chart-icon></bar-chart-icon> </template>
</n-icon> <!-- 图表 -->
</template> <aside>
<div class="menu-width-box">
<template #top-right> <n-menu
<charts-search v-show="getCharts" :menuOptions="menuOptions"></charts-search> class="menu-width"
</template> v-model:value="selectValue"
<!-- 图表 --> :options="menuOptions"
<aside> :icon-size="16"
<div class="menu-width-box"> :indent="18"
<n-menu @update:value="clickItemHandle"
class="menu-width" ></n-menu>
v-model:value="selectValue" <div class="menu-component-box">
:options="menuOptions" <go-skeleton :load="!selectOptions" round text :repeat="2" style="width: 90%"></go-skeleton>
:icon-size="16" <charts-option-content
:indent="18" v-if="selectOptions"
@update:value="clickItemHandle" :selectOptions="selectOptions"
></n-menu> :key="selectValue"
<div class="menu-component-box"> ></charts-option-content>
<go-skeleton </div>
:load="!selectOptions" </div>
round </aside>
text </content-box>
:repeat="2" </template>
style="width: 90%"
></go-skeleton> <script setup lang="ts">
<charts-option-content import { ContentBox } from '../ContentBox/index'
v-if="selectOptions" import { ChartsOptionContent } from './components/ChartsOptionContent'
:selectOptions="selectOptions" import { ChartsSearch } from './components/ChartsSearch'
:key="selectValue" import { useAsideHook } from './hooks/useAside.hook'
></charts-option-content>
</div> const { getCharts, BarChartIcon, themeColor, selectOptions, selectValue, clickItemHandle, menuOptions } = useAsideHook()
</div> </script>
</aside>
</content-box> <style lang="scss" scoped>
</template> /* 整体宽度 */
$width: 330px;
<script setup lang="ts"> /* 列表的宽度 */
import { ContentBox } from '../ContentBox/index' $widthScoped: 65px;
import { ChartsOptionContent } from './components/ChartsOptionContent' /* 此高度与 ContentBox 组件关联 */
import { ChartsSearch } from './components/ChartsSearch' $topHeight: 40px;
import {
getCharts, @include go(content-charts) {
BarChartIcon, width: $width;
themeColor, @extend .go-transition;
selectOptions, &.scoped,
selectValue, .menu-width {
clickItemHandle, width: $widthScoped;
menuOptions, }
} from './hooks/useAside.hook' .menu-width-box {
</script> display: flex;
height: calc(100vh - #{$--header-height} - #{$topHeight});
<style lang="scss" scoped> .menu-width {
/* 整体宽度 */ flex-shrink: 0;
$width: 330px; @include fetch-bg-color('background-color2');
/* 列表的宽度 */ }
$widthScoped: 65px; .menu-component-box {
/* 此高度与 ContentBox 组件关联 */ flex-shrink: 0;
$topHeight: 40px; width: $width - $widthScoped;
overflow: hidden;
@include go(content-charts) { }
width: $width; }
@extend .go-transition; @include deep() {
&.scoped, .menu-width {
.menu-width { .n-menu-item {
width: $widthScoped; height: auto !important;
} &.n-menu-item--selected {
.menu-width-box { &::after {
display: flex; content: '';
height: calc(100vh - #{$--header-height} - #{$topHeight}); position: absolute;
.menu-width { left: 2px;
flex-shrink: 0; top: 0;
@include fetch-bg-color('background-color2'); height: 100%;
} width: 3px;
.menu-component-box { background-color: v-bind('themeColor');
flex-shrink: 0; border-top-right-radius: 3px;
width: $width - $widthScoped; border-bottom-right-radius: 3px;
overflow: hidden; }
} }
} .n-menu-item-content {
@include deep() { display: flex;
.menu-width { flex-direction: column;
.n-menu-item { padding: 6px 12px !important;
height: auto !important; font-size: 14px !important;
&.n-menu-item--selected { }
&::after { .n-menu-item-content__icon {
content: ''; font-size: 18px !important;
position: absolute; margin-right: 0 !important;
left: 2px; }
top: 0; }
height: 100%; }
width: 3px; }
background-color: v-bind('themeColor'); }
border-top-right-radius: 3px; </style>
border-bottom-right-radius: 3px;
}
}
.n-menu-item-content {
display: flex;
flex-direction: column;
padding: 6px 12px !important;
font-size: 14px !important;
}
.n-menu-item-content__icon {
font-size: 18px !important;
margin-right: 0 !important;
}
}
}
}
}
</style>

View File

@ -33,7 +33,7 @@ export const dragHandle = async (e: DragEvent) => {
// 创建新图表组件 // 创建新图表组件
let newComponent: CreateComponentType = await createComponent(dropData) let newComponent: CreateComponentType = await createComponent(dropData)
if (dropData.virtualComponent) { if (dropData.redirectComponent) {
dropData.dataset && (newComponent.option.dataset = dropData.dataset) dropData.dataset && (newComponent.option.dataset = dropData.dataset)
newComponent.chartConfig.title = dropData.title newComponent.chartConfig.title = dropData.title
} }

View File

@ -1,159 +1,159 @@
<template> <template>
<div class="go-content-layers-list-item" :class="{ hover, select, 'list-mini': selectText }"> <div class="go-content-layers-list-item" :class="{ hover, select, 'list-mini': selectText }">
<div class="go-flex-center item-content"> <div class="go-flex-center item-content">
<n-image <n-image
class="list-img" class="list-img"
object-fit="contain" object-fit="contain"
preview-disabled preview-disabled
:src="imageInfo" :src="imageInfo"
:fallback-src="requireErrorImg()" :fallback-src="requireErrorImg()"
></n-image> ></n-image>
<n-ellipsis style="margin-right: auto"> <n-ellipsis style="margin-right: auto">
<span class="list-text"> <span class="list-text">
{{ props.componentData.chartConfig.title }} {{ props.componentData.chartConfig.title }}
</span> </span>
</n-ellipsis> </n-ellipsis>
<layers-status :isGroup="isGroup" :hover="hover" :status="status"></layers-status> <layers-status :isGroup="isGroup" :hover="hover" :status="status"></layers-status>
</div> </div>
<div :class="{ 'select-modal': select }"></div> <div :class="{ 'select-modal': select }"></div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, PropType, ref, watch } from 'vue' import { computed, PropType, ref } from 'vue'
import { requireErrorImg } from '@/utils' import { requireErrorImg } from '@/utils'
import { useDesignStore } from '@/store/modules/designStore/designStore' import { useDesignStore } from '@/store/modules/designStore/designStore'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { LayerModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' import { LayerModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { fetchImages } from '@/packages' import { fetchImages } from '@/packages'
import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d' import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
import { LayersStatus } from '../LayersStatus/index' import { LayersStatus } from '../LayersStatus/index'
const props = defineProps({ const props = defineProps({
componentData: { componentData: {
type: Object as PropType<CreateComponentType | CreateComponentGroupType>, type: Object as PropType<CreateComponentType | CreateComponentGroupType>,
required: true required: true
}, },
isGroup: { isGroup: {
type: Boolean, type: Boolean,
default: false default: false
}, },
layerMode: { layerMode: {
type: String as PropType<LayerModeEnum>, type: String as PropType<LayerModeEnum>,
default: LayerModeEnum.THUMBNAIL default: LayerModeEnum.THUMBNAIL
} }
}) })
// //
const designStore = useDesignStore() const designStore = useDesignStore()
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const imageInfo = ref('') const imageInfo = ref('')
// //
const fetchImageUrl = async () => { const fetchImageUrl = async () => {
imageInfo.value = await fetchImages(props.componentData.chartConfig) imageInfo.value = await fetchImages(props.componentData.chartConfig)
} }
fetchImageUrl() fetchImageUrl()
// //
const themeColor = computed(() => { const themeColor = computed(() => {
return designStore.getAppTheme return designStore.getAppTheme
}) })
// //
const select = computed(() => { const select = computed(() => {
const id = props.componentData.id const id = props.componentData.id
return chartEditStore.getTargetChart.selectId.find((e: string) => e === id) return chartEditStore.getTargetChart.selectId.find((e: string) => e === id)
}) })
// //
const hover = computed(() => { const hover = computed(() => {
return props.componentData.id === chartEditStore.getTargetChart.hoverId return props.componentData.id === chartEditStore.getTargetChart.hoverId
}) })
// / // /
const status = computed(() => { const status = computed(() => {
return props.componentData.status return props.componentData.status
}) })
// //
const selectText = computed(() => { const selectText = computed(() => {
return props.layerMode === LayerModeEnum.TEXT return props.layerMode === LayerModeEnum.TEXT
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$centerHeight: 52px; $centerHeight: 52px;
$centerMiniHeight: 28px; $centerMiniHeight: 28px;
$textSize: 10px; $textSize: 10px;
@include go(content-layers-list-item) { @include go(content-layers-list-item) {
position: relative; position: relative;
height: $centerHeight; height: $centerHeight;
width: 90%; width: 90%;
margin: 5px 5%; margin: 5px 5%;
margin-bottom: 5px; margin-bottom: 5px;
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0); border: 1px solid rgba(0, 0, 0, 0);
@extend .go-transition-quick; @extend .go-transition-quick;
&.hover, &.hover,
&:hover { &:hover {
@include fetch-bg-color('background-color4'); @include fetch-bg-color('background-color4');
} }
&:hover { &:hover {
@include deep() { @include deep() {
.icon-item { .icon-item {
opacity: 1; opacity: 1;
} }
} }
} }
.select-modal, .select-modal,
.item-content { .item-content {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
} }
.item-content { .item-content {
z-index: 1; z-index: 1;
padding: 6px 5px; padding: 6px 5px;
justify-content: start !important; justify-content: start !important;
width: calc(100% - 10px); width: calc(100% - 10px);
height: calc(100% - 10px); height: calc(100% - 10px);
} }
.select-modal { .select-modal {
width: 100%; width: 100%;
height: 100%; height: 100%;
opacity: 0.3; opacity: 0.3;
background-color: v-bind('themeColor'); background-color: v-bind('themeColor');
} }
.list-img { .list-img {
flex-shrink: 0; flex-shrink: 0;
height: $centerHeight; height: $centerHeight;
border-radius: 5px; border-radius: 5px;
overflow: hidden; overflow: hidden;
border: none !important; border: none !important;
padding: 2px; padding: 2px;
@include hover-border-color('hover-border-color'); @include hover-border-color('hover-border-color');
} }
.list-text { .list-text {
padding-left: 6px; padding-left: 6px;
font-size: $textSize; font-size: $textSize;
} }
/* 选中样式 */ /* 选中样式 */
&.select { &.select {
border: 1px solid v-bind('themeColor'); border: 1px solid v-bind('themeColor');
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
} }
// mini // mini
&.list-mini { &.list-mini {
height: $centerMiniHeight; height: $centerMiniHeight;
} }
} }
</style> </style>

View File

@ -132,7 +132,7 @@ export const useSync = () => {
) => { ) => {
// 补充 class 上的方法 // 补充 class 上的方法
let newComponent: CreateComponentType = await createComponent(_componentInstance.chartConfig) let newComponent: CreateComponentType = await createComponent(_componentInstance.chartConfig)
if (_componentInstance.chartConfig.virtualComponent) { if (_componentInstance.chartConfig.redirectComponent) {
_componentInstance.chartConfig.dataset && (newComponent.option.dataset = _componentInstance.chartConfig.dataset) _componentInstance.chartConfig.dataset && (newComponent.option.dataset = _componentInstance.chartConfig.dataset)
newComponent.chartConfig.title = _componentInstance.chartConfig.title newComponent.chartConfig.title = _componentInstance.chartConfig.title
} }