diff --git a/examples/sites/demos/pc/webdoc/theme-en.md b/examples/sites/demos/pc/webdoc/theme-en.md index cb56c9885..e4339ff7a 100644 --- a/examples/sites/demos/pc/webdoc/theme-en.md +++ b/examples/sites/demos/pc/webdoc/theme-en.md @@ -10,11 +10,42 @@ Design website: [Administrative side specification design variable] (https://rnd Basic style variable `npm` Repository path: `@opentiny/vue-theme/theme` -### Using Predefined Topics and Dynamically Switching Topics +### Using predefined themes and dynamically switching themes -At present, two sets of themes are officially provided: default theme and unlimited theme. +Currently, the official offers 4 sets of themes: -Topic initialization and dynamic theme switching are described as follows: +-Default Theme +-Infinity Theme ` tinyInfinityTheme` +-Aurora Theme ` tinyAuroraTheme` +-XDesign Theme ` tinySmbTheme` + +#### Using predefined themes through alias [Currently only supported: Eurora theme and XDesign theme] + +vue.config.js define + +```js +chainWebpack: (config) => { + // XDesign Theme + config.resolve.alias.set('@opentiny/vue-theme', '@opentiny/vue-theme/smb-theme') + // Aurora Theme : The aurora theme is to replace all the 'smb' characters in the above SMB themes with 'aurora' +} +``` + +vite.config.js define + +```js +resolve: { + alias: [ + // XDesign Theme + { + find: /\@opentiny\/vue-theme\/(?!(smb))/, + replacement: '@opentiny/vue-theme/smb-theme/' + } + ] +} +``` + +#### The specific usage of theme initialization and dynamic theme switching is shown below, and the following code is added to the main.ts file. ```js import TinyThemeTool from' @opentiny/vue-theme/theme-tool.js' diff --git a/examples/sites/demos/pc/webdoc/theme.md b/examples/sites/demos/pc/webdoc/theme.md index ca7e119ea..6101216ce 100644 --- a/examples/sites/demos/pc/webdoc/theme.md +++ b/examples/sites/demos/pc/webdoc/theme.md @@ -17,7 +17,33 @@ - 欧若拉主题 `tinyAuroraTheme` - XDesign 主题 `tinySmbTheme` -主题初始化和动态切换主题的具体使用方式如下文所示,在 main.ts 文件中增加以下代码。 +#### 通过 alias 使用预定义主题【目前仅支持:欧若拉主题 和 XDesign 主题】 + +vue.config.js 定义 + +```js +chainWebpack: (config) => { + // XDesign 主题 + config.resolve.alias.set('@opentiny/vue-theme', '@opentiny/vue-theme/smb-theme') + // aurora 主题 则是将以上smb主题中的'smb'字符全替换成 'aurora'即可 +} +``` + +vite.config.js 定义 + +```js +resolve: { + alias: [ + // XDesign 主题 + { + find: /\@opentiny\/vue-theme\/(?!(smb))/, + replacement: '@opentiny/vue-theme/smb-theme/' + } + ] +} +``` + +#### 主题初始化和动态切换主题的具体使用方式如下文所示,在 main.ts 文件中增加以下代码。 ```js import TinyThemeTool from '@opentiny/vue-theme/theme-tool' diff --git a/packages/theme/build/edit‐dir‐theme.js b/packages/theme/build/edit‐dir‐theme.js new file mode 100644 index 000000000..319792148 --- /dev/null +++ b/packages/theme/build/edit‐dir‐theme.js @@ -0,0 +1,281 @@ +const fs = require('fs') +const fsExtra = require('fs-extra') +const indexLessPath = 'index.less' +const indexJsPath = 'index.js' +const smbThemeName = 'smb-theme' +const auroraThemeName = 'aurora-theme' +const originRootPath = '../src/' +const originThemeRootPath = `${originRootPath}theme/` +const basePath = '/base/' + +const emptyThemeContent = `@import '../../{{}}/index.less';` + +const baseContent = `@import '../../base/index.less'; +html:root` + +const scopedTitle = `@import '../../custom.less'; +@import '../../{{}}/index.less'; +` + +const scopedContent = ` +@{compName}-prefix-cls: ~'@{css-prefix}{compName}'; + +.@{{compName}-prefix-cls}[class*=tiny] { + {var} +}` + +const buildThemePathMap = { + [auroraThemeName]: `${originRootPath}${auroraThemeName}${basePath}`, + [smbThemeName]: `${originRootPath}${smbThemeName}${basePath}` +} + +const cssScopedMap = { + 'anchor': 'anchor__wrapper', + 'badge': 'badge__wrapper', + 'crop': 'crop__wrapper', + 'date-panel': 'picker-panel', + 'date-range': 'date-range-picker', + 'detail-page': 'grid-modal__box', + 'dialog-box': 'dialog-box__wrapper', + 'error-page': 'errortips__box', + 'floatbar': 'float-bar', + 'flowchart': 'flow-chart', + 'grid': 'grid__wrapper', + 'input': ['input', 'input-group'], + 'option': 'select-dropdown', + 'pager': ['pager', 'pager__selector'], + 'picker': 'date-editor', + 'pop-upload': ['popupload__modal', 'popupload'], + 'popconfirm': ['popconfirm', 'popconfirm-popover'], + 'popeditor': ['dialog-box', 'popeditor'], + 'scroll-text': 'scroll-text__wrapper', + 'slider': 'slider-container', + 'tabs': ['tabs__more-dropdown', 'tabs'], + 'time-range': 'time-range-picker', + 'tip': 'tips', + 'tree': ['tree', 'tree__del-popover'], + 'upload': 'file-upload', + 'upload-dragger': 'file-upload', + 'upload-list': 'file-upload', + 'user-account': 'user-contact' +} + +const createTheme = (callbackFn) => { + const createDir = (path, fn = null) => { + fs.mkdir(path, (err) => { + if (err) { + console.log(path, err) + } + fn && fn() + }) + } + + let timer = null + const readerDirStart = () => { + clearTimeout(timer) + timer = null + timer = setTimeout(() => { + readDir(originRootPath, 'less') + readDir(originThemeRootPath, 'js') + }, 10) + } + + const beginCreateDir = () => { + for (let k in buildThemePathMap) { + createDir(originRootPath + k, readerDirStart) + } + } + + const readDir = (originPath, type) => { + fs.readdir(originPath, (err, data) => { + if (err) { + console.log('readDir', err) + return + } + + data.forEach((fileDir) => { + mkStat(originPath, fileDir, type) + }) + }) + } + + let isFinalTimer = null + + const isFinalFn = () => { + clearTimeout(isFinalTimer) + isFinalTimer = null + isFinalTimer = setTimeout(() => { + callbackFn() + }, 20) + } + + const mkStat = (originPath, fileDir, type) => { + const statPath = `${originPath}/${fileDir}` + fs.stat(statPath, (err, data) => { + if (err) { + console.log('mkStat', err) + return + } + if (data.isDirectory()) { + if (fileDir !== 'base' && fileDir !== 'theme') { + if (fileDir === 'images') { + fsExtra.copy(statPath, `${originPath}/smb-theme/${fileDir}`, (subErr) => { + if (subErr) { + console.log(subErr) + return + } + }) + fsExtra.copy(statPath, `${originPath}/aurora-theme/${fileDir}`, (subErr) => { + if (subErr) { + console.log(subErr) + return + } + }) + } else { + isFileExist(originPath, fileDir, type) + } + } + } + }) + } + + const isFileExist = (originPath, fileDir, type) => { + const parentPath = `${originPath}${fileDir}` + const path = `${parentPath}/index.${type}` + fs.exists(path, (exist) => { + if (exist) { + if (type === 'less') { + readComponentsFile(fileDir) + } else if (type === 'js') { + readThemeFile(fileDir) + } + return + } + }) + } + + const writeThemeIndexLess = (path, fileDir, themeName) => { + fs.stat(path, (err, data) => { + const parentPath = `${originRootPath}${themeName}/${fileDir}` + const writeTheme = () => { + if (err) { + writeFile(parentPath + '/index.less', emptyThemeContent.replace('{{}}', fileDir)) + + return + } + + if (!data.isDirectory()) { + fs.readFile(path, 'utf8', (subErr, dataStr) => { + if (subErr) { + console.log(subErr) + return + } + const startIndex = dataStr.indexOf('{') + 4 + const endIndex = dataStr.indexOf('}') + const newDataStr = + dataStr + .slice(startIndex, endIndex) + .replace(/\'ti-/g, '--ti-') + .replace(/\'/g, '') + .replace(/\,\n/g, ';\n') + .replace(/\, \/\//g, '; //') + .slice(0, -1) + ';' + + let scropedData = scopedTitle.replace('{{}}', fileDir) + + if (cssScopedMap[fileDir]) { + if (Array.isArray(cssScopedMap[fileDir])) { + cssScopedMap[fileDir].forEach((item) => { + scropedData += scopedContent.replace(/\{compName\}/g, item).replace(/\{var\}/g, newDataStr) + }) + } else { + scropedData += scopedContent + .replace(/\{compName\}/g, cssScopedMap[fileDir]) + .replace(/\{var\}/g, newDataStr) + } + } else { + scropedData += scopedContent.replace(/\{compName\}/g, fileDir).replace(/\{var\}/g, newDataStr) + } + + writeFile(parentPath + '/index.less', scropedData) + }) + } + } + + createDir(parentPath, writeTheme) + }) + } + + const readComponentsFile = (fileDir) => { + const indexPath = `${originRootPath}/${fileDir}/${indexLessPath}` + const smbPath = `${originRootPath}/${fileDir}/${smbThemeName}.js` + const auroraPath = `${originRootPath}/${fileDir}/${auroraThemeName}.js` + fs.readFile(indexPath, 'utf8', (err, dataStr) => { + if (err) { + console.log(err) + } + + // 写入smb-theme/**/index.less + writeThemeIndexLess(smbPath, fileDir, smbThemeName) + + // 写入aurora-theme/**/index.less + writeThemeIndexLess(auroraPath, fileDir, auroraThemeName) + }) + } + + const readThemeFile = (fileDir) => { + const indexPath = `${originThemeRootPath}/${fileDir}/${indexJsPath}` + + if (!buildThemePathMap[fileDir]) { + return + } + + fs.readFile(indexPath, 'utf8', (err, dataStr) => { + createDir(buildThemePathMap[fileDir]) + + if (err) { + console.log(err) + } + + const startIndex = dataStr.indexOf('{') - 1 + const endIndex = dataStr.indexOf('}') + let newDataStr = dataStr.slice(startIndex, endIndex) + const lastIndex = newDataStr.lastIndexOf("'") + newDataStr = + baseContent + + newDataStr + .slice(0, lastIndex) + .replace(/\'ti-/g, '--ti-') + .replace(/\'/g, '') + .replace(/\,\n/g, ';\n') + .replace(/\, \/\//g, '; //') + + ';\n}' + + writeFile(buildThemePathMap[fileDir] + '/index.less', newDataStr) + }) + } + + const writeFile = (filePath, data) => { + fs.writeFile(filePath, data, 'utf8', (err) => { + if (err) { + console.log('writeFile', err) + } + isFinalFn() + }) + } + + beginCreateDir() +} + +const removeDir = (callbackFn) => { + for (let k in buildThemePathMap) { + fsExtra.remove(`${originRootPath}${k}`, (err) => { + if (err) { + console.log('removeDir', err) + } + callbackFn() + }) + } +} + +module.exports = { createTheme, removeDir } diff --git a/packages/theme/build/gulp-dist.js b/packages/theme/build/gulp-dist.js index 2a12db527..23803f5c1 100644 --- a/packages/theme/build/gulp-dist.js +++ b/packages/theme/build/gulp-dist.js @@ -9,9 +9,12 @@ const svgInline = require('gulp-svg-inline') const prefixer = require('gulp-autoprefixer') const fg = require('fast-glob') const fs = require('node:fs') +const { createTheme, removeDir } = require('./edit‐dir‐theme.js') const source = '../src' const dist = '../dist' +const distSmb = '../dist/smb-theme' +const distAurora = '../dist/aurora-theme' const svgInlineOption = { maxImageSize: 1 * 1024 * 1024, extensions: [/\.svg/gi] @@ -26,9 +29,16 @@ const importStr = fileList const note = fs.readFileSync('../src/index.less', { encoding: 'utf-8' }).match(/(^\/\*\*.+?\*\/)/s)[0] fs.writeFileSync('../src/index.less', `${note}\n\n${importStr}`) +gulp.task('build-dir', createTheme) + gulp.task('compile', () => { return gulp - .src([`${source}/**/index.less`, `${source}/index.less`]) + .src([ + `${source}/**/index.less`, + `${source}/aurora-theme/**/index.less`, + `${source}/smb-theme/**/index.less`, + `${source}/index.less` + ]) .pipe(svgInline(svgInlineOption)) .pipe(less()) .pipe( @@ -57,4 +67,26 @@ gulp.task('copyimage', () => { return gulp.src([`${source}/images/**`]).pipe(gulp.dest(`${dist}/images`)) }) -gulp.task('build', gulp.series('compile', 'copycssvar', 'copysvgs', 'copyimage')) +gulp.task('copyimage-aurora', () => { + return gulp.src([`${source}/aurora-theme/images/**`]).pipe(gulp.dest(`${distAurora}/images`)) +}) + +gulp.task('copyimage-smb', () => { + return gulp.src([`${source}/smb-theme/images/**`]).pipe(gulp.dest(`${distSmb}/images`)) +}) + +gulp.task('remove-dir', removeDir) + +gulp.task( + 'build', + gulp.series( + 'build-dir', + 'compile', + 'copycssvar', + 'copysvgs', + 'copyimage', + 'copyimage-aurora', + 'copyimage-smb', + 'remove-dir' + ) +) diff --git a/packages/theme/src/theme/infinity-theme/index.js b/packages/theme/src/theme/infinity-theme/index.js index d183dd961..c1a0b3e62 100644 --- a/packages/theme/src/theme/infinity-theme/index.js +++ b/packages/theme/src/theme/infinity-theme/index.js @@ -307,7 +307,7 @@ export const infinityTheme = { 'ti-notify-close-icon-color': 'var(--ti-base-color-common-1)', 'ti-notify-title-text-color': 'var(--ti-base-color-common-1)', 'ti-notify-title-margin-top': '0', - 'ti-notify-title-margin-horizontal': '', + 'ti-notify-title-margin-horizontal': '0', 'ti-notify-title-margin-bottom': '8px', 'ti-notify-title-height': '25px', 'ti-notify-title-line-height': '25px',