mirror of
https://github.com/kuaifan/dootask.git
synced 2026-07-23 14:38:39 +00:00
fix(micro-apps): 修复深色主题下微应用自定义背景色显示错误
深色主题通过 html 整体 invert+hue-rotate 反色,导致配置的背景色被反成错误颜色。 改为在 iframe 铺底伪元素上承载真实背景色,并在深色下对该伪元素施加同款反向 filter 抵消整体反转,使背景与内容区反色行为一致,不再用 JS 近似反相。 - modal.vue:bodyStyle 通过 CSS 变量 --micro-body-background-color 传递真实色 - iframe.vue:.micro-app-iframe::before 铺底背景层 + 深色反向 filter,并加 isolation 隔离 z-index - 移除不再需要的 utils/color.js 颜色反相工具 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a19822a617
commit
135b419572
@ -14,9 +14,21 @@
|
||||
<style lang="scss" scoped>
|
||||
.micro-app-iframe {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--micro-body-background-color, transparent);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.micro-app-iframe-container {
|
||||
border: none;
|
||||
width: 100%;
|
||||
@ -30,6 +42,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
body.dark-mode-reverse {
|
||||
.micro-app-iframe {
|
||||
&::before {
|
||||
-webkit-filter: invert(100%) hue-rotate(180deg) contrast(100%) !important;
|
||||
filter: invert(100%) hue-rotate(180deg) contrast(100%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
|
||||
@ -50,7 +50,6 @@
|
||||
import { mapState } from 'vuex';
|
||||
import TransferDom from "../../directives/transfer-dom";
|
||||
import ResizeLine from "../ResizeLine.vue";
|
||||
import { colorReverse } from "../../utils/color";
|
||||
|
||||
export default {
|
||||
name: 'MicroModal',
|
||||
@ -107,10 +106,12 @@ export default {
|
||||
return ['micro-modal-fade', 'micro-modal-slide']
|
||||
},
|
||||
bodyStyle() {
|
||||
const styleObject = {}
|
||||
const styleObject = {
|
||||
'--micro-body-background-color': 'transparent'
|
||||
}
|
||||
if (this.options.background) {
|
||||
const colors = `${this.options.background}|`.split('|');
|
||||
styleObject.background = (this.themeName === 'dark' ? colorReverse(colors[1]) : null) || colors[0];
|
||||
styleObject['--micro-body-background-color'] = (this.themeName === 'dark' ? colors[1] : null) || colors[0];
|
||||
}
|
||||
return styleObject;
|
||||
},
|
||||
|
||||
145
resources/assets/js/utils/color.js
vendored
145
resources/assets/js/utils/color.js
vendored
@ -1,145 +0,0 @@
|
||||
/**
|
||||
* Color utilities for parsing and inverting colors.
|
||||
*/
|
||||
|
||||
export function colorReverse(colorValue) {
|
||||
const rgba = parseToRgba(colorValue);
|
||||
if (!rgba) {
|
||||
return colorValue;
|
||||
}
|
||||
const inverted = {
|
||||
r: 255 - rgba.r,
|
||||
g: 255 - rgba.g,
|
||||
b: 255 - rgba.b,
|
||||
a: typeof rgba.a === 'number' ? rgba.a : 1
|
||||
};
|
||||
return rgbaToHex(inverted);
|
||||
}
|
||||
|
||||
export function parseToRgba(colorValue) {
|
||||
if (!colorValue || typeof colorValue !== 'string') {
|
||||
return null;
|
||||
}
|
||||
const value = colorValue.trim();
|
||||
return parseHex(value) || parseRgb(value) || parseHsl(value);
|
||||
}
|
||||
|
||||
export function parseHex(value) {
|
||||
const hex = value.replace(/^#/, '').toLowerCase();
|
||||
if (![3, 4, 6, 8].includes(hex.length) || /[^a-f0-9]/.test(hex)) {
|
||||
return null;
|
||||
}
|
||||
const normalized = hex.length === 3 || hex.length === 4
|
||||
? hex.split('').map(char => char + char).join('')
|
||||
: hex;
|
||||
const hasAlpha = normalized.length === 8;
|
||||
const r = parseInt(normalized.slice(0, 2), 16);
|
||||
const g = parseInt(normalized.slice(2, 4), 16);
|
||||
const b = parseInt(normalized.slice(4, 6), 16);
|
||||
const a = hasAlpha ? parseInt(normalized.slice(6, 8), 16) / 255 : 1;
|
||||
return {r, g, b, a};
|
||||
}
|
||||
|
||||
export function parseRgb(value) {
|
||||
const match = value.match(/rgba?\(\s*([\d.]+%?)\s*,\s*([\d.]+%?)\s*,\s*([\d.]+%?)(?:\s*,\s*([\d.]+%?))?\s*\)/i);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const channels = match.slice(1, 4).map(item => normalizeChannel(item));
|
||||
if (channels.some(item => item === null)) {
|
||||
return null;
|
||||
}
|
||||
const alpha = typeof match[4] !== 'undefined' ? normalizeAlpha(match[4]) : 1;
|
||||
return {r: channels[0], g: channels[1], b: channels[2], a: alpha};
|
||||
}
|
||||
|
||||
export function parseHsl(value) {
|
||||
const match = value.match(/hsla?\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%(?:\s*,\s*([\d.]+%?))?\s*\)/i);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const h = ((parseFloat(match[1]) % 360) + 360) % 360;
|
||||
const s = parseFloat(match[2]) / 100;
|
||||
const l = parseFloat(match[3]) / 100;
|
||||
const alpha = typeof match[4] !== 'undefined' ? normalizeAlpha(match[4]) : 1;
|
||||
const {r, g, b} = hslToRgb(h, s, l);
|
||||
return {r, g, b, a: alpha};
|
||||
}
|
||||
|
||||
export function normalizeChannel(value) {
|
||||
if (value.indexOf('%') > -1) {
|
||||
const num = parseFloat(value);
|
||||
if (isNaN(num)) {
|
||||
return null;
|
||||
}
|
||||
return Math.round(Math.max(0, Math.min(100, num)) * 2.55);
|
||||
}
|
||||
const num = parseFloat(value);
|
||||
if (isNaN(num)) {
|
||||
return null;
|
||||
}
|
||||
return Math.round(Math.max(0, Math.min(255, num)));
|
||||
}
|
||||
|
||||
export function normalizeAlpha(value) {
|
||||
if (typeof value === 'undefined') {
|
||||
return 1;
|
||||
}
|
||||
if (value.indexOf('%') > -1) {
|
||||
const num = parseFloat(value);
|
||||
if (isNaN(num)) {
|
||||
return 1;
|
||||
}
|
||||
return Math.max(0, Math.min(100, num)) / 100;
|
||||
}
|
||||
const num = parseFloat(value);
|
||||
if (isNaN(num)) {
|
||||
return 1;
|
||||
}
|
||||
return Math.max(0, Math.min(1, num));
|
||||
}
|
||||
|
||||
export function hslToRgb(h, s, l) {
|
||||
const c = (1 - Math.abs(2 * l - 1)) * s;
|
||||
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
|
||||
const m = l - c / 2;
|
||||
let rPrime = 0;
|
||||
let gPrime = 0;
|
||||
let bPrime = 0;
|
||||
|
||||
if (h < 60) {
|
||||
rPrime = c;
|
||||
gPrime = x;
|
||||
} else if (h < 120) {
|
||||
rPrime = x;
|
||||
gPrime = c;
|
||||
} else if (h < 180) {
|
||||
gPrime = c;
|
||||
bPrime = x;
|
||||
} else if (h < 240) {
|
||||
gPrime = x;
|
||||
bPrime = c;
|
||||
} else if (h < 300) {
|
||||
rPrime = x;
|
||||
bPrime = c;
|
||||
} else {
|
||||
rPrime = c;
|
||||
bPrime = x;
|
||||
}
|
||||
|
||||
return {
|
||||
r: Math.round((rPrime + m) * 255),
|
||||
g: Math.round((gPrime + m) * 255),
|
||||
b: Math.round((bPrime + m) * 255)
|
||||
};
|
||||
}
|
||||
|
||||
export function rgbaToHex({r, g, b, a = 1}) {
|
||||
const toHex = (value) => {
|
||||
const clamped = Math.max(0, Math.min(255, Math.round(value)));
|
||||
return clamped.toString(16).padStart(2, '0');
|
||||
};
|
||||
const alpha = Math.max(0, Math.min(1, a));
|
||||
const alphaHex = alpha < 1 ? toHex(alpha * 255) : '';
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}${alphaHex}`;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user