55 changed files with 3534 additions and 772 deletions
@ -1,249 +1,251 @@ |
|||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||
import { ref } from 'vue'; |
// import { ref } from 'vue'; |
||||
|
|
||||
|
// import { echartsInstance as echarts } from '@vben/chart-ui'; |
||||
|
|
||||
defineOptions({ name: 'WelCome' }); |
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: { |
// const cardList = ref([ |
||||
trigger: 'axis', |
// { |
||||
axisPointer: { |
// color: 'green', |
||||
type: 'cross', |
// extra: '月', |
||||
// label: { |
// leftContent: '2000', |
||||
// backgroundColor: '#6a7985', |
// leftFooter: '总访问数', |
||||
// }, |
// rightContent: 'flat-color-icons:conference-call', |
||||
}, |
// rightFooter: '5000', |
||||
}, |
// title: '访问数', |
||||
legend: { |
// }, |
||||
data: ['Line 1', 'Line 2', 'Line 3', 'Line 4', 'Line 5'], |
// { |
||||
}, |
// color: 'red', |
||||
toolbox: { |
// extra: '日', |
||||
feature: { |
// leftContent: '$1350', |
||||
saveAsImage: {}, |
// leftFooter: '总销售额', |
||||
}, |
// rightContent: 'flat-color-icons:sales-performance', |
||||
}, |
// rightFooter: '$550000', |
||||
grid: { |
// title: '销售额', |
||||
left: '3%', |
// }, |
||||
right: '4%', |
// ]); |
||||
bottom: '3%', |
// const chartTabs = ref([ |
||||
containLabel: true, |
// { |
||||
}, |
// name: '1', |
||||
xAxis: [ |
// option: { |
||||
{ |
// color: ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'], |
||||
type: 'category', |
|
||||
boundaryGap: false, |
// grid: { |
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
// bottom: '3%', |
||||
}, |
// containLabel: true, |
||||
], |
// left: '3%', |
||||
yAxis: [ |
// right: '4%', |
||||
{ |
// }, |
||||
type: 'value', |
// legend: { |
||||
}, |
// data: ['Line 1', 'Line 2', 'Line 3', 'Line 4', 'Line 5'], |
||||
], |
// }, |
||||
series: [ |
// series: [ |
||||
{ |
// { |
||||
name: 'Line 1', |
// areaStyle: { |
||||
type: 'line', |
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
stack: 'Total', |
// { |
||||
smooth: true, |
// color: 'rgb(128, 255, 165)', |
||||
lineStyle: { |
// offset: 0, |
||||
width: 0, |
// }, |
||||
}, |
// { |
||||
showSymbol: false, |
// color: 'rgb(1, 191, 236)', |
||||
areaStyle: { |
// offset: 1, |
||||
opacity: 0.8, |
// }, |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
// ]), |
||||
{ |
// opacity: 0.8, |
||||
offset: 0, |
// }, |
||||
color: 'rgb(128, 255, 165)', |
// data: [140, 232, 101, 264, 90, 340, 250], |
||||
}, |
// emphasis: { |
||||
{ |
// focus: 'series', |
||||
offset: 1, |
// }, |
||||
color: 'rgb(1, 191, 236)', |
// lineStyle: { |
||||
}, |
// width: 0, |
||||
]), |
// }, |
||||
}, |
// name: 'Line 1', |
||||
emphasis: { |
// showSymbol: false, |
||||
focus: 'series', |
// smooth: true, |
||||
}, |
// stack: 'Total', |
||||
data: [140, 232, 101, 264, 90, 340, 250], |
// type: 'line', |
||||
}, |
// }, |
||||
{ |
// { |
||||
name: 'Line 2', |
// areaStyle: { |
||||
type: 'line', |
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
stack: 'Total', |
// { |
||||
smooth: true, |
// color: 'rgb(0, 221, 255)', |
||||
lineStyle: { |
// offset: 0, |
||||
width: 0, |
// }, |
||||
}, |
// { |
||||
showSymbol: false, |
// color: 'rgb(77, 119, 255)', |
||||
areaStyle: { |
// offset: 1, |
||||
opacity: 0.8, |
// }, |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
// ]), |
||||
{ |
// opacity: 0.8, |
||||
offset: 0, |
// }, |
||||
color: 'rgb(0, 221, 255)', |
// data: [120, 282, 111, 234, 220, 340, 310], |
||||
}, |
// emphasis: { |
||||
{ |
// focus: 'series', |
||||
offset: 1, |
// }, |
||||
color: 'rgb(77, 119, 255)', |
// lineStyle: { |
||||
}, |
// width: 0, |
||||
]), |
// }, |
||||
}, |
// name: 'Line 2', |
||||
emphasis: { |
// showSymbol: false, |
||||
focus: 'series', |
// smooth: true, |
||||
}, |
// stack: 'Total', |
||||
data: [120, 282, 111, 234, 220, 340, 310], |
// type: 'line', |
||||
}, |
// }, |
||||
{ |
// { |
||||
name: 'Line 3', |
// areaStyle: { |
||||
type: 'line', |
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
stack: 'Total', |
// { |
||||
smooth: true, |
// color: 'rgb(55, 162, 255)', |
||||
lineStyle: { |
// offset: 0, |
||||
width: 0, |
// }, |
||||
}, |
// { |
||||
showSymbol: false, |
// color: 'rgb(116, 21, 219)', |
||||
areaStyle: { |
// offset: 1, |
||||
opacity: 0.8, |
// }, |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
// ]), |
||||
{ |
// opacity: 0.8, |
||||
offset: 0, |
// }, |
||||
color: 'rgb(55, 162, 255)', |
// data: [320, 132, 201, 334, 190, 130, 220], |
||||
}, |
// emphasis: { |
||||
{ |
// focus: 'series', |
||||
offset: 1, |
// }, |
||||
color: 'rgb(116, 21, 219)', |
// lineStyle: { |
||||
}, |
// width: 0, |
||||
]), |
// }, |
||||
}, |
// name: 'Line 3', |
||||
emphasis: { |
// showSymbol: false, |
||||
focus: 'series', |
// smooth: true, |
||||
}, |
// stack: 'Total', |
||||
data: [320, 132, 201, 334, 190, 130, 220], |
// type: 'line', |
||||
}, |
// }, |
||||
{ |
// { |
||||
name: 'Line 4', |
// areaStyle: { |
||||
type: 'line', |
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
stack: 'Total', |
// { |
||||
smooth: true, |
// color: 'rgb(255, 0, 135)', |
||||
lineStyle: { |
// offset: 0, |
||||
width: 0, |
// }, |
||||
}, |
// { |
||||
showSymbol: false, |
// color: 'rgb(135, 0, 157)', |
||||
areaStyle: { |
// offset: 1, |
||||
opacity: 0.8, |
// }, |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
// ]), |
||||
{ |
// opacity: 0.8, |
||||
offset: 0, |
// }, |
||||
color: 'rgb(255, 0, 135)', |
// data: [220, 402, 231, 134, 190, 230, 120], |
||||
}, |
// emphasis: { |
||||
{ |
// focus: 'series', |
||||
offset: 1, |
// }, |
||||
color: 'rgb(135, 0, 157)', |
// lineStyle: { |
||||
}, |
// width: 0, |
||||
]), |
// }, |
||||
}, |
// name: 'Line 4', |
||||
emphasis: { |
// showSymbol: false, |
||||
focus: 'series', |
// smooth: true, |
||||
}, |
// stack: 'Total', |
||||
data: [220, 402, 231, 134, 190, 230, 120], |
// type: 'line', |
||||
}, |
// }, |
||||
{ |
// { |
||||
name: 'Line 5', |
// areaStyle: { |
||||
type: 'line', |
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
stack: 'Total', |
// { |
||||
smooth: true, |
// color: 'rgb(255, 191, 0)', |
||||
lineStyle: { |
// offset: 0, |
||||
width: 0, |
// }, |
||||
}, |
// { |
||||
showSymbol: false, |
// color: 'rgb(224, 62, 76)', |
||||
label: { |
// offset: 1, |
||||
show: true, |
// }, |
||||
position: 'top', |
// ]), |
||||
}, |
// opacity: 0.8, |
||||
areaStyle: { |
// }, |
||||
opacity: 0.8, |
// data: [220, 302, 181, 234, 210, 290, 150], |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
// emphasis: { |
||||
{ |
// focus: 'series', |
||||
offset: 0, |
// }, |
||||
color: 'rgb(255, 191, 0)', |
// label: { |
||||
}, |
// position: 'top', |
||||
{ |
// show: true, |
||||
offset: 1, |
// }, |
||||
color: 'rgb(224, 62, 76)', |
// lineStyle: { |
||||
}, |
// width: 0, |
||||
]), |
// }, |
||||
}, |
// name: 'Line 5', |
||||
emphasis: { |
// showSymbol: false, |
||||
focus: 'series', |
// smooth: true, |
||||
}, |
// stack: 'Total', |
||||
data: [220, 302, 181, 234, 210, 290, 150], |
// type: 'line', |
||||
}, |
// }, |
||||
], |
// ], |
||||
}, |
// toolbox: { |
||||
}, |
// feature: { |
||||
{ |
// saveAsImage: {}, |
||||
name: '2', |
// }, |
||||
title: '访问量', |
// }, |
||||
option: { |
// tooltip: { |
||||
xAxis: { |
// axisPointer: { |
||||
type: 'category', |
// type: 'cross', |
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
// // label: { |
||||
}, |
// // backgroundColor: '#6a7985', |
||||
yAxis: { |
// // }, |
||||
type: 'value', |
// }, |
||||
}, |
// trigger: 'axis', |
||||
series: [ |
// }, |
||||
{ |
// xAxis: [ |
||||
data: [ |
// { |
||||
120, |
// boundaryGap: false, |
||||
{ |
// data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
||||
value: 200, |
// type: 'category', |
||||
itemStyle: { |
// }, |
||||
color: '#a90000', |
// ], |
||||
}, |
// yAxis: [ |
||||
}, |
// { |
||||
150, |
// type: 'value', |
||||
80, |
// }, |
||||
70, |
// ], |
||||
110, |
// }, |
||||
130, |
// title: '流量趋势', |
||||
], |
// }, |
||||
type: 'bar', |
// { |
||||
}, |
// 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> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<Dashboard :cardList="cardList" :chartTabs="chartTabs"></Dashboard> |
<div>dashboard</div> |
||||
|
<!-- <Dashboard :card-list="cardList" :chart-tabs="chartTabs" /> --> |
||||
</template> |
</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 { describe, expect, it } from 'vitest'; |
||||
|
|
||||
import { convertToHsl, convertToHslCssVar, isValidColor } from './color'; |
import { convertToHsl, convertToHslCssVar, isValidColor } from './utils'; |
||||
|
|
||||
describe('color conversion functions', () => { |
describe('color conversion functions', () => { |
||||
it('should correctly convert color to HSL format', () => { |
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