feat(sites): add mobile-first playground (#864)
This commit is contained in:
parent
45acef6cb2
commit
4ccc2ba1eb
|
@ -14,29 +14,38 @@ import Share from './icons/Share.vue'
|
|||
const versions = ['3.11', '3.10', '3.9', '3.8']
|
||||
const latestVersion = versions[0]
|
||||
const cdnHost = window.localStorage.getItem('setting-cdn')
|
||||
const getRuntime = (version) => `${cdnHost}/@opentiny/vue@${version}/runtime/`
|
||||
|
||||
const searchObj = new URLSearchParams(location.search)
|
||||
const tinyMode = searchObj.get('mode')
|
||||
const tinyTheme = tinyMode === 'saas' ? 'aurora' : searchObj.get('theme')
|
||||
const isMobileFirst = tinyMode === 'mobile-first'
|
||||
|
||||
const createImportMap = (version) => {
|
||||
const imports = {
|
||||
'@opentiny/vue': `${cdnHost}/@opentiny/vue@${version}/runtime/tiny-vue.mjs`,
|
||||
'@opentiny/vue-icon': `${cdnHost}/@opentiny/vue@${version}/runtime/tiny-vue-icon.mjs`,
|
||||
'@opentiny/vue-locale': `${cdnHost}/@opentiny/vue@${version}/runtime/tiny-vue-locale.mjs`,
|
||||
'@opentiny/vue-common': `${cdnHost}/@opentiny/vue@${version}/runtime/tiny-vue-common.mjs`,
|
||||
'@opentiny/vue': `${getRuntime(version)}tiny-vue.mjs`,
|
||||
'@opentiny/vue-icon': `${getRuntime(version)}tiny-vue-icon.mjs`,
|
||||
'@opentiny/vue-locale': `${getRuntime(version)}tiny-vue-locale.mjs`,
|
||||
'@opentiny/vue-common': `${getRuntime(version)}tiny-vue-common.mjs`,
|
||||
'@opentiny/vue-theme/': `${cdnHost}/@opentiny/vue-theme@${version}/`,
|
||||
'sortablejs': `${cdnHost}/sortablejs@1.15.0/modular/sortable.esm.js`
|
||||
}
|
||||
if (['aurora', 'smb'].includes(tinyTheme)) {
|
||||
imports[`@opentiny/vue-design-${tinyTheme}`] = `${cdnHost}/@opentiny/vue-design-${tinyTheme}@${version}/index.js`
|
||||
}
|
||||
if (isMobileFirst) {
|
||||
imports['@opentiny/vue'] = `${getRuntime(version)}tiny-vue-mobile-first.mjs`
|
||||
imports['@opentiny/vue-icon'] = `${getRuntime(version)}tiny-vue-icon-saas.mjs`
|
||||
}
|
||||
return {
|
||||
imports
|
||||
}
|
||||
}
|
||||
|
||||
const getTinyTheme = (version) => {
|
||||
if (isMobileFirst) {
|
||||
return `${getRuntime(version)}tailwind.css`
|
||||
}
|
||||
let theme = tinyMode === 'saas' ? 'saas' : tinyTheme
|
||||
if (!['smb', 'default', 'aurora', 'saas'].includes(theme)) {
|
||||
theme = 'default'
|
||||
|
@ -81,7 +90,7 @@ const state = reactive({
|
|||
const designThemeMap = {
|
||||
aurora: 'tinyAuroraTheme',
|
||||
infinity: 'tinyInfinityTheme',
|
||||
smb: 'tinySmbTheme',
|
||||
smb: 'tinySmbTheme'
|
||||
}
|
||||
|
||||
function setTinyDesign() {
|
||||
|
@ -90,14 +99,14 @@ function setTinyDesign() {
|
|||
// 目前只有aurora和smb有design包
|
||||
if (['aurora', 'smb'].includes(tinyTheme)) {
|
||||
importCode += `import designConfig from '@opentiny/vue-design-${tinyTheme}';
|
||||
import { design } from '@opentiny/vue-common';\n`,
|
||||
import { design } from '@opentiny/vue-common';\n`
|
||||
useCode += 'app.provide(design.configKey, designConfig)\n'
|
||||
}
|
||||
|
||||
if (['aurora', 'infinity', 'smb'].includes(tinyTheme)) {
|
||||
const designTheme = designThemeMap[tinyTheme]
|
||||
importCode += `import TinyThemeTool from '@opentiny/vue-theme/theme-tool';
|
||||
import { ${designTheme} } from '@opentiny/vue-theme/theme';\n`,
|
||||
import { ${designTheme} } from '@opentiny/vue-theme/theme';\n`
|
||||
useCode += `const theme = new TinyThemeTool(${designTheme})\n`
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,18 @@
|
|||
<div class="hp100 of-hidden">
|
||||
<tiny-config-provider :design="designConfig">
|
||||
<router-view />
|
||||
<tiny-modal v-show="visible" :show-header="false" :show-footer="false" v-model="modalSHow" fullscreen>
|
||||
<tiny-icon-close class="close-icon" @click="visible = !visible"></tiny-icon-close>
|
||||
<iframe width="100%" height="100%" :src="previewUrl" frameborder="0"></iframe>
|
||||
</tiny-modal>
|
||||
</tiny-config-provider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, computed, onMounted, toRefs } from 'vue'
|
||||
import { ConfigProvider } from '@opentiny/vue'
|
||||
import { defineComponent, onMounted, provide, ref } from 'vue'
|
||||
import { ConfigProvider, Modal } from '@opentiny/vue'
|
||||
import { iconClose } from '@opentiny/vue-icon'
|
||||
import { appData } from './tools'
|
||||
import useTheme from './tools/useTheme'
|
||||
|
||||
|
@ -16,9 +21,14 @@ export default defineComponent({
|
|||
name: 'AppVue',
|
||||
props: [],
|
||||
components: {
|
||||
TinyConfigProvider: ConfigProvider
|
||||
TinyConfigProvider: ConfigProvider,
|
||||
TinyModal: Modal,
|
||||
TinyIconClose: iconClose()
|
||||
},
|
||||
setup() {
|
||||
const visible = ref(false)
|
||||
const previewUrl = ref(import.meta.env.VITE_PLAYGROUND_URL)
|
||||
const modalSHow = ref(false)
|
||||
onMounted(() => {
|
||||
// 加载header
|
||||
const common = new window.TDCommon(['#header'], {
|
||||
|
@ -31,12 +41,35 @@ export default defineComponent({
|
|||
}
|
||||
})
|
||||
common.renderHeader()
|
||||
modalSHow.value = true
|
||||
})
|
||||
const { designConfig } = useTheme()
|
||||
|
||||
provide('showPreview', (url) => {
|
||||
previewUrl.value = url
|
||||
visible.value = true
|
||||
})
|
||||
return {
|
||||
appData,
|
||||
designConfig
|
||||
designConfig,
|
||||
visible,
|
||||
previewUrl,
|
||||
modalSHow
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.close-icon.tiny-svg {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.tiny-modal {
|
||||
:deep(.tiny-modal__body) {
|
||||
padding: 34px 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<!-- DEMO 的标题 + 说明desc+ 示例wcTag -->
|
||||
<div class="ti-f-r ti-f-pos-between ti-f-box-end ti-pb20">
|
||||
<div class="ti-f18 ti-cur-hand">{{ demo.name[langKey] }}</div>
|
||||
<div v-if="!isMobileFirst">
|
||||
<div>
|
||||
<tiny-tooltip placement="top" :append-to-body="false" :content="copyTip">
|
||||
<i
|
||||
:class="copyIcon"
|
||||
|
@ -32,7 +32,7 @@
|
|||
<component :is="getDescMd(demo)" class="ti-mb16 ti-f14" />
|
||||
|
||||
<div v-if="isMobileFirst" class="pc-demo-container">
|
||||
<tiny-button @click="openPlayground(demo)">多端预览</tiny-button>
|
||||
<tiny-button @click="openPlayground(demo, false)">多端预览</tiny-button>
|
||||
</div>
|
||||
<div v-else-if="demoConfig.isMobile" class="phone-container">
|
||||
<div class="mobile-view-container">
|
||||
|
@ -64,7 +64,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
import { defineComponent, reactive, computed, toRefs, shallowRef, onMounted, watch, nextTick } from 'vue'
|
||||
import { defineComponent, reactive, computed, toRefs, shallowRef, onMounted, watch, nextTick, inject } from 'vue'
|
||||
import { $t, $t2 } from '@/i18n'
|
||||
import { $split, appData, fetchDemosFile, $pub } from '@/tools'
|
||||
import { Tooltip as TinyTooltip, Tabs as TinyTabs, TabItem as TinyTabItem, Button as TinyButton } from '@opentiny/vue'
|
||||
|
@ -77,33 +77,6 @@ import AsyncHighlight from './async-highlight.vue'
|
|||
|
||||
const { apiModeState, apiModeFn } = useApiMode()
|
||||
|
||||
const getDemoCodeFn = async (demo, forceUpdate) => {
|
||||
// 获取code代码文本
|
||||
if (!demo.files || forceUpdate) {
|
||||
const cmpId = router.currentRoute.value.params.cmpId
|
||||
const promises = demo.codeFiles.map(async (fileName) => {
|
||||
// 切换option-api和composition-api
|
||||
const demoName = apiModeFn.getDemoName(`${getWebdocPath(cmpId)}/${fileName}`)
|
||||
let code = ''
|
||||
|
||||
const path = `${staticDemoPath}/${demoName}`
|
||||
code = await fetchDemosFile(path)
|
||||
.then((code) => {
|
||||
return code
|
||||
})
|
||||
.catch((error) => {
|
||||
return `${demoName}示例资源不存在,请检查文件名是否正确?`
|
||||
})
|
||||
const ext = $split(fileName, '.', -1)
|
||||
const language = languageMap[ext] || ''
|
||||
return { code, fileName, language }
|
||||
})
|
||||
demo.files = await Promise.all(promises)
|
||||
return demo.files
|
||||
}
|
||||
return demo.files
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Demo',
|
||||
props: ['demo'],
|
||||
|
@ -120,6 +93,34 @@ export default defineComponent({
|
|||
const isMobileFirst = computed(() => {
|
||||
return templateModeState.mode === 'mobile-first'
|
||||
})
|
||||
|
||||
const getDemoCodeFn = async (demo, forceUpdate) => {
|
||||
// 获取code代码文本
|
||||
if (!demo.files || forceUpdate) {
|
||||
const cmpId = router.currentRoute.value.params.cmpId
|
||||
const promises = demo.codeFiles.map(async (fileName) => {
|
||||
// 切换option-api和composition-api
|
||||
const demoName = apiModeFn.getDemoName(`${getWebdocPath(cmpId)}/${fileName}`)
|
||||
let code = ''
|
||||
|
||||
// const path = `${staticDemoPath}/${demoName}`
|
||||
const path = isMobileFirst ? `@demos/mobile-first/app/${demoName}` : `${staticDemoPath}/${demoName}`
|
||||
code = await fetchDemosFile(path)
|
||||
.then((code) => {
|
||||
return code
|
||||
})
|
||||
.catch((error) => {
|
||||
return `${demoName}示例资源不存在,请检查文件名是否正确?`
|
||||
})
|
||||
const ext = $split(fileName, '.', -1)
|
||||
const language = languageMap[ext] || ''
|
||||
return { code, fileName, language }
|
||||
})
|
||||
demo.files = await Promise.all(promises)
|
||||
return demo.files
|
||||
}
|
||||
return demo.files
|
||||
}
|
||||
const state = reactive({
|
||||
tabValue: 'tab0',
|
||||
cmpId: router.currentRoute.value.params.cmpId,
|
||||
|
@ -139,6 +140,8 @@ export default defineComponent({
|
|||
|
||||
const cmp = shallowRef(null)
|
||||
|
||||
const showPreview = inject('showPreview')
|
||||
|
||||
const fn = {
|
||||
getDescMd(demo) {
|
||||
// desc字段可能是md,也可能是html。 返回md组件或者html组件
|
||||
|
@ -180,14 +183,18 @@ export default defineComponent({
|
|||
// 获取code代码文本
|
||||
return getDemoCodeFn(demo)
|
||||
},
|
||||
openPlayground(demo) {
|
||||
openPlayground(demo, open = true) {
|
||||
const cmpId = router.currentRoute.value.params.cmpId
|
||||
const tinyTheme = currThemeLabel.value.split('-')[1]
|
||||
window.open(
|
||||
`${import.meta.env.VITE_PLAYGROUND_URL}?cmpId=${cmpId}&fileName=${demo.codeFiles[0]}&apiMode=${
|
||||
apiModeState.apiMode
|
||||
}&mode=${templateModeState.mode}&theme=${tinyTheme}`
|
||||
)
|
||||
const url = `${import.meta.env.VITE_PLAYGROUND_URL}?cmpId=${cmpId}&fileName=${demo.codeFiles[0]}&apiMode=${
|
||||
apiModeState.apiMode
|
||||
}&mode=${templateModeState.mode}&theme=${tinyTheme}`
|
||||
|
||||
if (open) {
|
||||
window.open(url)
|
||||
} else {
|
||||
showPreview(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
"rollup": "^3.7.3",
|
||||
"semver": "^7.3.8",
|
||||
"tsup": "7.2.0",
|
||||
"vite": "^4.3.8"
|
||||
"vite": "^4.3.8",
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
"bin": {
|
||||
"cli": "./dist/cjs/index.js"
|
||||
|
|
|
@ -7,6 +7,7 @@ import babel from '@rollup/plugin-babel'
|
|||
import { logGreen } from '../../shared/utils'
|
||||
import type { BuildUiOption, BaseConfig } from './build-ui'
|
||||
import { pathFromPackages, getBaseConfig, requireModules } from './build-ui'
|
||||
import { createProcessor } from 'tailwindcss/src/cli/build/plugin'
|
||||
|
||||
const argv = minimist(process.argv.slice(2))
|
||||
const { tiny_mode = 'pc' } = argv
|
||||
|
@ -123,11 +124,7 @@ async function batchBuildAll({ vueVersion, tasks, message, emptyOutDir, npmScope
|
|||
}
|
||||
|
||||
function getEntryTasks() {
|
||||
return [
|
||||
{
|
||||
path: 'vue-icon/index.ts',
|
||||
libPath: 'tiny-vue-icon'
|
||||
},
|
||||
const entry = [
|
||||
{
|
||||
path: 'vue-locale/src/index.ts',
|
||||
libPath: 'tiny-vue-locale'
|
||||
|
@ -141,6 +138,18 @@ function getEntryTasks() {
|
|||
libPath: tiny_mode === 'mobile-first' ? 'tiny-vue-mobile-first' : 'tiny-vue'
|
||||
}
|
||||
]
|
||||
if (tiny_mode === 'mobile-first') {
|
||||
entry.push({
|
||||
path: 'vue-icon-saas/index.ts',
|
||||
libPath: 'tiny-vue-icon-saas'
|
||||
})
|
||||
} else {
|
||||
entry.push({
|
||||
path: 'vue-icon/index.ts',
|
||||
libPath: 'tiny-vue-icon'
|
||||
})
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
export async function buildRuntime({
|
||||
|
@ -163,6 +172,19 @@ export async function buildRuntime({
|
|||
for (let i = 0; i < tasks.length; i++) {
|
||||
await batchBuildAll({ vueVersion, tasks: [tasks[i]], message, emptyOutDir, npmScope: scope, min })
|
||||
}
|
||||
if (tiny_mode === 'mobile-first') {
|
||||
const rootDir = pathFromPackages('')
|
||||
const runtimeDir = `dist${vueVersion}/@opentiny/vue/runtime`
|
||||
const outDir = path.resolve(rootDir, runtimeDir)
|
||||
const processor = await createProcessor(
|
||||
{
|
||||
'--output': path.join(outDir, 'tailwind.css'),
|
||||
'--content': path.join(outDir, 'tiny-vue-mobile-first.mjs')
|
||||
},
|
||||
path.resolve(rootDir, 'theme-saas/tailwind.config.js')
|
||||
)
|
||||
await processor.build()
|
||||
}
|
||||
// 确保只运行一次
|
||||
emptyOutDir = false
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
"create:mapping": "pnpm -C internals/cli create:mapping",
|
||||
"sync-icons": "pnpm -C internals/cli sync-icons",
|
||||
"// ---------- 打包运行时组件库 ----------": "",
|
||||
"build:runtime": "pnpm -C internals/cli build:runtime",
|
||||
"build:runtime": "pnpm -C internals/cli build:runtime && pnpm -C internals/cli build:runtime-mf",
|
||||
"// ---------- 构建相关脚本 ----------": "",
|
||||
"build:ui": "pnpm create:icon-saas && pnpm create:mapping && pnpm build:entry && gulp themeConcat && pnpm -C internals/cli build:ui",
|
||||
"build:renderless": "pnpm -C packages/renderless build:fast",
|
||||
|
@ -256,4 +256,4 @@
|
|||
"> 1%",
|
||||
"last 2 versions"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue