55 changed files with 3534 additions and 772 deletions
@ -1,249 +1,251 @@ |
|||
<script lang="ts" setup> |
|||
import { ref } from 'vue'; |
|||
// import { ref } from 'vue'; |
|||
|
|||
// import { echartsInstance as echarts } from '@vben/chart-ui'; |
|||
|
|||
defineOptions({ name: 'WelCome' }); |
|||
import { Dashboard } from '@vben/universal-ui'; |
|||
import { echartsInstance as echarts } from '@vben/chart-ui'; |
|||
const cardList = ref([ |
|||
{ |
|||
title: '访问数', |
|||
extra: '月', |
|||
leftContent: '2000', |
|||
rightContent: 'flat-color-icons:conference-call', |
|||
leftFooter: '总访问数', |
|||
color: 'green', |
|||
rightFooter: '5000', |
|||
}, |
|||
{ |
|||
title: '销售额', |
|||
extra: '日', |
|||
leftContent: '$1350', |
|||
rightContent: 'flat-color-icons:sales-performance', |
|||
leftFooter: '总销售额', |
|||
color: 'red', |
|||
rightFooter: '$550000', |
|||
}, |
|||
]); |
|||
const chartTabs = ref([ |
|||
{ |
|||
name: '1', |
|||
title: '流量趋势', |
|||
option: { |
|||
color: ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'], |
|||
|
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
type: 'cross', |
|||
// label: { |
|||
// backgroundColor: '#6a7985', |
|||
// }, |
|||
}, |
|||
}, |
|||
legend: { |
|||
data: ['Line 1', 'Line 2', 'Line 3', 'Line 4', 'Line 5'], |
|||
}, |
|||
toolbox: { |
|||
feature: { |
|||
saveAsImage: {}, |
|||
}, |
|||
}, |
|||
grid: { |
|||
left: '3%', |
|||
right: '4%', |
|||
bottom: '3%', |
|||
containLabel: true, |
|||
}, |
|||
xAxis: [ |
|||
{ |
|||
type: 'category', |
|||
boundaryGap: false, |
|||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
|||
}, |
|||
], |
|||
yAxis: [ |
|||
{ |
|||
type: 'value', |
|||
}, |
|||
], |
|||
series: [ |
|||
{ |
|||
name: 'Line 1', |
|||
type: 'line', |
|||
stack: 'Total', |
|||
smooth: true, |
|||
lineStyle: { |
|||
width: 0, |
|||
}, |
|||
showSymbol: false, |
|||
areaStyle: { |
|||
opacity: 0.8, |
|||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
{ |
|||
offset: 0, |
|||
color: 'rgb(128, 255, 165)', |
|||
}, |
|||
{ |
|||
offset: 1, |
|||
color: 'rgb(1, 191, 236)', |
|||
}, |
|||
]), |
|||
}, |
|||
emphasis: { |
|||
focus: 'series', |
|||
}, |
|||
data: [140, 232, 101, 264, 90, 340, 250], |
|||
}, |
|||
{ |
|||
name: 'Line 2', |
|||
type: 'line', |
|||
stack: 'Total', |
|||
smooth: true, |
|||
lineStyle: { |
|||
width: 0, |
|||
}, |
|||
showSymbol: false, |
|||
areaStyle: { |
|||
opacity: 0.8, |
|||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
{ |
|||
offset: 0, |
|||
color: 'rgb(0, 221, 255)', |
|||
}, |
|||
{ |
|||
offset: 1, |
|||
color: 'rgb(77, 119, 255)', |
|||
}, |
|||
]), |
|||
}, |
|||
emphasis: { |
|||
focus: 'series', |
|||
}, |
|||
data: [120, 282, 111, 234, 220, 340, 310], |
|||
}, |
|||
{ |
|||
name: 'Line 3', |
|||
type: 'line', |
|||
stack: 'Total', |
|||
smooth: true, |
|||
lineStyle: { |
|||
width: 0, |
|||
}, |
|||
showSymbol: false, |
|||
areaStyle: { |
|||
opacity: 0.8, |
|||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
{ |
|||
offset: 0, |
|||
color: 'rgb(55, 162, 255)', |
|||
}, |
|||
{ |
|||
offset: 1, |
|||
color: 'rgb(116, 21, 219)', |
|||
}, |
|||
]), |
|||
}, |
|||
emphasis: { |
|||
focus: 'series', |
|||
}, |
|||
data: [320, 132, 201, 334, 190, 130, 220], |
|||
}, |
|||
{ |
|||
name: 'Line 4', |
|||
type: 'line', |
|||
stack: 'Total', |
|||
smooth: true, |
|||
lineStyle: { |
|||
width: 0, |
|||
}, |
|||
showSymbol: false, |
|||
areaStyle: { |
|||
opacity: 0.8, |
|||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
{ |
|||
offset: 0, |
|||
color: 'rgb(255, 0, 135)', |
|||
}, |
|||
{ |
|||
offset: 1, |
|||
color: 'rgb(135, 0, 157)', |
|||
}, |
|||
]), |
|||
}, |
|||
emphasis: { |
|||
focus: 'series', |
|||
}, |
|||
data: [220, 402, 231, 134, 190, 230, 120], |
|||
}, |
|||
{ |
|||
name: 'Line 5', |
|||
type: 'line', |
|||
stack: 'Total', |
|||
smooth: true, |
|||
lineStyle: { |
|||
width: 0, |
|||
}, |
|||
showSymbol: false, |
|||
label: { |
|||
show: true, |
|||
position: 'top', |
|||
}, |
|||
areaStyle: { |
|||
opacity: 0.8, |
|||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
{ |
|||
offset: 0, |
|||
color: 'rgb(255, 191, 0)', |
|||
}, |
|||
{ |
|||
offset: 1, |
|||
color: 'rgb(224, 62, 76)', |
|||
}, |
|||
]), |
|||
}, |
|||
emphasis: { |
|||
focus: 'series', |
|||
}, |
|||
data: [220, 302, 181, 234, 210, 290, 150], |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
name: '2', |
|||
title: '访问量', |
|||
option: { |
|||
xAxis: { |
|||
type: 'category', |
|||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
|||
}, |
|||
yAxis: { |
|||
type: 'value', |
|||
}, |
|||
series: [ |
|||
{ |
|||
data: [ |
|||
120, |
|||
{ |
|||
value: 200, |
|||
itemStyle: { |
|||
color: '#a90000', |
|||
}, |
|||
}, |
|||
150, |
|||
80, |
|||
70, |
|||
110, |
|||
130, |
|||
], |
|||
type: 'bar', |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
]); |
|||
// const cardList = ref([ |
|||
// { |
|||
// color: 'green', |
|||
// extra: '月', |
|||
// leftContent: '2000', |
|||
// leftFooter: '总访问数', |
|||
// rightContent: 'flat-color-icons:conference-call', |
|||
// rightFooter: '5000', |
|||
// title: '访问数', |
|||
// }, |
|||
// { |
|||
// color: 'red', |
|||
// extra: '日', |
|||
// leftContent: '$1350', |
|||
// leftFooter: '总销售额', |
|||
// rightContent: 'flat-color-icons:sales-performance', |
|||
// rightFooter: '$550000', |
|||
// title: '销售额', |
|||
// }, |
|||
// ]); |
|||
// const chartTabs = ref([ |
|||
// { |
|||
// name: '1', |
|||
// option: { |
|||
// color: ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'], |
|||
|
|||
// grid: { |
|||
// bottom: '3%', |
|||
// containLabel: true, |
|||
// left: '3%', |
|||
// right: '4%', |
|||
// }, |
|||
// legend: { |
|||
// data: ['Line 1', 'Line 2', 'Line 3', 'Line 4', 'Line 5'], |
|||
// }, |
|||
// series: [ |
|||
// { |
|||
// areaStyle: { |
|||
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
// { |
|||
// color: 'rgb(128, 255, 165)', |
|||
// offset: 0, |
|||
// }, |
|||
// { |
|||
// color: 'rgb(1, 191, 236)', |
|||
// offset: 1, |
|||
// }, |
|||
// ]), |
|||
// opacity: 0.8, |
|||
// }, |
|||
// data: [140, 232, 101, 264, 90, 340, 250], |
|||
// emphasis: { |
|||
// focus: 'series', |
|||
// }, |
|||
// lineStyle: { |
|||
// width: 0, |
|||
// }, |
|||
// name: 'Line 1', |
|||
// showSymbol: false, |
|||
// smooth: true, |
|||
// stack: 'Total', |
|||
// type: 'line', |
|||
// }, |
|||
// { |
|||
// areaStyle: { |
|||
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
// { |
|||
// color: 'rgb(0, 221, 255)', |
|||
// offset: 0, |
|||
// }, |
|||
// { |
|||
// color: 'rgb(77, 119, 255)', |
|||
// offset: 1, |
|||
// }, |
|||
// ]), |
|||
// opacity: 0.8, |
|||
// }, |
|||
// data: [120, 282, 111, 234, 220, 340, 310], |
|||
// emphasis: { |
|||
// focus: 'series', |
|||
// }, |
|||
// lineStyle: { |
|||
// width: 0, |
|||
// }, |
|||
// name: 'Line 2', |
|||
// showSymbol: false, |
|||
// smooth: true, |
|||
// stack: 'Total', |
|||
// type: 'line', |
|||
// }, |
|||
// { |
|||
// areaStyle: { |
|||
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
// { |
|||
// color: 'rgb(55, 162, 255)', |
|||
// offset: 0, |
|||
// }, |
|||
// { |
|||
// color: 'rgb(116, 21, 219)', |
|||
// offset: 1, |
|||
// }, |
|||
// ]), |
|||
// opacity: 0.8, |
|||
// }, |
|||
// data: [320, 132, 201, 334, 190, 130, 220], |
|||
// emphasis: { |
|||
// focus: 'series', |
|||
// }, |
|||
// lineStyle: { |
|||
// width: 0, |
|||
// }, |
|||
// name: 'Line 3', |
|||
// showSymbol: false, |
|||
// smooth: true, |
|||
// stack: 'Total', |
|||
// type: 'line', |
|||
// }, |
|||
// { |
|||
// areaStyle: { |
|||
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
// { |
|||
// color: 'rgb(255, 0, 135)', |
|||
// offset: 0, |
|||
// }, |
|||
// { |
|||
// color: 'rgb(135, 0, 157)', |
|||
// offset: 1, |
|||
// }, |
|||
// ]), |
|||
// opacity: 0.8, |
|||
// }, |
|||
// data: [220, 402, 231, 134, 190, 230, 120], |
|||
// emphasis: { |
|||
// focus: 'series', |
|||
// }, |
|||
// lineStyle: { |
|||
// width: 0, |
|||
// }, |
|||
// name: 'Line 4', |
|||
// showSymbol: false, |
|||
// smooth: true, |
|||
// stack: 'Total', |
|||
// type: 'line', |
|||
// }, |
|||
// { |
|||
// areaStyle: { |
|||
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
// { |
|||
// color: 'rgb(255, 191, 0)', |
|||
// offset: 0, |
|||
// }, |
|||
// { |
|||
// color: 'rgb(224, 62, 76)', |
|||
// offset: 1, |
|||
// }, |
|||
// ]), |
|||
// opacity: 0.8, |
|||
// }, |
|||
// data: [220, 302, 181, 234, 210, 290, 150], |
|||
// emphasis: { |
|||
// focus: 'series', |
|||
// }, |
|||
// label: { |
|||
// position: 'top', |
|||
// show: true, |
|||
// }, |
|||
// lineStyle: { |
|||
// width: 0, |
|||
// }, |
|||
// name: 'Line 5', |
|||
// showSymbol: false, |
|||
// smooth: true, |
|||
// stack: 'Total', |
|||
// type: 'line', |
|||
// }, |
|||
// ], |
|||
// toolbox: { |
|||
// feature: { |
|||
// saveAsImage: {}, |
|||
// }, |
|||
// }, |
|||
// tooltip: { |
|||
// axisPointer: { |
|||
// type: 'cross', |
|||
// // label: { |
|||
// // backgroundColor: '#6a7985', |
|||
// // }, |
|||
// }, |
|||
// trigger: 'axis', |
|||
// }, |
|||
// xAxis: [ |
|||
// { |
|||
// boundaryGap: false, |
|||
// data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
|||
// type: 'category', |
|||
// }, |
|||
// ], |
|||
// yAxis: [ |
|||
// { |
|||
// type: 'value', |
|||
// }, |
|||
// ], |
|||
// }, |
|||
// title: '流量趋势', |
|||
// }, |
|||
// { |
|||
// name: '2', |
|||
// option: { |
|||
// series: [ |
|||
// { |
|||
// data: [ |
|||
// 120, |
|||
// { |
|||
// itemStyle: { |
|||
// color: '#a90000', |
|||
// }, |
|||
// value: 200, |
|||
// }, |
|||
// 150, |
|||
// 80, |
|||
// 70, |
|||
// 110, |
|||
// 130, |
|||
// ], |
|||
// type: 'bar', |
|||
// }, |
|||
// ], |
|||
// xAxis: { |
|||
// data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
|||
// type: 'category', |
|||
// }, |
|||
// yAxis: { |
|||
// type: 'value', |
|||
// }, |
|||
// }, |
|||
// title: '访问量', |
|||
// }, |
|||
// ]); |
|||
</script> |
|||
|
|||
<template> |
|||
<Dashboard :cardList="cardList" :chartTabs="chartTabs"></Dashboard> |
|||
<div>dashboard</div> |
|||
<!-- <Dashboard :card-list="cardList" :chart-tabs="chartTabs" /> --> |
|||
</template> |
|||
|
|||
@ -0,0 +1,7 @@ |
|||
import { defineBuildConfig } from 'unbuild'; |
|||
|
|||
export default defineBuildConfig({ |
|||
clean: true, |
|||
declaration: true, |
|||
entries: ['src/index'], |
|||
}); |
|||
@ -0,0 +1,42 @@ |
|||
{ |
|||
"name": "@vben-core/colorful", |
|||
"version": "5.0.0", |
|||
"homepage": "https://github.com/vbenjs/vue-vben-admin", |
|||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git", |
|||
"directory": "packages/@vben-core/shared/colorful" |
|||
}, |
|||
"license": "MIT", |
|||
"type": "module", |
|||
"scripts": { |
|||
"build": "pnpm unbuild", |
|||
"stub": "pnpm unbuild --stub" |
|||
}, |
|||
"files": [ |
|||
"dist" |
|||
], |
|||
"sideEffects": false, |
|||
"main": "./dist/index.mjs", |
|||
"module": "./dist/index.mjs", |
|||
"exports": { |
|||
".": { |
|||
"types": "./src/index.ts", |
|||
"development": "./src/index.ts", |
|||
"default": "./dist/index.mjs" |
|||
} |
|||
}, |
|||
"publishConfig": { |
|||
"exports": { |
|||
".": { |
|||
"types": "./dist/index.d.ts", |
|||
"default": "./dist/index.mjs" |
|||
} |
|||
} |
|||
}, |
|||
"dependencies": { |
|||
"@ant-design/colors": "^7.0.2", |
|||
"@ctrl/tinycolor": "4.1.0" |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
import { generate } from '@ant-design/colors'; |
|||
|
|||
import { convertToHslCssVar } from './utils'; |
|||
|
|||
export * from '@ant-design/colors'; |
|||
|
|||
interface Opts { |
|||
backgroundColor?: string; |
|||
theme?: 'dark' | 'default'; |
|||
} |
|||
|
|||
interface ColorItem { |
|||
alias?: string; |
|||
color: string; |
|||
name: string; |
|||
} |
|||
|
|||
function generatorColorVariables(colorItems: ColorItem[], opts?: Opts) { |
|||
const colorVariables: Record<string, string> = {}; |
|||
|
|||
colorItems.forEach(({ alias, color, name }) => { |
|||
if (color) { |
|||
const colors = generate(color, opts); |
|||
let mainColor = colors[5]; |
|||
colors.forEach((colorValue, colorIndex) => { |
|||
const hslColor = convertToHslCssVar(colorValue); |
|||
colorVariables[`--${name}-${colorIndex + 1}00`] = hslColor; |
|||
if (alias) { |
|||
colorVariables[`--${alias}-${colorIndex + 1}00`] = hslColor; |
|||
} |
|||
|
|||
if (colorIndex === 5) { |
|||
mainColor = hslColor; |
|||
} |
|||
}); |
|||
if (alias) { |
|||
colorVariables[`--${alias}`] = mainColor; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
return colorVariables; |
|||
} |
|||
|
|||
export { generatorColorVariables }; |
|||
@ -0,0 +1,2 @@ |
|||
export * from './generator'; |
|||
export * from './utils'; |
|||
@ -1,6 +1,6 @@ |
|||
import { describe, expect, it } from 'vitest'; |
|||
|
|||
import { convertToHsl, convertToHslCssVar, isValidColor } from './color'; |
|||
import { convertToHsl, convertToHslCssVar, isValidColor } from './utils'; |
|||
|
|||
describe('color conversion functions', () => { |
|||
it('should correctly convert color to HSL format', () => { |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"$schema": "https://json.schemastore.org/tsconfig", |
|||
"extends": "@vben/tsconfig/library.json", |
|||
"include": ["src"], |
|||
"exclude": ["node_modules"] |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
import { expect, it } from 'vitest'; |
|||
|
|||
import { updateCSSVariables } from './update-css-variables'; |
|||
|
|||
it('updateCSSVariables should update CSS variables in :root selector', () => { |
|||
// 模拟初始的内联样式表内容
|
|||
const initialStyleContent = ':root { --primaryColor: red; }'; |
|||
document.head.innerHTML = `<style id="custom-styles">${initialStyleContent}</style>`; |
|||
|
|||
// 要更新的CSS变量和它们的新值
|
|||
const updatedVariables = { |
|||
fontSize: '16px', |
|||
primaryColor: 'blue', |
|||
secondaryColor: 'green', |
|||
}; |
|||
|
|||
// 调用函数来更新CSS变量
|
|||
updateCSSVariables(updatedVariables, 'custom-styles'); |
|||
|
|||
// 获取更新后的样式内容
|
|||
const styleElement = document.querySelector('#custom-styles'); |
|||
const updatedStyleContent = styleElement ? styleElement.textContent : ''; |
|||
|
|||
// 检查更新后的样式内容是否包含正确的更新值
|
|||
expect( |
|||
updatedStyleContent?.includes('primaryColor: blue;') && |
|||
updatedStyleContent?.includes('secondaryColor: green;') && |
|||
updatedStyleContent?.includes('fontSize: 16px;'), |
|||
).toBe(true); |
|||
}); |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* 更新 CSS 变量的函数 |
|||
* @param variables 要更新的 CSS 变量与其新值的映射 |
|||
*/ |
|||
function updateCSSVariables( |
|||
variables: { [key: string]: string }, |
|||
id = '__vben-styles__', |
|||
): void { |
|||
// 获取或创建内联样式表元素
|
|||
const styleElement = |
|||
document.querySelector(`#${id}`) || document.createElement('style'); |
|||
|
|||
styleElement.id = id; |
|||
|
|||
// 构建要更新的 CSS 变量的样式文本
|
|||
let cssText = ':root {'; |
|||
for (const key in variables) { |
|||
if (Object.prototype.hasOwnProperty.call(variables, key)) { |
|||
cssText += `${key}: ${variables[key]};`; |
|||
} |
|||
} |
|||
cssText += '}'; |
|||
|
|||
// 将样式文本赋值给内联样式表
|
|||
styleElement.textContent = cssText; |
|||
|
|||
// 将内联样式表添加到文档头部
|
|||
if (!document.querySelector(`#${id}`)) { |
|||
setTimeout(() => { |
|||
document.head.append(styleElement); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
export { updateCSSVariables }; |
|||
@ -0,0 +1,137 @@ |
|||
<script setup lang="ts"> |
|||
import type { BuiltinThemeType } from '@vben/types'; |
|||
|
|||
import { computed, ref } from 'vue'; |
|||
|
|||
import { $t } from '@vben/locales'; |
|||
import { TinyColor, convertToHsl } from '@vben-core/colorful'; |
|||
import { MdiEditBoxOutline } from '@vben-core/iconify'; |
|||
import { |
|||
BUILT_IN_THEME_PRESETS, |
|||
type BuiltinThemePreset, |
|||
} from '@vben-core/preferences'; |
|||
|
|||
defineOptions({ |
|||
name: 'PreferenceBuiltinTheme', |
|||
}); |
|||
|
|||
const props = defineProps<{ isDark: boolean }>(); |
|||
|
|||
const colorInput = ref(); |
|||
const modelValue = defineModel<BuiltinThemeType>({ default: 'default' }); |
|||
const themeColorPrimary = defineModel<string>('themeColorPrimary'); |
|||
|
|||
const inputValue = computed(() => { |
|||
return new TinyColor(themeColorPrimary.value).toHexString(); |
|||
}); |
|||
|
|||
function typeView(name: BuiltinThemeType) { |
|||
switch (name) { |
|||
case 'default': { |
|||
return $t('preferences.theme.default'); |
|||
} |
|||
case 'violet': { |
|||
return $t('preferences.theme.violet'); |
|||
} |
|||
case 'pink': { |
|||
return $t('preferences.theme.pink'); |
|||
} |
|||
case 'rose': { |
|||
return $t('preferences.theme.rose'); |
|||
} |
|||
case 'sky-blue': { |
|||
return $t('preferences.theme.sky-blue'); |
|||
} |
|||
case 'deep-blue': { |
|||
return $t('preferences.theme.deep-blue'); |
|||
} |
|||
|
|||
case 'green': { |
|||
return $t('preferences.theme.green'); |
|||
} |
|||
case 'deep-green': { |
|||
return $t('preferences.theme.deep-green'); |
|||
} |
|||
case 'orange': { |
|||
return $t('preferences.theme.orange'); |
|||
} |
|||
case 'yellow': { |
|||
return $t('preferences.theme.yellow'); |
|||
} |
|||
case 'zinc': { |
|||
return $t('preferences.theme.zinc'); |
|||
} |
|||
case 'neutral': { |
|||
return $t('preferences.theme.neutral'); |
|||
} |
|||
case 'slate': { |
|||
return $t('preferences.theme.slate'); |
|||
} |
|||
case 'gray': { |
|||
return $t('preferences.theme.gray'); |
|||
} |
|||
case 'custom': { |
|||
return $t('preferences.theme.custom'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function handleSelect(theme: BuiltinThemePreset) { |
|||
modelValue.value = theme.type; |
|||
const primaryColor = props.isDark |
|||
? theme.darkPrimaryColor || theme.primaryColor |
|||
: theme.primaryColor; |
|||
|
|||
themeColorPrimary.value = primaryColor || theme.color; |
|||
} |
|||
|
|||
function handleInputChange(e: Event) { |
|||
const target = e.target as HTMLInputElement; |
|||
themeColorPrimary.value = convertToHsl(target.value); |
|||
} |
|||
|
|||
function selectColor() { |
|||
colorInput.value?.[0]?.click?.(); |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="flex w-full flex-wrap justify-between"> |
|||
<template v-for="theme in BUILT_IN_THEME_PRESETS" :key="theme.type"> |
|||
<div class="flex cursor-pointer flex-col" @click="handleSelect(theme)"> |
|||
<div |
|||
:class="{ |
|||
'outline-box-active': theme.type === modelValue, |
|||
}" |
|||
class="outline-box flex-center group cursor-pointer" |
|||
> |
|||
<template v-if="theme.type !== 'custom'"> |
|||
<div |
|||
:style="{ backgroundColor: theme.color }" |
|||
class="mx-10 my-2 size-5 rounded-md" |
|||
></div> |
|||
</template> |
|||
<template v-else> |
|||
<div class="size-full px-10 py-2" @click.stop="selectColor"> |
|||
<div class="flex-center relative size-5 rounded-sm"> |
|||
<MdiEditBoxOutline |
|||
class="absolute z-10 size-5 opacity-60 group-hover:opacity-100" |
|||
/> |
|||
<input |
|||
ref="colorInput" |
|||
:value="inputValue" |
|||
class="absolute inset-0 opacity-0" |
|||
type="color" |
|||
@input="handleInputChange" |
|||
/> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
</div> |
|||
<div class="text-muted-foreground my-2 text-center text-xs"> |
|||
{{ typeView(theme.type) }} |
|||
</div> |
|||
</div> |
|||
</template> |
|||
</div> |
|||
</template> |
|||
@ -1,90 +0,0 @@ |
|||
<script setup lang="ts"> |
|||
import type { CSSProperties } from 'vue'; |
|||
import { computed, ref, watch, watchEffect } from 'vue'; |
|||
|
|||
import { MdiEditBoxOutline } from '@vben-core/iconify'; |
|||
import { TinyColor, convertToHsl } from '@vben-core/toolkit'; |
|||
|
|||
defineOptions({ |
|||
name: 'PreferenceColor', |
|||
}); |
|||
|
|||
const props = withDefaults(defineProps<{ colorPrimaryPresets: string[] }>(), { |
|||
colorPrimaryPresets: () => [], |
|||
}); |
|||
|
|||
const colorInput = ref(); |
|||
const currentColor = ref(props.colorPrimaryPresets?.[0]); |
|||
|
|||
const modelValue = defineModel<string>(); |
|||
|
|||
const activeColor = computed((): CSSProperties => { |
|||
return { |
|||
outlineColor: currentColor.value, |
|||
outlineWidth: '2px', |
|||
}; |
|||
}); |
|||
|
|||
function isActive(color: string): string[] { |
|||
return color === currentColor.value ? ['outline-box-active'] : []; |
|||
} |
|||
|
|||
const inputStyle = computed((): CSSProperties => { |
|||
return props.colorPrimaryPresets.includes(currentColor.value) |
|||
? {} |
|||
: activeColor.value; |
|||
}); |
|||
|
|||
const inputValue = computed(() => { |
|||
return new TinyColor(modelValue.value).toHexString(); |
|||
}); |
|||
|
|||
function selectColor() { |
|||
colorInput.value.click(); |
|||
} |
|||
|
|||
function handleInputChange(e: Event) { |
|||
const target = e.target as HTMLInputElement; |
|||
modelValue.value = convertToHsl(target.value); |
|||
} |
|||
|
|||
// 监听颜色变化,转成系统可识别的 hsl 格式 |
|||
watch(currentColor, (val) => { |
|||
modelValue.value = convertToHsl(val); |
|||
}); |
|||
|
|||
watchEffect(() => { |
|||
if (modelValue.value) { |
|||
currentColor.value = modelValue.value; |
|||
} |
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="flex w-full flex-wrap justify-between"> |
|||
<template v-for="color in colorPrimaryPresets" :key="color"> |
|||
<div |
|||
:class="isActive(color)" |
|||
class="outline-box p-2" |
|||
@click="currentColor = color" |
|||
> |
|||
<div |
|||
:style="{ backgroundColor: color }" |
|||
class="h-5 w-5 rounded-md" |
|||
></div> |
|||
</div> |
|||
</template> |
|||
<div :style="inputStyle" class="outline-box p-2" @click="selectColor"> |
|||
<div class="flex-center bg-accent relative h-5 w-5 rounded-md"> |
|||
<MdiEditBoxOutline class="absolute z-10" /> |
|||
<input |
|||
ref="colorInput" |
|||
:value="inputValue" |
|||
class="absolute inset-0 opacity-0" |
|||
type="color" |
|||
@input="handleInputChange" |
|||
/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
@ -0,0 +1,38 @@ |
|||
<script setup lang="ts"> |
|||
import { ToggleGroup, ToggleGroupItem } from '@vben-core/shadcn-ui'; |
|||
|
|||
defineOptions({ |
|||
name: 'PreferenceColorMode', |
|||
}); |
|||
|
|||
const modelValue = defineModel<string | undefined>('themeRadius', { |
|||
default: '0.5', |
|||
}); |
|||
|
|||
const items = [ |
|||
{ label: '0', value: '0' }, |
|||
{ label: '0.25', value: '0.25' }, |
|||
{ label: '0.5', value: '0.5' }, |
|||
{ label: '0.75', value: '0.75' }, |
|||
{ label: '1', value: '1' }, |
|||
]; |
|||
</script> |
|||
|
|||
<template> |
|||
<ToggleGroup |
|||
v-model="modelValue" |
|||
class="gap-2" |
|||
size="sm" |
|||
type="single" |
|||
variant="outline" |
|||
> |
|||
<template v-for="item in items" :key="item.value"> |
|||
<ToggleGroupItem |
|||
:value="item.value" |
|||
class="data-[state=on]:bg-primary data-[state=on]:text-primary-foreground h-7 w-16 rounded-sm" |
|||
> |
|||
{{ item.label }} |
|||
</ToggleGroupItem> |
|||
</template> |
|||
</ToggleGroup> |
|||
</template> |
|||
@ -1,4 +0,0 @@ |
|||
:root.dark { |
|||
/* authentication */ |
|||
--color-authentication: hsl(240deg 11% 2%); |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue