diff --git a/packages/settings/styles/src/components/classNamesContainer/index.vue b/packages/settings/styles/src/components/classNamesContainer/index.vue index 6bfbb858c5..cd0207f305 100644 --- a/packages/settings/styles/src/components/classNamesContainer/index.vue +++ b/packages/settings/styles/src/components/classNamesContainer/index.vue @@ -109,7 +109,7 @@ import { useProperties, useCanvas, useHistory, useHelp } from '@opentiny/tiny-en import { LinkButton } from '@opentiny/tiny-engine-common' import { CodeConfigurator } from '@opentiny/tiny-engine-configurator' import useStyle, { updateGlobalStyleStr } from '../../js/useStyle' -import { stringify, getSelectorArr, parser } from '../../js/parser' +import { stringify, getSelectorArr, buildCssObjectFromContent } from '../../js/parser' const { getSchema, propsUpdateKey, setProp } = useProperties() @@ -448,11 +448,8 @@ watchEffect(() => { }) const save = ({ content }) => { - const { styleObject } = parser(content) - const cssObject = {} - Object.keys(styleObject).forEach((styleKey) => { - cssObject[styleKey] = styleObject[styleKey].rules - }) + const cssObject = buildCssObjectFromContent(content) + const { addHistory } = useHistory() const { updateRect } = useCanvas().canvasApi.value const { updateSchema } = useCanvas() diff --git a/packages/settings/styles/src/js/parser.ts b/packages/settings/styles/src/js/parser.ts index 87da3f2363..f94dac8355 100644 --- a/packages/settings/styles/src/js/parser.ts +++ b/packages/settings/styles/src/js/parser.ts @@ -42,6 +42,7 @@ const handleAtRules = (node: any) => { return { type, + hasBlock: node.nodes !== undefined, style: { type, value: rawString @@ -143,6 +144,47 @@ export const parser = (css: string) => { } } +/** + * 根据编辑器 css 内容生成对象,保留源码顺序并支持 at-rule + * @param {string} content + * @returns {Record} + */ +export const buildCssObjectFromContent = (content: string) => { + const { parseList, styleObject } = parser(content) + const cssObject = {} + + // 保证存入 cssObject 的键值顺序与编辑器中的源码字符顺序一致 + parseList.forEach((item) => { + // parser 中的 handleRules 没有给普通 rule 赋 type 属性,只具备 selectors 和 style + if (!item.type && item.selectors) { + if (styleObject[item.selectors]) { + cssObject[item.selectors] = styleObject[item.selectors].rules + } + } else if (item.type === 'atrule') { + const rawValue = item.style?.value || '' + let key = '' + let value = '' + + if (item.hasBlock) { + const braceIdx = rawValue.indexOf('{') + key = braceIdx !== -1 ? rawValue.slice(0, braceIdx).trim() : rawValue.trim() + value = braceIdx !== -1 ? rawValue.slice(braceIdx).trim() : '' + } else { + key = rawValue.trim() + value = '' + } + + if (cssObject[key] !== undefined) { + cssObject[key] = Array.isArray(cssObject[key]) ? [...cssObject[key], value] : [cssObject[key], value] + } else { + cssObject[key] = value + } + } + }) + + return cssObject +} + /** * 拿到组合选择器的数组,比如 .test1.test2 得到 ['.test1', '.test2'] * @param {string} selector diff --git a/packages/utils/src/utils/index.ts b/packages/utils/src/utils/index.ts index c31f7c599f..cb786b2200 100644 --- a/packages/utils/src/utils/index.ts +++ b/packages/utils/src/utils/index.ts @@ -464,9 +464,27 @@ export const objectCssToString = (css) => { return css } let cssString = '' + let topString = '' for (const selector in css) { const properties = css[selector] + + if (typeof properties === 'string' || (Array.isArray(properties) && typeof properties[0] === 'string')) { + const isTopRule = + selector.startsWith('@charset') || selector.startsWith('@import') || selector.startsWith('@namespace') + const propsArray = Array.isArray(properties) ? properties : [properties] + + propsArray.forEach((prop) => { + const line = prop ? `${selector} ${prop}\n` : `${selector}\n` + if (isTopRule) { + topString += line + } else { + cssString += line + } + }) + continue + } + let ruleString = `${selector} {\r\n` for (const property in properties) { @@ -476,5 +494,5 @@ export const objectCssToString = (css) => { ruleString += '}\n' cssString += ruleString } - return cssString + return topString + cssString }