├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── README.zh-CN.md ├── package.json ├── packages ├── components │ ├── react │ │ ├── README.md │ │ ├── README.zh-CN.md │ │ ├── common │ │ │ ├── package.json │ │ │ └── src │ │ │ │ └── index.js │ │ ├── index.js │ │ ├── package.json │ │ └── src │ │ │ ├── button │ │ │ ├── package.json │ │ │ └── src │ │ │ │ └── pc.jsx │ │ │ └── countdown │ │ │ ├── package.json │ │ │ └── src │ │ │ └── pc.jsx │ ├── renderless │ │ ├── README.md │ │ ├── README.zh-CN.md │ │ ├── package.json │ │ └── src │ │ │ ├── button │ │ │ ├── index.js │ │ │ ├── react.js │ │ │ ├── solid.js │ │ │ └── vue.js │ │ │ └── countdown │ │ │ ├── index.js │ │ │ ├── react.js │ │ │ ├── solid.js │ │ │ └── vue.js │ ├── solid │ │ ├── README.md │ │ ├── README.zh-CN.md │ │ ├── common │ │ │ ├── package.json │ │ │ └── src │ │ │ │ └── index.js │ │ ├── index.js │ │ ├── package.json │ │ └── src │ │ │ ├── button │ │ │ ├── package.json │ │ │ └── src │ │ │ │ └── pc.jsx │ │ │ └── countdown │ │ │ ├── package.json │ │ │ └── src │ │ │ └── pc.jsx │ ├── theme-mobile │ │ ├── README.md │ │ ├── README.zh-CN.md │ │ ├── package.json │ │ └── src │ │ │ ├── base │ │ │ ├── basic-var.less │ │ │ ├── index.less │ │ │ ├── reset.less │ │ │ └── vars.less │ │ │ ├── button │ │ │ ├── index.less │ │ │ └── vars.less │ │ │ ├── custom.less │ │ │ └── mixins │ │ │ └── button.less │ ├── theme-watch │ │ ├── README.md │ │ ├── README.zh-CN.md │ │ ├── package.json │ │ └── src │ │ │ ├── base │ │ │ ├── basic-var.less │ │ │ ├── index.less │ │ │ ├── reset.less │ │ │ └── vars.less │ │ │ ├── button │ │ │ ├── index.less │ │ │ └── vars.less │ │ │ ├── custom.less │ │ │ └── mixins │ │ │ └── button.less │ ├── theme │ │ ├── README.md │ │ ├── README.zh-CN.md │ │ ├── package.json │ │ └── src │ │ │ ├── base │ │ │ ├── basic-var.less │ │ │ ├── index.less │ │ │ └── reset.less │ │ │ ├── button │ │ │ ├── index.less │ │ │ └── vars.less │ │ │ ├── countdown │ │ │ ├── index.less │ │ │ └── vars.less │ │ │ ├── custom.less │ │ │ └── mixins │ │ │ └── button.less │ └── vue │ │ ├── README.md │ │ ├── README.zh-CN.md │ │ ├── button │ │ ├── index.js │ │ ├── package.json │ │ └── src │ │ │ ├── index.js │ │ │ ├── mobile.vue │ │ │ ├── pc.vue │ │ │ └── watch.vue │ │ ├── common │ │ ├── package.json │ │ └── src │ │ │ ├── adapter │ │ │ ├── index.js │ │ │ ├── teleport.js │ │ │ ├── utils.js │ │ │ ├── vue2 │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ └── vue3 │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ ├── csscls.js │ │ │ └── index.js │ │ ├── config-provider │ │ ├── hooks │ │ │ └── use-config.ts │ │ ├── index.js │ │ ├── package.json │ │ └── src │ │ │ └── index.vue │ │ ├── countdown │ │ ├── index.js │ │ ├── package.json │ │ └── src │ │ │ └── pc.vue │ │ ├── index.js │ │ └── package.json ├── element-to-opentiny │ ├── README.md │ ├── README.zh-CN.md │ ├── index.html │ ├── package.json │ ├── public │ │ ├── assets │ │ │ ├── all-pages-element-to-opentiny.png │ │ │ ├── el-button-to-tiny-button.png │ │ │ ├── one-page-el-to-tiny.png │ │ │ ├── vue2-element.png │ │ │ └── vue2-to-vue3.png │ │ └── favicon.ico │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── logo.png │ │ ├── components │ │ │ ├── FormPage.vue │ │ │ ├── HelloWorld.vue │ │ │ ├── HomePage.vue │ │ │ └── ListPage.vue │ │ └── main.js │ └── vite.config.js ├── home │ ├── README.md │ ├── README.zh-CN.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── logo.png │ │ ├── index.css │ │ ├── main.js │ │ ├── router │ │ │ └── index.js │ │ └── views │ │ │ ├── Home.vue │ │ │ ├── React.vue │ │ │ ├── Solid.vue │ │ │ ├── Vue2.vue │ │ │ └── Vue3.vue │ └── vite.config.js ├── react │ ├── README.md │ ├── README.zh-CN.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.jsx │ │ ├── main.jsx │ │ └── style.css │ └── vite.config.js ├── solid │ ├── README.md │ ├── README.zh-CN.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.jsx │ │ ├── assets │ │ │ └── solid.svg │ │ ├── index.jsx │ │ └── style.css │ └── vite.config.js ├── vue2 │ ├── README.md │ ├── README.zh-CN.md │ ├── index.html │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── logo.png │ │ └── main.js │ └── vite.config.js └── vue3 │ ├── README.md │ ├── README.zh-CN.md │ ├── index.html │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.vue │ ├── DemoPoc.vue │ ├── assets │ │ └── vue.svg │ ├── main.js │ ├── style.css │ └── views │ │ ├── mobile.vue │ │ ├── pc.vue │ │ └── watch.vue │ └── vite.config.js ├── pnpm-workspace.yaml └── setup.js /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['airbnb-base'], 3 | // 指定解析器选项 4 | 'parserOptions': { 5 | ecmaVersion: 7, 6 | sourceType: 'module' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | 4 | vite.config.ts.timestamp* 5 | vitest.config.ts.timestamp* 6 | 7 | # local env 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-lock.yaml 16 | yarn.lock 17 | 18 | # Editor directories and files 19 | .idea 20 | .history 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | *.log 27 | *.stackdump 28 | 29 | tgzs 30 | *.tgz 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | pnpm-lock.yaml 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "printWidth": 80, 5 | "trailingComma": "none", 6 | "quoteProps": "preserve", 7 | "endOfLine": "auto", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": true, 10 | "jsxSingleQuote": false, 11 | "useTabs": false, 12 | "tabWidth": 2, 13 | "proseWrap": "preserve" 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/cross-framework 2 | 3 | This is a demonstration of the OpenTiny cross-end and cross-framework micro-front-end project (built based on WUJIE and pnpm): 4 | 5 | 6 | ## Local startup 7 | 8 | You can run the following command in the root directory of the project to download the project dependency: 9 | 10 | ```shell 11 | pnpm i 12 | ``` 13 | 14 | You can then start the case project by running the following command: 15 | 16 | ```shell 17 | pnpm dev 18 | ``` 19 | 20 | You can run the following commands to open a demonstration case of a framework: 21 | 22 | ```shell 23 | pnpm dev:react 24 | # or 25 | pnpm dev:solid 26 | # or 27 | pnpm dev:vue2 28 | # or 29 | pnpm dev:vue3 30 | ``` 31 | 32 | Congratulations on the success of the launch! 33 | 34 | ``` html 35 | cross-framework-component 36 | ├─ package.json 37 | ├─ packages 38 | │ ├─ components 39 | │ │ ├─ react 40 | │ │ ├─ renderless 41 | │ │ ├─ solid 42 | │ │ ├─ theme 43 | │ │ ├─ theme-mobile 44 | │ │ ├─ theme-watch 45 | │ │ └─ vue 46 | │ ├─ element-to-opentiny 47 | │ ├─ home 48 | │ ├─ react 49 | │ ├─ solid 50 | │ ├─ vue2 51 | │ └─ vue3 52 | ├─ pnpm-workspace.yaml 53 | ├─ README.md 54 | ├─ README.zh-CN.md 55 | └─ setup.js 56 | 57 | ``` 58 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/cross-framework 2 | 3 | 这是一个演示OpenTiny跨端、跨框架的案例微前端工程(基于WUJIE和pnpm搭建): 4 | 5 | 6 | ## 本地启动 7 | 8 | 可以通过在项目根目录执行以下命令下载工程依赖: 9 | 10 | ```shell 11 | pnpm i 12 | ``` 13 | 14 | 接着可以,通过运行以下命令启动基于WUJIE的微前端案例工程: 15 | 16 | ```shell 17 | pnpm dev 18 | ``` 19 | 20 | 或者也可以分别通过以下命令单独打开某个框架的演示案例: 21 | 22 | ```shell 23 | pnpm dev:react 24 | # or 25 | pnpm dev:solid 26 | # or 27 | pnpm dev:vue2 28 | # or 29 | pnpm dev:vue3 30 | ``` 31 | 32 | 恭喜你启动成功!🎉 33 | 34 | ``` html 35 | cross-framework-component 36 | ├─ package.json 37 | ├─ packages 38 | │ ├─ components 39 | │ │ ├─ react 40 | │ │ ├─ renderless 41 | │ │ ├─ solid 42 | │ │ ├─ theme 43 | │ │ ├─ theme-mobile 44 | │ │ ├─ theme-watch 45 | │ │ └─ vue 46 | │ ├─ element-to-opentiny 47 | │ ├─ home 48 | │ ├─ react 49 | │ ├─ solid 50 | │ ├─ vue2 51 | │ └─ vue3 52 | ├─ pnpm-workspace.yaml 53 | ├─ README.md 54 | ├─ README.zh-CN.md 55 | └─ setup.js 56 | 57 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/cross-framework", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "preinstall": "npx only-allow pnpm", 8 | "dev": "node setup.js", 9 | "dev:home": "pnpm -C packages/home dev", 10 | "dev:react": "pnpm -C packages/react dev", 11 | "dev:solid": "pnpm -C packages/solid dev", 12 | "dev:vue2": "pnpm -C packages/vue2 dev", 13 | "dev:vue3": "pnpm -C packages/vue3 dev", 14 | "dev:el": "pnpm -C packages/element-to-opentiny dev" 15 | }, 16 | "repository": { 17 | "type": "git" 18 | }, 19 | "keywords": [], 20 | "author": "", 21 | "license": "ISC", 22 | "dependencies": { 23 | "eslint": "8.48.0" 24 | }, 25 | "pnpm": { 26 | "packageExtensions": { 27 | "vue-template-compiler@2.6.14": { 28 | "peerDependencies": { 29 | "vue": "2.6.14" 30 | } 31 | }, 32 | "@opentiny/vue-locale@2.9.0": { 33 | "peerDependencies": { 34 | "vue": "2.6.14" 35 | } 36 | }, 37 | "@opentiny/vue-common@2.9.0": { 38 | "peerDependencies": { 39 | "vue": "2.6.14" 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/components/react/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/react 2 | 3 | This is a component library based on the React framework: 4 | 5 | ### 1. Installation 6 | 7 | Download the component library dependencies by running the following command: 8 | 9 | ```shell 10 | npm i @opentiny/react 11 | ``` 12 | 13 | ### 2. Introduction and use 14 | 15 | Use the @opentiny/react component in the `App.jsx` file. 16 | 17 | ```js 18 | import { Button } from '@opentiny/react' 19 | 20 | function App() { 21 | return ( 22 | <> 23 | 24 | 25 | ) 26 | } 27 | 28 | export default App 29 | 30 | ``` -------------------------------------------------------------------------------- /packages/components/react/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/react 2 | 3 | 这是一个基于React框架的组件库: 4 | 5 | ### 1. 安装 6 | 7 | 通过运行以下命令下载组件库依赖: 8 | 9 | ```shell 10 | npm i @opentiny/react 11 | ``` 12 | 13 | ### 2. 引入和使用 14 | 15 | 在`App.jsx`文件中使用 @opentiny/react 组件。 16 | 17 | ```js 18 | import { Button } from '@opentiny/react' 19 | 20 | function App() { 21 | return ( 22 | <> 23 | 24 | 25 | ) 26 | } 27 | 28 | export default App 29 | 30 | ``` 31 | -------------------------------------------------------------------------------- /packages/components/react/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/react-common", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/renderless": "workspace:~", 14 | "@opentiny/theme": "workspace:~", 15 | "classnames": "^2.3.2", 16 | "ahooks": "3.7.8", 17 | "react": "18.2.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/components/react/common/src/index.js: -------------------------------------------------------------------------------- 1 | import * as hooks from 'react' 2 | import '@opentiny/theme/base/index.less' 3 | import { useReactive } from 'ahooks' // 使用ahooks提供的useReactive抹平vue框架的响应式数据 4 | 5 | // 抹平vue框架的事件触发机制 6 | export const emit = 7 | (props) => 8 | (evName, ...args) => { 9 | if (props[evName] && typeof props[evName] === 'function') { 10 | props[evName](...args) 11 | } 12 | } 13 | 14 | // 模拟vue框架的nextTick,等待 dom 更新后触发回调 15 | export const useNextTick = (callback) => { 16 | queueMicrotask(callback) 17 | } 18 | 19 | // emitEvent, dispath, broadcast 20 | export const emitEvent = () => { 21 | const broadcast = () => { 22 | return '' 23 | } 24 | 25 | return { 26 | dispatch: () => { 27 | return '' 28 | }, 29 | broadcast 30 | } 31 | } 32 | 33 | export const useSetup = ({ 34 | props, // 模板层传递过来的props属性 35 | renderless, // renderless无渲染函数 36 | extendOptions = { framework: 'React' } // 模板层传递过来的额外参数 37 | }) => { 38 | const render = 39 | typeof props.tiny_renderless === 'function' 40 | ? props.tiny_renderless 41 | : renderless 42 | const utils = { 43 | parent: {}, 44 | emit: emit(props) 45 | } 46 | const sdk = render( 47 | props, 48 | { ...hooks, useReactive, useNextTick }, 49 | utils, 50 | extendOptions 51 | ) 52 | return { 53 | ...sdk, 54 | type: props.type ?? 'default' 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/components/react/index.js: -------------------------------------------------------------------------------- 1 | import Button from '@opentiny/react-button/src/pc' 2 | import Countdown from '@opentiny/react-countdown/src/pc' 3 | 4 | export { Button, Countdown } 5 | -------------------------------------------------------------------------------- /packages/components/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/react", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/react-button": "workspace:~", 14 | "@opentiny/react-countdown": "workspace:~" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/components/react/src/button/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/react-button", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/pc.jsx", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/renderless": "workspace:~", 14 | "@opentiny/theme": "workspace:~", 15 | "@opentiny/react-common": "workspace:~" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/components/react/src/button/src/pc.jsx: -------------------------------------------------------------------------------- 1 | import renderless from '@opentiny/renderless/button/react' // renderless无渲染层 2 | import { useSetup } from '@opentiny/react-common' // 抹平不同框架的适配层 3 | import '@opentiny/theme/button/index.less' // 复用OpenTinyVue的样式文件 4 | 5 | export default function Button(props) { 6 | const { 7 | children, 8 | text, 9 | autofocus, 10 | round, 11 | circle, 12 | icon: Icon, 13 | size, 14 | nativeType = 'button' 15 | } = props 16 | 17 | const { handleClick, state, tabindex, type, $attrs } = useSetup({ 18 | // 通过common适配层的useSetup处理props和renderless无渲染层 19 | props: { nativeType: 'button', resetTime: 1000, ...props }, 20 | renderless 21 | }) 22 | 23 | const className = [ 24 | 'tiny-button', 25 | type ? 'tiny-button--' + type : '', 26 | size ? 'tiny-button--' + size : '', 27 | state.disabled ? 'is-disabled' : '', 28 | state.plain ? 'is-plain' : '', 29 | round ? 'is-round' : '', 30 | circle ? 'is-circle' : '' 31 | ] 32 | .join(' ') 33 | .trim() 34 | return ( 35 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /packages/components/react/src/countdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/react-countdown", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/pc.jsx", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/renderless": "workspace:~", 14 | "@opentiny/theme": "workspace:~", 15 | "@opentiny/react-common": "workspace:~" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/components/react/src/countdown/src/pc.jsx: -------------------------------------------------------------------------------- 1 | import renderless from '@opentiny/renderless/countdown/react' 2 | import { useSetup } from '@opentiny/react-common' 3 | import '@opentiny/theme/countdown/index.less' 4 | 5 | export default function Countdown(props) { 6 | const defaultProps = { 7 | deadline: 60, 8 | completeZero: true, 9 | leftSecond: 0, 10 | autoPlay: false, 11 | ignoreDay: true 12 | } 13 | 14 | const newProps = { ...defaultProps, ...props } 15 | 16 | const { state } = useSetup({ 17 | props: newProps, 18 | renderless 19 | }) 20 | 21 | return ( 22 |
{`${state.hour}:${state.minute}:${state.second}`}
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /packages/components/renderless/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/renderless 2 | 3 | 这是一个为 OpenTiny 跨端、跨框架组件提供 renderless 无渲染逻辑的公共逻辑层项目工程。 4 | # @opentiny/renderless 5 | 6 | This is a common logic layer project that provides renderless non-rendering logic for OpenTiny cross-end and cross-framework components. -------------------------------------------------------------------------------- /packages/components/renderless/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/renderless 2 | 3 | 这是一个为 OpenTiny 跨端、跨框架组件提供 renderless 无渲染逻辑的公共逻辑层项目工程。 4 | -------------------------------------------------------------------------------- /packages/components/renderless/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/renderless", 3 | "version": "3.9.0", 4 | "sideEffects": false, 5 | "type": "module", 6 | "exports": { 7 | "./package.json": "./package.json", 8 | "./*": "./src/*" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/components/renderless/src/button/index.js: -------------------------------------------------------------------------------- 1 | export const handleClick = 2 | ({ emit, props, state, framework }) => 3 | (event) => { 4 | if (props.nativeType === 'button' && props.resetTime > 0) { 5 | state.disabled = true 6 | state.timer = setTimeout(() => { 7 | state.disabled = false 8 | }, props.resetTime) 9 | } 10 | 11 | console.log(`${framework}框架代码已触发!!!!!!!!!`) 12 | 13 | emit('click', event) 14 | } 15 | 16 | export const clearTimer = (state) => () => clearTimeout(state.timer) 17 | -------------------------------------------------------------------------------- /packages/components/renderless/src/button/react.js: -------------------------------------------------------------------------------- 1 | import { handleClick, clearTimer } from './index' 2 | 3 | export const api = ['state', 'handleClick'] 4 | 5 | export default function renderless( 6 | props, 7 | { useReactive }, 8 | { emit }, 9 | { framework } 10 | ) { 11 | // 利用ahooks提供的useReactive模拟vue的响应式数据,并且使用react的useRef响应式数据被重复执行定义 12 | const state = useReactive({ 13 | timer: null, 14 | disabled: !!props.disabled, 15 | plain: props.plain, 16 | formDisabled: false 17 | }) 18 | 19 | const api = { 20 | state, 21 | clearTimer: clearTimer(state), 22 | handleClick: handleClick({ emit, props, state, framework }) 23 | } 24 | 25 | return api 26 | } 27 | -------------------------------------------------------------------------------- /packages/components/renderless/src/button/solid.js: -------------------------------------------------------------------------------- 1 | import { handleClick, clearTimer } from './index' 2 | 3 | export const api = ['state', 'handleClick'] 4 | 5 | export default function renderless( 6 | props, 7 | { useReactive }, 8 | { emit }, 9 | { framework } 10 | ) { 11 | const { state, proxy } = useReactive({ 12 | timer: null, 13 | disabled: !!props.disabled, 14 | plain: props.plain 15 | }) 16 | 17 | const api = { 18 | state, 19 | clearTimer: clearTimer(proxy), 20 | handleClick: handleClick({ emit, props, state: proxy, framework }) 21 | } 22 | 23 | return api 24 | } 25 | -------------------------------------------------------------------------------- /packages/components/renderless/src/button/vue.js: -------------------------------------------------------------------------------- 1 | import { handleClick, clearTimer } from './index' 2 | 3 | export const api = ['state', 'handleClick'] 4 | 5 | export const renderless = ( 6 | props, 7 | { computed, onBeforeUnmount, reactive, watch, inject }, 8 | { emit, parent }, 9 | { framework } 10 | ) => { 11 | parent.tinyForm = parent.tinyForm || inject('form', null) 12 | 13 | const state = reactive({ 14 | timer: null, 15 | disabled: props.disabled, 16 | plain: computed(() => props.plain || (parent.buttonGroup || {}).plain), 17 | formDisabled: computed(() => (parent.tinyForm || {}).disabled), 18 | buttonDisabled: computed( 19 | () => 20 | props.disabled || 21 | state.disabled || 22 | (parent.buttonGroup || {}).disabled || 23 | state.formDisabled 24 | ) 25 | }) 26 | 27 | watch( 28 | () => props.disabled, 29 | (value) => { 30 | state.disabled = value 31 | }, 32 | { immediate: true } 33 | ) 34 | 35 | const api = { 36 | state, 37 | clearTimer: clearTimer(state), 38 | handleClick: handleClick({ emit, props, state, framework }) 39 | } 40 | 41 | onBeforeUnmount(api.clearTimer) 42 | 43 | return api 44 | } 45 | -------------------------------------------------------------------------------- /packages/components/renderless/src/countdown/index.js: -------------------------------------------------------------------------------- 1 | // 格式化数字格式 2 | const formatNumber = (number, completeZero) => { 3 | if (number < 10 && completeZero) { 4 | return '0' + number 5 | } else { 6 | return number.toString() 7 | } 8 | } 9 | 10 | // 模拟setInterval 11 | export const CustomSetInterval = function (func, millisecond) { 12 | let setIntervalId 13 | 14 | if (typeof func === 'function') { 15 | setIntervalId = setTimeout(function self() { 16 | setIntervalId = setTimeout(self, millisecond) 17 | 18 | func() 19 | }, millisecond) 20 | } 21 | 22 | this.stop = () => { 23 | clearTimeout(setIntervalId) 24 | } 25 | } 26 | 27 | export const getRestTime = (state) => { 28 | // 获取剩余时间(秒) 29 | return Math.max(Math.round((state.deadlineTimestamp - Date.now()) / 1000), 0) 30 | } 31 | 32 | export const render = 33 | ({ state, props, nextTick, emit }) => 34 | () => { 35 | const restTime = getRestTime(state) 36 | if (state.ignoreDay) { 37 | state.hour = formatNumber( 38 | Math.floor(restTime / (60 * 60)), 39 | props.completeZero 40 | ) 41 | } else { 42 | state.day = formatNumber( 43 | Math.floor(restTime / (24 * 60 * 60)), 44 | props.completeZero 45 | ) 46 | state.hour = formatNumber( 47 | Math.floor((restTime / (60 * 60)) % 24), 48 | props.completeZero 49 | ) 50 | } 51 | state.minute = formatNumber( 52 | Math.floor((restTime / 60) % 60), 53 | props.completeZero 54 | ) 55 | 56 | state.second = formatNumber(restTime % 60, props.completeZero) 57 | 58 | nextTick(() => { 59 | emit('update', { 60 | day: state.day, 61 | hour: state.hour, 62 | minute: state.minute, 63 | second: state.second, 64 | restSecond: restTime // 剩余时间(秒) 65 | }) 66 | }) 67 | } 68 | 69 | export const start = 70 | ({ render, state, emit, props }) => 71 | () => { 72 | state.deadlineTimestamp = Date.now() + props.deadline * 1000 73 | if (getRestTime(state) > props.leftSecond) { 74 | state.setIntervalInstance.stop() 75 | 76 | state.setIntervalInstance = new CustomSetInterval(() => { 77 | render() 78 | 79 | if (getRestTime(state) <= props.leftSecond) { 80 | state.setIntervalInstance.stop() 81 | } 82 | }, 1000) 83 | } 84 | } 85 | 86 | export const reset = 87 | ({ state, props, renderFn }) => 88 | () => { 89 | state.deadlineTimestamp = Date.now() + props.deadline * 1000 90 | state.setIntervalInstance.stop() 91 | renderFn() 92 | } 93 | 94 | export const init = ({ api, props }) => { 95 | const { start, reset, render } = api 96 | render() 97 | props.operate && props.operate({ start, reset }) 98 | if (props.autoPlay) { 99 | start() 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /packages/components/renderless/src/countdown/react.js: -------------------------------------------------------------------------------- 1 | import { 2 | render, 3 | start, 4 | reset, 5 | init 6 | } from './index' 7 | 8 | export const api = ['state'] 9 | 10 | export default function renderless( 11 | props, 12 | { useReactive, useEffect, useNextTick: nextTick, use }, 13 | { emit } 14 | ) { 15 | const state = useReactive({ 16 | day: '00', 17 | hour: '00', 18 | minute: '00', 19 | second: '00', 20 | // 每秒执行(实例) 21 | setIntervalInstance: Object.freeze({ stop: () => {} }), 22 | deadlineTimestamp: Date.now() + props.deadline * 1000 23 | }) 24 | 25 | const renderFn = render({ state, props, nextTick, emit }) 26 | 27 | const api = { 28 | state, 29 | render: renderFn, 30 | start: start({ render: renderFn, state, emit, props }), 31 | reset: reset({ state, props, renderFn }) 32 | } 33 | 34 | // 模拟vue的mounted,只会执行一次 35 | useEffect(()=> init({ api, props, state }),[]) 36 | 37 | 38 | return api 39 | } 40 | -------------------------------------------------------------------------------- /packages/components/renderless/src/countdown/solid.js: -------------------------------------------------------------------------------- 1 | import { 2 | render, 3 | start, 4 | reset, 5 | init 6 | } from './index' 7 | 8 | export const api = ['state'] 9 | 10 | export default function renderless( 11 | props, 12 | { useReactive, useNextTick: nextTick }, 13 | { emit } 14 | ) { 15 | const { state, proxy } = useReactive({ 16 | day: '00', 17 | hour: '00', 18 | minute: '00', 19 | second: '00', 20 | // 每秒执行(实例) 21 | setIntervalInstance: Object.freeze({ stop: () => {} }), 22 | deadlineTimestamp: Date.now() + props.deadline * 1000 23 | }) 24 | const renderFn = render({ state: proxy, props, nextTick, emit }) 25 | 26 | const api = { 27 | state, 28 | render: renderFn, 29 | start: start({ render: renderFn, state: proxy, emit, props }), 30 | reset: reset({ state: proxy, props, renderFn }) 31 | } 32 | 33 | init({ api, props, state: proxy }) 34 | 35 | return api 36 | } 37 | -------------------------------------------------------------------------------- /packages/components/renderless/src/countdown/vue.js: -------------------------------------------------------------------------------- 1 | import { 2 | render, 3 | start, 4 | reset, 5 | init 6 | } from './index' 7 | 8 | export const api = ['state'] 9 | 10 | export const renderless = ( 11 | props, 12 | { onBeforeUnmount, reactive, watch }, 13 | { emit, nextTick } 14 | ) => { 15 | const state = reactive({ 16 | day: '00', 17 | hour: '00', 18 | minute: '00', 19 | second: '00', 20 | setIntervalInstance: Object.freeze({ stop: () => {} }), 21 | deadlineTimestamp: Date.now() + props.deadline * 1000 22 | }) 23 | 24 | const renderFn = render({ state, props, nextTick, emit }) 25 | 26 | const api = { 27 | state, 28 | render: renderFn, 29 | start: start({ render: renderFn, state, emit, props }), 30 | reset: reset({ state, props, renderFn }) 31 | } 32 | 33 | init({ api, props, state }) 34 | 35 | onBeforeUnmount(() => { 36 | state.setIntervalInstance.stop() 37 | }) 38 | 39 | return api 40 | } 41 | -------------------------------------------------------------------------------- /packages/components/solid/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/solid 2 | 3 | 这是一个基于 Solid 框架的组件库: 4 | 5 | ### 1. 安装 6 | 7 | 通过运行以下命令下载组件库依赖: 8 | 9 | ```shell 10 | npm i @opentiny/solid 11 | ``` 12 | 13 | ### 2. 引入和使用 14 | 15 | 在`App.jsx`文件中使用 @opentiny/solid 组件。 16 | 17 | ```js 18 | import { Button } from '@opentiny/solid' 19 | 20 | function App() { 21 | return ( 22 | <> 23 | 24 | 25 | ) 26 | } 27 | 28 | export default App 29 | 30 | ``` 31 | # @opentiny/solid 32 | 33 | This is a component library based on the Solid framework: 34 | 35 | ### 1. Installation 36 | 37 | Download the component library dependencies by running the following command: 38 | 39 | ```shell 40 | npm i @opentiny/solid 41 | ``` 42 | 43 | ### 2. Introduction and use 44 | 45 | Use the @opentiny/solid component in the `App.jsx` file. 46 | 47 | ```js 48 | import { Button } from '@opentiny/solid' 49 | 50 | function App() { 51 | return ( 52 | <> 53 | 54 | 55 | ) 56 | } 57 | 58 | export default App 59 | 60 | ``` -------------------------------------------------------------------------------- /packages/components/solid/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/solid 2 | 3 | 这是一个基于 Solid 框架的组件库: 4 | 5 | ### 1. 安装 6 | 7 | 通过运行以下命令下载组件库依赖: 8 | 9 | ```shell 10 | npm i @opentiny/solid 11 | ``` 12 | 13 | ### 2. 引入和使用 14 | 15 | 在`App.jsx`文件中使用 @opentiny/solid 组件。 16 | 17 | ```js 18 | import { Button } from '@opentiny/solid' 19 | 20 | function App() { 21 | return ( 22 | <> 23 | 24 | 25 | ) 26 | } 27 | 28 | export default App 29 | 30 | ``` 31 | -------------------------------------------------------------------------------- /packages/components/solid/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/solid-common", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/renderless": "workspace:~", 14 | "@opentiny/theme": "workspace:~", 15 | "classnames": "^2.3.2", 16 | "solid-js": "^1.7.8" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/components/solid/common/src/index.js: -------------------------------------------------------------------------------- 1 | import * as hooks from 'solid-js' 2 | import { createSignal } from 'solid-js' 3 | import '@opentiny/theme/base/index.less' 4 | 5 | const EVENTS_PREFIX = 'on' 6 | 7 | // 处理solid事件触发机制 8 | export const emit = 9 | (props) => 10 | (evName, ...args) => { 11 | const eventsName = `${EVENTS_PREFIX}${evName[0].toLocaleUpperCase()}${evName.slice( 12 | 1 13 | )}` 14 | if (props[eventsName] && typeof props[eventsName] === 'function') { 15 | props[eventsName](...args) 16 | } 17 | } 18 | 19 | export const useSetState = (initialState) => { 20 | const [state, setState] = createSignal(initialState, { equals: false }) 21 | 22 | return [state, setState] 23 | } 24 | 25 | // props 应该不用做处理, props 都是 . 访问。 26 | export const useReactive = (staticObject) => { 27 | const [state, setState] = useSetState(staticObject) 28 | 29 | return { 30 | state, 31 | proxy: new Proxy(state(), { 32 | get(target, property) { 33 | if (typeof target[property] === 'function') { 34 | return target[property](target) 35 | } else { 36 | return target[property] 37 | } 38 | }, 39 | set(target, property, value) { 40 | Reflect.set(target, property, value) 41 | setState((val) => val) 42 | return true 43 | } 44 | }) 45 | } 46 | } 47 | 48 | // nextTick, 等待 dom 更新后触发回调 49 | export const useNextTick = (callback) => { 50 | queueMicrotask(callback) 51 | } 52 | 53 | // emitEvent, dispath, broadcast 54 | export const emitEvent = () => { 55 | const broadcast = () => { 56 | return '' 57 | } 58 | 59 | return { 60 | dispatch: () => { 61 | return '' 62 | }, 63 | broadcast 64 | } 65 | } 66 | 67 | export const useSetup = ({ 68 | props, 69 | renderless, 70 | extendOptions = { framework: 'Solid' } 71 | }) => { 72 | const render = 73 | typeof props.tiny_renderless === 'function' 74 | ? props.tiny_renderless 75 | : renderless 76 | const utils = { 77 | parent: {}, 78 | emit: emit(props) 79 | } 80 | const sdk = render( 81 | props, 82 | { ...hooks, useReactive, useNextTick }, 83 | utils, 84 | extendOptions 85 | ) 86 | return { 87 | ...sdk, 88 | type: props.type ?? 'default' 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /packages/components/solid/index.js: -------------------------------------------------------------------------------- 1 | import Button from '@opentiny/solid-button/src/pc' 2 | import Countdown from '@opentiny/solid-countdown/src/pc' 3 | 4 | export { Button, Countdown } 5 | -------------------------------------------------------------------------------- /packages/components/solid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/solid", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/solid-button": "workspace:~", 14 | "@opentiny/solid-countdown": "workspace:~" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/components/solid/src/button/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/solid-button", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/pc.jsx", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/renderless": "workspace:~", 14 | "@opentiny/theme": "workspace:~", 15 | "@opentiny/solid-common": "workspace:~" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/components/solid/src/button/src/pc.jsx: -------------------------------------------------------------------------------- 1 | import renderless from '@opentiny/renderless/button/solid' 2 | import { useSetup } from '@opentiny/solid-common' 3 | import '@opentiny/theme/button/index.less' 4 | 5 | export default function Button(props) { 6 | const { 7 | children, 8 | text, 9 | autofocus, 10 | round, 11 | circle, 12 | icon: Icon, 13 | size, 14 | nativeType = 'button' 15 | } = props 16 | const { handleClick, state, tabindex, type, $attrs } = useSetup({ 17 | props: { nativeType: 'button', resetTime: 1000, ...props }, 18 | renderless 19 | }) 20 | 21 | return ( 22 | 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /packages/components/solid/src/countdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/solid-countdown", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/pc.jsx", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/renderless": "workspace:~", 14 | "@opentiny/theme": "workspace:~", 15 | "@opentiny/solid-common": "workspace:~" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/components/solid/src/countdown/src/pc.jsx: -------------------------------------------------------------------------------- 1 | import renderless from '@opentiny/renderless/countdown/solid' 2 | import { useSetup } from '@opentiny/solid-common' 3 | import '@opentiny/theme/countdown/index.less' 4 | 5 | export default function Countdown(props) { 6 | const defaultProps = { 7 | deadline: 60, 8 | completeZero: true, 9 | leftSecond: 0, 10 | ignoreDay: true 11 | } 12 | const { state } = useSetup({ 13 | props: { ...defaultProps, ...props }, 14 | renderless 15 | }) 16 | 17 | return ( 18 |
{`${state().hour}:${ 19 | state().minute 20 | }:${state().second}`}
21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/theme-mobile 2 | 3 | This is a project to provide Mobile template styles for OpenTiny cross-end and cross-framework components. -------------------------------------------------------------------------------- /packages/components/theme-mobile/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/theme-mobile 2 | 3 | 这是一个为 OpenTiny 跨端、跨框架组件提供 Mobile 模板样式的工程 4 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/theme-mobile", 3 | "version": "3.9.0", 4 | "description": "", 5 | "sideEffects": false, 6 | "dependencies": { 7 | "less": "~4.1.3" 8 | }, 9 | "type": "module", 10 | "exports": { 11 | "./*": "./src/*" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/src/base/basic-var.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | /*基础变量定义*/ 14 | :root,:host{ 15 | /* 品牌色*/ 16 | --ti-mobile-base-color-brand-1: #4a79fe; /* 蓝_品牌色*/ 17 | --ti-mobile-base-color-brand-2: #3168f1; 18 | --ti-mobile-base-color-brand-3: #6e94fe; 19 | --ti-mobile-base-color-brand-4: #92affe; 20 | --ti-mobile-base-color-brand-5: #b7c9ff; 21 | --ti-mobile-base-color-brand-6: #95a2f8; 22 | --ti-mobile-base-color-brand-6: #ffffff; 23 | 24 | /* 1.2背景色*/ 25 | --ti-mobile-base-color-bg-1: #f5f5f5; /* 深灰_背景色*/ 26 | --ti-mobile-base-color-bg-2: #fafafa; /* 浅灰_背景色*/ 27 | --ti-mobile-base-color-bg-3: #ffffff; /* 白色_背景色*/ 28 | 29 | /* 文本、线、面等常用灰色可用颜色参考:*/ 30 | --ti-mobile-base-color-common-1: #000000; 31 | --ti-mobile-base-color-common-2: #191919; 32 | --ti-mobile-base-color-common-3: #262626; 33 | --ti-mobile-base-color-common-4: #333333; 34 | --ti-mobile-base-color-common-5: #595959; 35 | --ti-mobile-base-color-common-6: #999999; 36 | --ti-mobile-base-color-common-7: #c2c2c2; 37 | --ti-mobile-base-color-common-8: #dbdbdb; 38 | --ti-mobile-base-color-common-9: #ededed; 39 | --ti-mobile-base-color-common-10: #f5f5f5; 40 | --ti-mobile-base-color-common-11: #fafafa; 41 | --ti-mobile-base-color-common-12: #ffffff; 42 | --ti-mobile-base-color-common-13: #627199; 43 | 44 | /* 功能色*/ 45 | --ti-mobile-common-color-warning-figure-1: #eb4096; /* 致命警告图形色*/ 46 | --ti-mobile-common-color-warning-text-1: #e61c81; /* 致命警告文字色*/ 47 | --ti-mobile-common-color-warning-figure-2: #ff9c32; /* 一般告警/中性1图形色*/ 48 | --ti-mobile-common-color-warning-text-2: #ff8800; /* 一般告警/中性1文字色*/ 49 | --ti-mobile-common-color-warning-figure-3: #1ebe40; /* 一般告警/中性1图形色*/ 50 | --ti-mobile-common-color-warning-text-3: #00b336; /* 一般告警/中性1文字色*/ 51 | 52 | --ti-mobile-common-color-error-figure-1: #eb5454; /* 异常/下降/告警/驳回/退订/失败图形色*/ 53 | --ti-mobile-common-color-error-text-1: #e62222; /* 异常/下降/告警/驳回/退订/失败文字色*/ 54 | --ti-mobile-common-color-error-figure-2: #fccd32; /* 异常/下降/告警/驳回/退订/失败图形色*/ 55 | --ti-mobile-common-color-error-text-2: #fcbe1e; /* 异常/下降/告警/驳回/退订/失败文字色*/ 56 | --ti-mobile-common-color-error-figure-3: #1e8aff; /* 异常/下降/告警/驳回/退订/失败图形色*/ 57 | --ti-mobile-common-color-error-text-3: #1476ff; /* 异常/下降/告警/驳回/退订/失败文字色*/ 58 | --ti-mobile-common-color-error-figure-4: #c2c2c2; /* 异常/下降/告警/驳回/退订/失败图形色*/ 59 | --ti-mobile-common-color-error-text-4: #999999; /* 异常/下降/告警/驳回/退订/失败文字色*/ 60 | 61 | /* 图表推荐配色*/ 62 | --ti-mobile-common-color-chart-1: #1476ff; 63 | --ti-mobile-common-color-chart-2: #10c7c1; 64 | --ti-mobile-common-color-chart-3: #b878f0; 65 | --ti-mobile-common-color-chart-4: #d6a981; 66 | --ti-mobile-common-color-chart-5: #c1cc7a; 67 | 68 | /* 文本色*/ 69 | --ti-mobile-common-color-text-primary: var( 70 | --ti-mobile-base-color-common-2 71 | ); /* 一级文本色-重要信息/标题颜色/输入类文本颜色*/ 72 | --ti-mobile-common-color-text-secondary: var(--ti-mobile-base-color-common-5); /* 二级文本色-次要信息*/ 73 | --ti-mobile-common-color-text-weaken-dark: var(--ti-mobile-base-color-common-6); /* 三级文本色-弱化信息/说明文字*/ 74 | --ti-mobile-common-color-text-weaken-ligtht: var(--ti-mobile-base-color-common-7); /* 文本_弱化色_浅色/提示*/ 75 | --ti-mobile-common-color-text-weaken-disabled: var(--ti-mobile-base-color-common-8); /* 文本_弱化色_浅色/禁用*/ 76 | --ti-mobile-common-color-text-white: var(--ti-mobile-base-color-common-12); /* 文本_白色*/ 77 | --ti-mobile-common-color-text-placeholder-primary: #dbe5fc; /* placeholder_主要*/ 78 | --ti-mobile-common-color-text-placeholder-gray: #acacac; /* placeholder_灰*/ 79 | --ti-mobile-common-color-text-high-light: var(--ti-mobile-base-color-brand-1); /* 文本_高亮*/ 80 | 81 | /* 文本链接色*/ 82 | --ti-mobile-common-color-link-dark: var(--ti-mobile-base-color-common-2); /* 文本_链接__深色*/ 83 | --ti-mobile-common-color-link-highlight: var(--ti-mobile-base-color-brand-1); /* 文本_链接_高亮色*/ 84 | --ti-mobile-common-color-link-gray: var(--ti-mobile-base-color-common-13); /* 文本_链接_灰色加强*/ 85 | --ti-mobile-common-color-link-white: var(--ti-mobile-base-color-common-12); /* 文本_链接_白色*/ 86 | 87 | /* 线颜色*/ 88 | --ti-mobile-common-color-line-hightlight: var(--ti-mobile-base-color-brand-1); /* 高亮_描边色*/ 89 | --ti-mobile-common-color-line-dark: var(--ti-mobile-base-color-common-9); /* 深_描边色*/ 90 | --ti-mobile-common-color-line-light: var(--ti-mobile-base-color-common-10); /* 浅_描边色*/ 91 | --ti-mobile-common-color-line-white: var(--ti-mobile-base-color-common-12); /* 白色*/ 92 | 93 | /* 蒙层色*/ 94 | --ti-mobile-common-color-mask-light: rgba(0, 0, 0, 0.5); /* 蒙层_相对浅*/ 95 | --ti-mobile-common-color-mask-dark: rgba(0, 0, 0, 0.75); /* 蒙层_相对深*/ 96 | 97 | /* 背景色*/ 98 | --ti-mobile-common-bg-color-white: var(--ti-mobile-base-color-bg-3); /* 背景_白*/ 99 | --ti-mobile-common-bg-color-main: var(--ti-mobile-base-color-brand-1); /* 背景_蓝*/ 100 | --ti-mobile-common-bg-color-light: var(--ti-mobile-base-color-bg-2); /* 背景_浅灰*/ 101 | --ti-mobile-common-bg-color-blue-1: var(--ti-mobile-base-color-brand-2); /* 背景_蓝*/ 102 | --ti-mobile-common-bg-color-disabled: #e8e8e8; /* 背景_禁用*/ 103 | --ti-mobile-common-bg-color-dark-gray: var(--ti-mobile-base-color-bg-1); /* 背景_深灰*/ 104 | --ti-mobile-common-bg-color-weaken: var(--ti-mobile-base-color-common-8); /* 背景_弱化色*/ 105 | --ti-mobile-common-status-bg-color-success: #5bd475; /* 背景_成功/通过*/ 106 | --ti-mobile-common-status-bg-color-error: #eb5454; /* 背景_失败/驳回*/ 107 | --ti-mobile-common-status-bg-color-normal: #fff; /* 背景_默认*/ 108 | --ti-mobile-common-status-bg-color-handing: var(--ti-mobile-base-color-brand-1); /* 背景_进行中*/ 109 | } 110 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/src/base/index.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @import './reset.less'; 14 | @import './basic-var.less'; 15 | @import './vars.less'; 16 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/src/base/reset.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @import '../custom.less'; 14 | 15 | [class*=~'@{css-prefix}'] { 16 | -webkit-box-sizing: border-box; 17 | box-sizing: border-box; 18 | 19 | *:after, 20 | *:before { 21 | -webkit-box-sizing: border-box; 22 | box-sizing: border-box; 23 | } 24 | 25 | a { 26 | cursor: pointer; 27 | background-image: none; 28 | text-decoration: none; 29 | outline: none; 30 | 31 | &:focus, 32 | &:active, 33 | &:hover { 34 | outline: none; 35 | text-decoration: none; 36 | } 37 | } 38 | 39 | dl, 40 | dt, 41 | dd, 42 | ul, 43 | ol, 44 | li, 45 | th, 46 | td { 47 | margin: 0; 48 | padding: 0; 49 | } 50 | 51 | ol, 52 | ul { 53 | list-style: none; 54 | } 55 | 56 | audio, 57 | canvas, 58 | video { 59 | display: inline-block; 60 | } 61 | 62 | audio:not([controls]) { 63 | display: none; 64 | height: 0; 65 | } 66 | 67 | mark { 68 | background: #ff0; 69 | color: #000; 70 | } 71 | 72 | pre { 73 | white-space: pre-wrap; 74 | } 75 | 76 | sub, 77 | sup { 78 | font-size: 75%; 79 | line-height: 0; 80 | position: relative; 81 | vertical-align: baseline; 82 | } 83 | 84 | sup { 85 | top: -0.5em; 86 | } 87 | 88 | sub { 89 | bottom: -0.25em; 90 | } 91 | 92 | fieldset { 93 | border: 1px solid #c0c0c0; 94 | margin: 0 2px; 95 | padding: 0.35em 0.625em 0.75em; 96 | } 97 | 98 | legend { 99 | border: 0; 100 | padding: 0; 101 | } 102 | 103 | // 清除IE 104 | input::-ms-clear, 105 | input::-ms-reveal { 106 | display: none; 107 | } 108 | 109 | button::-moz-focus-inner, 110 | input::-moz-focus-inner { 111 | border: 0; 112 | padding: 0; 113 | } 114 | 115 | textarea { 116 | overflow: auto; 117 | vertical-align: top; 118 | } 119 | 120 | table { 121 | border-collapse: collapse; 122 | border-spacing: 0; 123 | } 124 | 125 | .@{css-prefix}hide { 126 | display: none; 127 | } 128 | 129 | .popper__arrow { 130 | &, 131 | &:after { 132 | position: absolute; 133 | display: block; 134 | width: 0; 135 | height: 0; 136 | border-color: transparent; 137 | border-style: solid; 138 | } 139 | } 140 | 141 | @media (min-width: 768px) { 142 | ::-webkit-scrollbar { 143 | width: 8px; 144 | height: 8px; 145 | } 146 | 147 | ::-webkit-scrollbar-track-piece { 148 | background: #fafafa; 149 | } 150 | 151 | ::-webkit-scrollbar-thumb { 152 | background: #5c6173; 153 | border-radius: 6px; 154 | } 155 | 156 | ::-webkit-scrollbar-thumb:hover { 157 | background: #999999; 158 | } 159 | 160 | ::-webkit-scrollbar-thumb:active { 161 | background: #999999; 162 | } 163 | 164 | .@{css-prefix}scrollbar::-webkit-scrollbar { 165 | width: 8px; 166 | height: 8px; 167 | } 168 | 169 | .@{css-prefix}scrollbar::-webkit-scrollbar-track-piece { 170 | background: transparent; 171 | border: 0; 172 | } 173 | 174 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb { 175 | background: #5c6173; 176 | border-radius: 4px; 177 | } 178 | 179 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb:hover { 180 | background: #999999; 181 | } 182 | 183 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb:active { 184 | background: #999999; 185 | } 186 | 187 | .@{css-prefix}min-scrollbar::-webkit-scrollbar { 188 | width: 4px; 189 | height: 4px; 190 | } 191 | 192 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-track-piece { 193 | background: transparent; 194 | border: 0; 195 | } 196 | 197 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb { 198 | background: #bfbfbf; 199 | border-radius: 2px; 200 | } 201 | 202 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb:hover { 203 | background: #999999; 204 | } 205 | 206 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb:active { 207 | background: #999999; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/src/base/vars.less: -------------------------------------------------------------------------------- 1 | :root { 2 | --ti-mobile-base-color-primary-normal: #1890ff; 3 | --ti-mobile-base-color-primary-disabled: #bfbfbf; 4 | --ti-mobile-base-color-primary-hover: #40a9ff; 5 | --ti-mobile-base-color-primary-active: #096dd9; 6 | --ti-mobile-base-color-success-normal: #52c41a; 7 | --ti-mobile-base-color-success-disabled: #a6c3b9; 8 | --ti-mobile-base-color-success-hover: #73d13d; 9 | --ti-mobile-base-color-success-active: #389e0d; 10 | --ti-mobile-base-color-warning-normal: #faad14; 11 | --ti-mobile-base-color-warning-disabled: #d3c6a2; 12 | --ti-mobile-base-color-warning-hover: #ffc53d; 13 | --ti-mobile-base-color-warning-active: #ffc53d; 14 | --ti-mobile-base-color-danger-normal: #f5222d; 15 | --ti-mobile-base-color-danger-disabled: #d8bab5; 16 | --ti-mobile-base-color-danger-hover: #ff4d4f; 17 | --ti-mobile-base-color-danger-active: #cf1322; 18 | --ti-mobile-base-color-info-normal: #333333; 19 | --ti-mobile-base-color-info-disabled: #bfbfbf; 20 | --ti-mobile-base-color-info-hover: #54657e; 21 | --ti-mobile-base-color-info-active: #54657e; 22 | --ti-mobile-base-color-light: #fff; 23 | --ti-mobile-base-color-dark: #000; 24 | --ti-mobile-base-color-border: #d9d9d9; 25 | --ti-mobile-base-color-secondary: #666; 26 | --ti-mobile-base-color-placeholder: #999; 27 | --ti-mobile-base-color-hover-background: #e6f7ff; 28 | --ti-mobile-base-color-selected-background: #f5f5f5; 29 | --ti-mobile-base-color-navigation-background: #2e3243; 30 | --ti-mobile-base-radius-large: 3px; 31 | --ti-mobile-base-radius-medium: 2px; 32 | --ti-mobile-base-radius-small: 1px; 33 | --ti-mobile-base-font-family-normal: Helvetica, Arial, 'microsoft yahei'; 34 | --ti-mobile-base-font-size: 12px; 35 | --ti-mobile-base-font-size-normal: 1em; 36 | --ti-mobile-base-font-size-large: 1.125em; 37 | --ti-mobile-base-font-weight-bold: 700; 38 | --ti-mobile-base-size-width-large: 130px; 39 | --ti-mobile-base-size-width-medium: 120px; 40 | --ti-mobile-base-size-width-normal: 80px; 41 | --ti-mobile-base-size-width-small: 36px; 42 | --ti-mobile-base-size-width-minor: 30px; 43 | --ti-mobile-base-size-width-mini: 24px; 44 | --ti-mobile-base-size-height-large: 48px; 45 | --ti-mobile-base-size-height-medium: 42px; 46 | --ti-mobile-base-size-height-small: 36px; 47 | --ti-mobile-base-size-height-minor: 30px; 48 | --ti-mobile-base-size-height-mini: 24px; 49 | } 50 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/src/button/index.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @import '../mixins/button.less'; 14 | @import '../custom.less'; 15 | @import './vars.less'; 16 | 17 | @button-prefix-cls: ~'@{css-prefix}mobile-button'; 18 | @svg-prefix-cls: ~'@{css-prefix}svg'; 19 | 20 | .@{button-prefix-cls} { 21 | position: relative; 22 | margin: 0; 23 | padding: 0; 24 | height: var(--ti-mobile-button-height); 25 | line-height: var(--ti-mobile-button-height); 26 | font-size: var(--ti-mobile-button-font-size-default); 27 | -webkit-text-size-adjust: 100%; 28 | border-width: 1px; 29 | border-style: solid; 30 | border-image: initial; 31 | border-radius: 2px; 32 | padding: 0 16px; 33 | transition: 34 | border 0.3s ease 0s, 35 | color 0.3s ease 0s, 36 | background 0.3s ease 0s; 37 | cursor: pointer; 38 | outline: 0; 39 | display: inline-block; 40 | user-select: none; 41 | -webkit-appearance: none; 42 | text-align: center; 43 | box-sizing: border-box; 44 | white-space: nowrap; 45 | overflow: hidden; 46 | text-overflow: ellipsis; 47 | vertical-align: middle; 48 | 49 | &::before { 50 | position: absolute; 51 | top: 50%; 52 | left: 50%; 53 | width: 100%; 54 | height: 100%; 55 | background-color: #000; 56 | border: inherit; 57 | border-color: #000; 58 | border-radius: inherit; 59 | transform: translate(-50%, -50%); 60 | opacity: 0; 61 | content: ' '; 62 | } 63 | 64 | &::-moz-focus-inner { 65 | border: 0; 66 | } 67 | 68 | &:active, 69 | &.is-active { 70 | &::before { 71 | opacity: 0.1; 72 | } 73 | } 74 | 75 | &.is-disabled, 76 | &.is-disabled:active, 77 | &.is-disabled.is-active { 78 | cursor: not-allowed; 79 | .button-color(var(--ti-mobile-button-text-color-disabled), 80 | var(--ti-mobile-button-bg-color-disabled), 81 | var(--ti-mobile-button-bg-color-disabled)); 82 | } 83 | 84 | .is-icon { 85 | fill: var(--ti-mobile-button-text-color-white); 86 | font-size: var(--ti-mobile-button-font-size-default); 87 | } 88 | 89 | &--default { 90 | .button-color(var(--ti-mobile-button-text-color-default), 91 | var(--ti-mobile-button-border-color), 92 | var(--ti-mobile-button-bg-color-default)); 93 | 94 | .is-icon { 95 | fill: var(--ti-mobile-button-text-color-default); 96 | } 97 | 98 | &.is-disabled .is-icon { 99 | fill: var(--ti-mobile-button-text-color-white); 100 | } 101 | } 102 | 103 | &&--default { 104 | &.is-loading svg { 105 | fill: #666; 106 | } 107 | } 108 | 109 | &--primary { 110 | .button-type(var(--ti-mobile-button-text-color-white), 111 | var(--ti-mobile-button-bg-color-primary)); 112 | 113 | &.is-plain { 114 | .button-plain(var(--ti-mobile-button-bg-color-primary)); 115 | } 116 | } 117 | 118 | &--success { 119 | .button-type(var(--ti-mobile-button-text-color-white), 120 | var(--ti-mobile-button-bg-color-success)); 121 | 122 | &.is-plain { 123 | .button-plain(var(--ti-mobile-button-bg-color-success)); 124 | } 125 | } 126 | 127 | &--warning { 128 | .button-type(var(--ti-mobile-button-text-color-white), 129 | var(--ti-mobile-button-bg-color-warning)); 130 | 131 | &.is-plain { 132 | .button-plain(var(--ti-mobile-button-bg-color-warning)); 133 | } 134 | } 135 | 136 | &--danger { 137 | .button-type(var(--ti-mobile-button-text-color-white), 138 | var(--ti-mobile-button-bg-color-danger)); 139 | 140 | &.is-plain { 141 | .button-plain(var(--ti-mobile-button-bg-color-danger)); 142 | } 143 | } 144 | 145 | &--info { 146 | .button-type(var(--ti-mobile-button-text-color-white), 147 | var(--ti-mobile-button-bg-color-info)); 148 | 149 | &.is-plain { 150 | .button-plain(var(--ti-mobile-button-bg-color-info)); 151 | } 152 | } 153 | 154 | &--text { 155 | .button-text(var(--ti-mobile-button-text-color), 156 | var(--ti-mobile-button-text-color-hover), 157 | var(--ti-mobile-button-text-color-active), 158 | var(--ti-mobile-button-text-color-disabled)); 159 | } 160 | 161 | &--secondary { 162 | .button-type(var(--ti-mobile-button-text-color-default), 163 | var(--ti-mobile-button-bg-color-secondary)); 164 | 165 | &.is-plain { 166 | .button-plain(var(--ti-mobile-button-bg-color-secondary)); 167 | } 168 | } 169 | 170 | &--default { 171 | .button-color(var(--ti-mobile-button-text-color-default), 172 | var(--ti-mobile-button-text-color-disabled), 173 | var(--ti-mobile-button-text-color-white)); 174 | 175 | &.is-disabled { 176 | .button-color(var(--ti-mobile-button-text-color-disabled), 177 | var(--ti-mobile-button-text-color-disabled), 178 | var(--ti-mobile-button-bg-color-white)); 179 | } 180 | } 181 | 182 | &--large { 183 | padding: 0 20px; 184 | } 185 | 186 | &--medium { 187 | padding: 0 16px; 188 | line-height: 36px; 189 | 190 | .button-size(var(--ti-mobile-button-height-medium), 191 | var(--ti-mobile-button-font-size-medium,)); 192 | } 193 | 194 | &--small { 195 | padding: 0 12px; 196 | line-height: 28px; 197 | 198 | .button-size(var(--ti-mobile-button-height-small), 199 | var(--ti-mobile-button-font-size-small)); 200 | } 201 | 202 | &--mini { 203 | padding: 0 8px; 204 | line-height: 22px; 205 | 206 | .button-size(var(--ti-mobile-button-height-mini), 207 | var(--ti-mobile-button-font-size-mini)); 208 | } 209 | 210 | &.is-loading { 211 | position: relative; 212 | pointer-events: none; 213 | 214 | svg { 215 | fill: var(--ti-mobile-button-text-color-white); 216 | font-size: var(--ti-mobile-button-font-size-default); 217 | } 218 | } 219 | 220 | &.is-round { 221 | border-radius: 999px; 222 | } 223 | } 224 | 225 | .@{css-prefix-iconfont}-loading { 226 | margin-right: 4px; 227 | font-size: 16px; 228 | line-height: 1; 229 | animation: rotating 2s linear infinite; 230 | } 231 | 232 | @keyframes rotating { 233 | 0% { 234 | transform: rotateZ(0deg); 235 | } 236 | 237 | 100% { 238 | transform: rotateZ(360deg); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/src/button/vars.less: -------------------------------------------------------------------------------- 1 | :root,:host { 2 | --ti-mobile-button-height: 42px; 3 | --ti-mobile-button-font-size-default: 16px; 4 | --ti-mobile-button-text-color-white: var(--ti-mobile-common-color-text-white, #fff); 5 | --ti-mobile-button-text-color-disabled: var(--ti-mobile-common-color-text-weaken-disabled, #dbdbdb); 6 | --ti-mobile-button-text-color-default: var(--ti-mobile-common-color-text-primary, #191919); 7 | --ti-mobile-button-border-color: var(--ti-mobile-common-color-text-weaken-disabled, #dbdbdb); 8 | --ti-mobile-button-bg-color-default: var(--ti-mobile-common-bg-color-white, #fff); 9 | --ti-mobile-button-bg-color-primary: var(--ti-mobile-common-color-error-figure-3, #1e8aff); 10 | --ti-mobile-button-bg-color-success: var(--ti-mobile-common-color-warning-figure-3, #1ebe40); 11 | --ti-mobile-button-bg-color-warning: var(--ti-mobile-common-color-error-figure-2, #fccd32); 12 | --ti-mobile-button-bg-color-danger: var(--ti-mobile-common-color-error-figure-1, #eb5454); 13 | --ti-mobile-button-bg-color-disabled: var(--ti-mobile-common-bg-color-disabled, #e8e8e8); 14 | --ti-mobile-button-bg-color-info: #333; 15 | --ti-mobile-button-height-medium: 36px; 16 | --ti-mobile-button-font-size-medium: 14px; 17 | --ti-mobile-button-height-small: 28px; 18 | --ti-mobile-button-font-size-small: 12px; 19 | --ti-mobile-button-height-mini: 22px; 20 | --ti-mobile-button-font-size-mini: 10px; 21 | --ti-mobile-button-text-color: var(--ti-mobile-common-color-link-highlight, #4a79fe); 22 | --ti-mobile-button-text-color-hover: #6e94fe; 23 | --ti-mobile-button-text-color-active: var(--ti-mobile-common-color-link-highlight, #4a79fe); 24 | } 25 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/src/custom.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @css-prefix: tiny-; 14 | @css-prefix-iconfont: tiny-icon; 15 | -------------------------------------------------------------------------------- /packages/components/theme-mobile/src/mixins/button.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | .button-color(@color, @border-color, @background-color) { 14 | color: @color; 15 | border-color: @border-color; 16 | background-color: @background-color; 17 | } 18 | 19 | .button-text(@color, @hover-color, @active-color, @disabled-color) { 20 | color: @color; 21 | border-color: transparent; 22 | background-color: transparent; 23 | 24 | &:hover { 25 | color: @hover-color; 26 | border-color: transparent; 27 | background-color: transparent; 28 | } 29 | 30 | &:focus, 31 | &:active, 32 | &.is-active { 33 | color: @active-color; 34 | border-color: transparent; 35 | background-color: transparent; 36 | 37 | &::before { 38 | opacity: 0; 39 | } 40 | } 41 | 42 | &.is-disabled, 43 | &.is-disabled:active, 44 | &.is-disabled:focus, 45 | &.is-disabled:hover { 46 | color: @disabled-color; 47 | border-color: transparent; 48 | background-color: transparent; 49 | } 50 | } 51 | 52 | .button-type(@color, @normal-color) { 53 | color: @color; 54 | border-color: @normal-color; 55 | background-color: @normal-color; 56 | 57 | .is-icon { 58 | fill: @color; 59 | } 60 | } 61 | 62 | .button-size(@height, @font-size) { 63 | height: @height; 64 | line-height: @height; 65 | font-size: @font-size; 66 | 67 | .is-icon, 68 | &.is-loading svg { 69 | font-size: @font-size; 70 | } 71 | } 72 | 73 | .button-plain(@color) { 74 | color: @color; 75 | border-color: @color; 76 | background-color: transparent; 77 | 78 | .is-icon, 79 | &.is-loading svg { 80 | fill: @color; 81 | } 82 | 83 | &.is-disabled { 84 | &, 85 | &:active, 86 | &.is-active { 87 | color: #ccc; 88 | border-color: #ddd; 89 | background-color: transparent; 90 | } 91 | 92 | .is-icon, 93 | &.is-loading svg { 94 | fill: #ccc; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /packages/components/theme-watch/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/theme-watch 2 | 3 | This is a project to provide Watch template styles for OpenTiny cross-end and cross-framework components. -------------------------------------------------------------------------------- /packages/components/theme-watch/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/theme-watch 2 | 3 | 这是一个为 OpenTiny 跨端、跨框架组件提供 Watch 模板样式的工程 4 | -------------------------------------------------------------------------------- /packages/components/theme-watch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/theme-watch", 3 | "version": "3.9.0", 4 | "description": "", 5 | "sideEffects": false, 6 | "dependencies": { 7 | "less": "~4.1.3" 8 | }, 9 | "type": "module", 10 | "exports": { 11 | "./*": "./src/*" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/components/theme-watch/src/base/basic-var.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | /*基础变量定义*/ 14 | :root,:host { 15 | /* 品牌色*/ 16 | --ti-watch-base-color-brand-1: #4a79fe; /* 蓝_品牌色*/ 17 | --ti-watch-base-color-brand-2: #3168f1; 18 | --ti-watch-base-color-brand-3: #6e94fe; 19 | --ti-watch-base-color-brand-4: #92affe; 20 | --ti-watch-base-color-brand-5: #b7c9ff; 21 | --ti-watch-base-color-brand-6: #95a2f8; 22 | --ti-watch-base-color-brand-6: #ffffff; 23 | 24 | /* 1.2背景色*/ 25 | --ti-watch-base-color-bg-1: #f5f5f5; /* 深灰_背景色*/ 26 | --ti-watch-base-color-bg-2: #fafafa; /* 浅灰_背景色*/ 27 | --ti-watch-base-color-bg-3: #ffffff; /* 白色_背景色*/ 28 | 29 | /* 文本、线、面等常用灰色可用颜色参考:*/ 30 | --ti-watch-base-color-common-1: #000000; 31 | --ti-watch-base-color-common-2: #191919; 32 | --ti-watch-base-color-common-3: #262626; 33 | --ti-watch-base-color-common-4: #333333; 34 | --ti-watch-base-color-common-5: #595959; 35 | --ti-watch-base-color-common-6: #999999; 36 | --ti-watch-base-color-common-7: #c2c2c2; 37 | --ti-watch-base-color-common-8: #dbdbdb; 38 | --ti-watch-base-color-common-9: #ededed; 39 | --ti-watch-base-color-common-10: #f5f5f5; 40 | --ti-watch-base-color-common-11: #fafafa; 41 | --ti-watch-base-color-common-12: #ffffff; 42 | --ti-watch-base-color-common-13: #627199; 43 | 44 | /* 功能色*/ 45 | --ti-watch-common-color-warning-figure-1: #eb4096; /* 致命警告图形色*/ 46 | --ti-watch-common-color-warning-text-1: #e61c81; /* 致命警告文字色*/ 47 | --ti-watch-common-color-warning-figure-2: #ff9c32; /* 一般告警/中性1图形色*/ 48 | --ti-watch-common-color-warning-text-2: #ff8800; /* 一般告警/中性1文字色*/ 49 | --ti-watch-common-color-warning-figure-3: #1ebe40; /* 一般告警/中性1图形色*/ 50 | --ti-watch-common-color-warning-text-3: #00b336; /* 一般告警/中性1文字色*/ 51 | 52 | --ti-watch-common-color-error-figure-1: #eb5454; /* 异常/下降/告警/驳回/退订/失败图形色*/ 53 | --ti-watch-common-color-error-text-1: #e62222; /* 异常/下降/告警/驳回/退订/失败文字色*/ 54 | --ti-watch-common-color-error-figure-2: #fccd32; /* 异常/下降/告警/驳回/退订/失败图形色*/ 55 | --ti-watch-common-color-error-text-2: #fcbe1e; /* 异常/下降/告警/驳回/退订/失败文字色*/ 56 | --ti-watch-common-color-error-figure-3: #1e8aff; /* 异常/下降/告警/驳回/退订/失败图形色*/ 57 | --ti-watch-common-color-error-text-3: #1476ff; /* 异常/下降/告警/驳回/退订/失败文字色*/ 58 | --ti-watch-common-color-error-figure-4: #c2c2c2; /* 异常/下降/告警/驳回/退订/失败图形色*/ 59 | --ti-watch-common-color-error-text-4: #999999; /* 异常/下降/告警/驳回/退订/失败文字色*/ 60 | 61 | /* 图表推荐配色*/ 62 | --ti-watch-common-color-chart-1: #1476ff; 63 | --ti-watch-common-color-chart-2: #10c7c1; 64 | --ti-watch-common-color-chart-3: #b878f0; 65 | --ti-watch-common-color-chart-4: #d6a981; 66 | --ti-watch-common-color-chart-5: #c1cc7a; 67 | 68 | /* 文本色*/ 69 | --ti-watch-common-color-text-primary: var( 70 | --ti-watch-base-color-common-2 71 | ); /* 一级文本色-重要信息/标题颜色/输入类文本颜色*/ 72 | --ti-watch-common-color-text-secondary: var(--ti-watch-base-color-common-5); /* 二级文本色-次要信息*/ 73 | --ti-watch-common-color-text-weaken-dark: var(--ti-watch-base-color-common-6); /* 三级文本色-弱化信息/说明文字*/ 74 | --ti-watch-common-color-text-weaken-ligtht: var(--ti-watch-base-color-common-7); /* 文本_弱化色_浅色/提示*/ 75 | --ti-watch-common-color-text-weaken-disabled: var(--ti-watch-base-color-common-8); /* 文本_弱化色_浅色/禁用*/ 76 | --ti-watch-common-color-text-white: var(--ti-watch-base-color-common-12); /* 文本_白色*/ 77 | --ti-watch-common-color-text-placeholder-primary: #dbe5fc; /* placeholder_主要*/ 78 | --ti-watch-common-color-text-placeholder-gray: #acacac; /* placeholder_灰*/ 79 | --ti-watch-common-color-text-high-light: var(--ti-watch-base-color-brand-1); /* 文本_高亮*/ 80 | 81 | /* 文本链接色*/ 82 | --ti-watch-common-color-link-dark: var(--ti-watch-base-color-common-2); /* 文本_链接__深色*/ 83 | --ti-watch-common-color-link-highlight: var(--ti-watch-base-color-brand-1); /* 文本_链接_高亮色*/ 84 | --ti-watch-common-color-link-gray: var(--ti-watch-base-color-common-13); /* 文本_链接_灰色加强*/ 85 | --ti-watch-common-color-link-white: var(--ti-watch-base-color-common-12); /* 文本_链接_白色*/ 86 | 87 | /* 线颜色*/ 88 | --ti-watch-common-color-line-hightlight: var(--ti-watch-base-color-brand-1); /* 高亮_描边色*/ 89 | --ti-watch-common-color-line-dark: var(--ti-watch-base-color-common-9); /* 深_描边色*/ 90 | --ti-watch-common-color-line-light: var(--ti-watch-base-color-common-10); /* 浅_描边色*/ 91 | --ti-watch-common-color-line-white: var(--ti-watch-base-color-common-12); /* 白色*/ 92 | 93 | /* 蒙层色*/ 94 | --ti-watch-common-color-mask-light: rgba(0, 0, 0, 0.5); /* 蒙层_相对浅*/ 95 | --ti-watch-common-color-mask-dark: rgba(0, 0, 0, 0.75); /* 蒙层_相对深*/ 96 | 97 | /* 背景色*/ 98 | --ti-watch-common-bg-color-white: var(--ti-watch-base-color-bg-3); /* 背景_白*/ 99 | --ti-watch-common-bg-color-main: var(--ti-watch-base-color-brand-1); /* 背景_蓝*/ 100 | --ti-watch-common-bg-color-light: var(--ti-watch-base-color-bg-2); /* 背景_浅灰*/ 101 | --ti-watch-common-bg-color-blue-1: var(--ti-watch-base-color-brand-2); /* 背景_蓝*/ 102 | --ti-watch-common-bg-color-disabled: #e8e8e8; /* 背景_禁用*/ 103 | --ti-watch-common-bg-color-dark-gray: var(--ti-watch-base-color-bg-1); /* 背景_深灰*/ 104 | --ti-watch-common-bg-color-weaken: var(--ti-watch-base-color-common-8); /* 背景_弱化色*/ 105 | --ti-watch-common-status-bg-color-success: #5bd475; /* 背景_成功/通过*/ 106 | --ti-watch-common-status-bg-color-error: #eb5454; /* 背景_失败/驳回*/ 107 | --ti-watch-common-status-bg-color-normal: #fff; /* 背景_默认*/ 108 | --ti-watch-common-status-bg-color-handing: var(--ti-watch-base-color-brand-1); /* 背景_进行中*/ 109 | } 110 | -------------------------------------------------------------------------------- /packages/components/theme-watch/src/base/index.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @import './reset.less'; 14 | @import './basic-var.less'; 15 | @import './vars.less'; 16 | -------------------------------------------------------------------------------- /packages/components/theme-watch/src/base/reset.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @import '../custom.less'; 14 | 15 | [class*=~'@{css-prefix}'] { 16 | -webkit-box-sizing: border-box; 17 | box-sizing: border-box; 18 | 19 | *:after, 20 | *:before { 21 | -webkit-box-sizing: border-box; 22 | box-sizing: border-box; 23 | } 24 | 25 | a { 26 | cursor: pointer; 27 | background-image: none; 28 | text-decoration: none; 29 | outline: none; 30 | 31 | &:focus, 32 | &:active, 33 | &:hover { 34 | outline: none; 35 | text-decoration: none; 36 | } 37 | } 38 | 39 | dl, 40 | dt, 41 | dd, 42 | ul, 43 | ol, 44 | li, 45 | th, 46 | td { 47 | margin: 0; 48 | padding: 0; 49 | } 50 | 51 | ol, 52 | ul { 53 | list-style: none; 54 | } 55 | 56 | audio, 57 | canvas, 58 | video { 59 | display: inline-block; 60 | } 61 | 62 | audio:not([controls]) { 63 | display: none; 64 | height: 0; 65 | } 66 | 67 | mark { 68 | background: #ff0; 69 | color: #000; 70 | } 71 | 72 | pre { 73 | white-space: pre-wrap; 74 | } 75 | 76 | sub, 77 | sup { 78 | font-size: 75%; 79 | line-height: 0; 80 | position: relative; 81 | vertical-align: baseline; 82 | } 83 | 84 | sup { 85 | top: -0.5em; 86 | } 87 | 88 | sub { 89 | bottom: -0.25em; 90 | } 91 | 92 | fieldset { 93 | border: 1px solid #c0c0c0; 94 | margin: 0 2px; 95 | padding: 0.35em 0.625em 0.75em; 96 | } 97 | 98 | legend { 99 | border: 0; 100 | padding: 0; 101 | } 102 | 103 | // 清除IE 104 | input::-ms-clear, 105 | input::-ms-reveal { 106 | display: none; 107 | } 108 | 109 | button::-moz-focus-inner, 110 | input::-moz-focus-inner { 111 | border: 0; 112 | padding: 0; 113 | } 114 | 115 | textarea { 116 | overflow: auto; 117 | vertical-align: top; 118 | } 119 | 120 | table { 121 | border-collapse: collapse; 122 | border-spacing: 0; 123 | } 124 | 125 | .@{css-prefix}hide { 126 | display: none; 127 | } 128 | 129 | .popper__arrow { 130 | &, 131 | &:after { 132 | position: absolute; 133 | display: block; 134 | width: 0; 135 | height: 0; 136 | border-color: transparent; 137 | border-style: solid; 138 | } 139 | } 140 | 141 | @media (min-width: 768px) { 142 | ::-webkit-scrollbar { 143 | width: 8px; 144 | height: 8px; 145 | } 146 | 147 | ::-webkit-scrollbar-track-piece { 148 | background: #fafafa; 149 | } 150 | 151 | ::-webkit-scrollbar-thumb { 152 | background: #5c6173; 153 | border-radius: 6px; 154 | } 155 | 156 | ::-webkit-scrollbar-thumb:hover { 157 | background: #999999; 158 | } 159 | 160 | ::-webkit-scrollbar-thumb:active { 161 | background: #999999; 162 | } 163 | 164 | .@{css-prefix}scrollbar::-webkit-scrollbar { 165 | width: 8px; 166 | height: 8px; 167 | } 168 | 169 | .@{css-prefix}scrollbar::-webkit-scrollbar-track-piece { 170 | background: transparent; 171 | border: 0; 172 | } 173 | 174 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb { 175 | background: #5c6173; 176 | border-radius: 4px; 177 | } 178 | 179 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb:hover { 180 | background: #999999; 181 | } 182 | 183 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb:active { 184 | background: #999999; 185 | } 186 | 187 | .@{css-prefix}min-scrollbar::-webkit-scrollbar { 188 | width: 4px; 189 | height: 4px; 190 | } 191 | 192 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-track-piece { 193 | background: transparent; 194 | border: 0; 195 | } 196 | 197 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb { 198 | background: #bfbfbf; 199 | border-radius: 2px; 200 | } 201 | 202 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb:hover { 203 | background: #999999; 204 | } 205 | 206 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb:active { 207 | background: #999999; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /packages/components/theme-watch/src/base/vars.less: -------------------------------------------------------------------------------- 1 | :root { 2 | --ti-watch-base-color-primary-normal: #1890ff; 3 | --ti-watch-base-color-primary-disabled: #bfbfbf; 4 | --ti-watch-base-color-primary-hover: #40a9ff; 5 | --ti-watch-base-color-primary-active: #096dd9; 6 | --ti-watch-base-color-success-normal: #52c41a; 7 | --ti-watch-base-color-success-disabled: #a6c3b9; 8 | --ti-watch-base-color-success-hover: #73d13d; 9 | --ti-watch-base-color-success-active: #389e0d; 10 | --ti-watch-base-color-warning-normal: #faad14; 11 | --ti-watch-base-color-warning-disabled: #d3c6a2; 12 | --ti-watch-base-color-warning-hover: #ffc53d; 13 | --ti-watch-base-color-warning-active: #ffc53d; 14 | --ti-watch-base-color-danger-normal: #f5222d; 15 | --ti-watch-base-color-danger-disabled: #d8bab5; 16 | --ti-watch-base-color-danger-hover: #ff4d4f; 17 | --ti-watch-base-color-danger-active: #cf1322; 18 | --ti-watch-base-color-info-normal: #333333; 19 | --ti-watch-base-color-info-disabled: #bfbfbf; 20 | --ti-watch-base-color-info-hover: #54657e; 21 | --ti-watch-base-color-info-active: #54657e; 22 | --ti-watch-base-color-light: #fff; 23 | --ti-watch-base-color-dark: #000; 24 | --ti-watch-base-color-border: #d9d9d9; 25 | --ti-watch-base-color-secondary: #666; 26 | --ti-watch-base-color-placeholder: #999; 27 | --ti-watch-base-color-hover-background: #e6f7ff; 28 | --ti-watch-base-color-selected-background: #f5f5f5; 29 | --ti-watch-base-color-navigation-background: #2e3243; 30 | --ti-watch-base-radius-large: 3px; 31 | --ti-watch-base-radius-medium: 2px; 32 | --ti-watch-base-radius-small: 1px; 33 | --ti-watch-base-font-family-normal: Helvetica, Arial, 'microsoft yahei'; 34 | --ti-watch-base-font-size: 12px; 35 | --ti-watch-base-font-size-normal: 1em; 36 | --ti-watch-base-font-size-large: 1.125em; 37 | --ti-watch-base-font-weight-bold: 700; 38 | --ti-watch-base-size-width-large: 130px; 39 | --ti-watch-base-size-width-medium: 120px; 40 | --ti-watch-base-size-width-normal: 80px; 41 | --ti-watch-base-size-width-small: 36px; 42 | --ti-watch-base-size-width-minor: 30px; 43 | --ti-watch-base-size-width-mini: 24px; 44 | --ti-watch-base-size-height-large: 48px; 45 | --ti-watch-base-size-height-medium: 42px; 46 | --ti-watch-base-size-height-small: 36px; 47 | --ti-watch-base-size-height-minor: 30px; 48 | --ti-watch-base-size-height-mini: 24px; 49 | } 50 | -------------------------------------------------------------------------------- /packages/components/theme-watch/src/button/index.less: -------------------------------------------------------------------------------- 1 | @import '../mixins/button.less'; 2 | @import '../custom.less'; 3 | @import './vars.less'; 4 | 5 | @button-prefix-cls: ~'@{css-prefix}watch-button'; 6 | @svg-prefix-cls: ~'@{css-prefix}svg'; 7 | 8 | .@{button-prefix-cls} { 9 | position: relative; 10 | width: var(--ti-watch-button-height); 11 | height: var(--ti-watch-button-height); 12 | line-height: var(--ti-watch-button-height); 13 | font-size: var(--ti-watch-button-font-size-default); 14 | border-width: 1px; 15 | border-style: solid; 16 | border-image: initial; 17 | border-radius: 100%; 18 | padding: 5px; 19 | transition: 20 | border 0.3s ease 0s, 21 | color 0.3s ease 0s, 22 | background 0.3s ease 0s; 23 | cursor: pointer; 24 | outline: 0; 25 | display: inline-block; 26 | user-select: none; 27 | text-align: center; 28 | box-sizing: border-box; 29 | white-space: nowrap; 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | display: flex; 33 | align-items: center; 34 | justify-content: center; 35 | 36 | 37 | &::-moz-focus-inner { 38 | border: 0; 39 | } 40 | 41 | &:active, 42 | &.is-active { 43 | &::before { 44 | opacity: 0.1; 45 | } 46 | } 47 | 48 | &.is-disabled, 49 | &.is-disabled:active, 50 | &.is-disabled.is-active { 51 | cursor: not-allowed; 52 | .button-color(var(--ti-watch-button-text-color-disabled), 53 | var(--ti-watch-button-bg-color-disabled), 54 | var(--ti-watch-button-bg-color-disabled)); 55 | } 56 | 57 | .is-icon { 58 | fill: var(--ti-watch-button-text-color-white); 59 | font-size: var(--ti-watch-button-font-size-default); 60 | } 61 | 62 | &--default { 63 | .button-color(var(--ti-watch-button-text-color-default), 64 | var(--ti-watch-button-border-color), 65 | var(--ti-watch-button-bg-color-default)); 66 | 67 | .is-icon { 68 | fill: var(--ti-watch-button-text-color-default); 69 | } 70 | 71 | &.is-disabled .is-icon { 72 | fill: var(--ti-watch-button-text-color-white); 73 | } 74 | } 75 | 76 | &&--default { 77 | &.is-loading svg { 78 | fill: #666; 79 | } 80 | } 81 | 82 | &--primary { 83 | .button-type(var(--ti-watch-button-text-color-white), 84 | var(--ti-watch-button-bg-color-primary)); 85 | 86 | &.is-plain { 87 | .button-plain(var(--ti-watch-button-bg-color-primary)); 88 | } 89 | } 90 | 91 | &--success { 92 | .button-type(var(--ti-watch-button-text-color-white), 93 | var(--ti-watch-button-bg-color-success)); 94 | 95 | &.is-plain { 96 | .button-plain(var(--ti-watch-button-bg-color-success)); 97 | } 98 | } 99 | 100 | &--warning { 101 | .button-type(var(--ti-watch-button-text-color-white), 102 | var(--ti-watch-button-bg-color-warning)); 103 | 104 | &.is-plain { 105 | .button-plain(var(--ti-watch-button-bg-color-warning)); 106 | } 107 | } 108 | 109 | &--danger { 110 | .button-type(var(--ti-watch-button-text-color-white), 111 | var(--ti-watch-button-bg-color-danger)); 112 | 113 | &.is-plain { 114 | .button-plain(var(--ti-watch-button-bg-color-danger)); 115 | } 116 | } 117 | 118 | &--info { 119 | .button-type(var(--ti-watch-button-text-color-white), 120 | var(--ti-watch-button-bg-color-info)); 121 | 122 | &.is-plain { 123 | .button-plain(var(--ti-watch-button-bg-color-info)); 124 | } 125 | } 126 | 127 | &--text { 128 | .button-text(var(--ti-watch-button-text-color), 129 | var(--ti-watch-button-text-color-hover), 130 | var(--ti-watch-button-text-color-active), 131 | var(--ti-watch-button-text-color-disabled)); 132 | } 133 | 134 | &--secondary { 135 | .button-type(var(--ti-watch-button-text-color-default), 136 | var(--ti-watch-button-bg-color-secondary)); 137 | 138 | &.is-plain { 139 | .button-plain(var(--ti-watch-button-bg-color-secondary)); 140 | } 141 | } 142 | 143 | &--default { 144 | .button-color(var(--ti-watch-button-text-color-default), 145 | var(--ti-watch-button-text-color-disabled), 146 | var(--ti-watch-button-text-color-white)); 147 | 148 | &.is-disabled { 149 | .button-color(var(--ti-watch-button-text-color-disabled), 150 | var(--ti-watch-button-text-color-disabled), 151 | var(--ti-watch-button-bg-color-white)); 152 | } 153 | } 154 | 155 | &--large { 156 | padding: 0 20px; 157 | } 158 | 159 | &--medium { 160 | padding: 0 16px; 161 | line-height: 36px; 162 | 163 | .button-size(var(--ti-watch-button-height-medium), 164 | var(--ti-watch-button-font-size-medium,)); 165 | } 166 | 167 | &--small { 168 | padding: 0 12px; 169 | line-height: 28px; 170 | 171 | .button-size(var(--ti-watch-button-height-small), 172 | var(--ti-watch-button-font-size-small)); 173 | } 174 | 175 | &--mini { 176 | padding: 0 8px; 177 | line-height: 22px; 178 | 179 | .button-size(var(--ti-watch-button-height-mini), 180 | var(--ti-watch-button-font-size-mini)); 181 | } 182 | 183 | &.is-loading { 184 | position: relative; 185 | pointer-events: none; 186 | 187 | svg { 188 | fill: var(--ti-watch-button-text-color-white); 189 | font-size: var(--ti-watch-button-font-size-default); 190 | } 191 | } 192 | 193 | &.is-round { 194 | border-radius:100%; 195 | } 196 | } 197 | 198 | .@{css-prefix-iconfont}-loading { 199 | margin-right: 4px; 200 | font-size: 16px; 201 | line-height: 1; 202 | animation: rotating 2s linear infinite; 203 | } 204 | 205 | @keyframes rotating { 206 | 0% { 207 | transform: rotateZ(0deg); 208 | } 209 | 210 | 100% { 211 | transform: rotateZ(360deg); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /packages/components/theme-watch/src/button/vars.less: -------------------------------------------------------------------------------- 1 | :root,:host { 2 | --ti-watch-button-height: 36px; 3 | --ti-watch-button-font-size-default: 12px; 4 | --ti-watch-button-text-color-white: var(--ti-watch-common-color-text-white, #fff); 5 | --ti-watch-button-text-color-disabled: var(--ti-watch-common-color-text-weaken-disabled, #dbdbdb); 6 | --ti-watch-button-text-color-default: var(--ti-watch-common-color-text-primary, #191919); 7 | --ti-watch-button-border-color: var(--ti-watch-common-color-text-weaken-disabled, #dbdbdb); 8 | --ti-watch-button-bg-color-default: var(--ti-watch-common-bg-color-white, #fff); 9 | --ti-watch-button-bg-color-primary: var(--ti-watch-common-color-error-figure-3, #1e8aff); 10 | --ti-watch-button-bg-color-success: var(--ti-watch-common-color-warning-figure-3, #1ebe40); 11 | --ti-watch-button-bg-color-warning: var(--ti-watch-common-color-error-figure-2, #fccd32); 12 | --ti-watch-button-bg-color-danger: var(--ti-watch-common-color-error-figure-1, #eb5454); 13 | --ti-watch-button-bg-color-disabled: var(--ti-watch-common-bg-color-disabled, #e8e8e8); 14 | --ti-watch-button-bg-color-info: #333; 15 | --ti-watch-button-height-medium: 36px; 16 | --ti-watch-button-font-size-medium: 14px; 17 | --ti-watch-button-height-small: 28px; 18 | --ti-watch-button-font-size-small: 12px; 19 | --ti-watch-button-height-mini: 22px; 20 | --ti-watch-button-font-size-mini: 10px; 21 | --ti-watch-button-text-color: var(--ti-watch-common-color-link-highlight, #4a79fe); 22 | --ti-watch-button-text-color-hover: #6e94fe; 23 | --ti-watch-button-text-color-active: var(--ti-watch-common-color-link-highlight, #4a79fe); 24 | } 25 | -------------------------------------------------------------------------------- /packages/components/theme-watch/src/custom.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @css-prefix: tiny-; 14 | @css-prefix-iconfont: tiny-icon; 15 | -------------------------------------------------------------------------------- /packages/components/theme-watch/src/mixins/button.less: -------------------------------------------------------------------------------- 1 | .button-color(@color, @border-color, @background-color) { 2 | color: @color; 3 | border-color: @border-color; 4 | background-color: @background-color; 5 | } 6 | 7 | .button-text(@color, @hover-color, @active-color, @disabled-color) { 8 | color: @color; 9 | border-color: transparent; 10 | background-color: transparent; 11 | 12 | &:hover { 13 | color: @hover-color; 14 | border-color: transparent; 15 | background-color: transparent; 16 | } 17 | 18 | &:focus, 19 | &:active, 20 | &.is-active { 21 | color: @active-color; 22 | border-color: transparent; 23 | background-color: transparent; 24 | 25 | &::before { 26 | opacity: 0; 27 | } 28 | } 29 | 30 | &.is-disabled, 31 | &.is-disabled:active, 32 | &.is-disabled:focus, 33 | &.is-disabled:hover { 34 | color: @disabled-color; 35 | border-color: transparent; 36 | background-color: transparent; 37 | } 38 | } 39 | 40 | .button-type(@color, @normal-color) { 41 | color: @color; 42 | border-color: @normal-color; 43 | background-color: @normal-color; 44 | 45 | .is-icon { 46 | fill: @color; 47 | } 48 | } 49 | 50 | .button-size(@height, @font-size) { 51 | height: @height; 52 | line-height: @height; 53 | font-size: @font-size; 54 | 55 | .is-icon, 56 | &.is-loading svg { 57 | font-size: @font-size; 58 | } 59 | } 60 | 61 | .button-plain(@color) { 62 | color: @color; 63 | border-color: @color; 64 | background-color: transparent; 65 | 66 | .is-icon, 67 | &.is-loading svg { 68 | fill: @color; 69 | } 70 | 71 | &.is-disabled { 72 | &, 73 | &:active, 74 | &.is-active { 75 | color: #ccc; 76 | border-color: #ddd; 77 | background-color: transparent; 78 | } 79 | 80 | .is-icon, 81 | &.is-loading svg { 82 | fill: #ccc; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/components/theme/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/theme 2 | 3 | This is a project to provide PC template styles for OpenTiny cross-end and cross-framework components. -------------------------------------------------------------------------------- /packages/components/theme/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/theme 2 | 3 | 这是一个为 OpenTiny 跨端、跨框架组件提供 PC 模板样式的工程 4 | -------------------------------------------------------------------------------- /packages/components/theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/theme", 3 | "version": "3.9.0", 4 | "description": "", 5 | "sideEffects": false, 6 | "dependencies": { 7 | "less": "~4.1.3" 8 | }, 9 | "type": "module", 10 | "exports": { 11 | "./*": "./src/*" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/components/theme/src/base/index.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @import './reset.less'; 14 | @import './basic-var.less'; 15 | -------------------------------------------------------------------------------- /packages/components/theme/src/base/reset.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @import '../custom.less'; 14 | @import './basic-var.less'; 15 | 16 | // 为了防止这些图标失去默认值,需要全局设置默认值 17 | 18 | .tiny-icon-success { 19 | fill: #5cb300; 20 | } 21 | 22 | .tiny-icon-error { 23 | fill: #f23030; 24 | } 25 | 26 | .tiny-icon-warning-triangle { 27 | fill: #ff8800; 28 | } 29 | 30 | .tiny-icon-prompt { 31 | fill: #1476ff; 32 | } 33 | 34 | .tiny-icon-text-type { 35 | fill: #9185f0; 36 | } 37 | 38 | 39 | [class*=~'@{css-prefix}'] { 40 | -webkit-box-sizing: border-box; 41 | box-sizing: border-box; 42 | 43 | *:after, 44 | *:before { 45 | -webkit-box-sizing: border-box; 46 | box-sizing: border-box; 47 | } 48 | 49 | a { 50 | cursor: pointer; 51 | background-image: none; 52 | text-decoration: none; 53 | outline: none; 54 | 55 | &:focus, 56 | &:active, 57 | &:hover { 58 | outline: none; 59 | text-decoration: none; 60 | } 61 | } 62 | 63 | dl, 64 | dt, 65 | dd, 66 | ul, 67 | ol, 68 | li, 69 | th, 70 | td { 71 | margin: 0; 72 | padding: 0; 73 | } 74 | 75 | ol, 76 | ul { 77 | list-style: none; 78 | } 79 | 80 | audio, 81 | canvas, 82 | video { 83 | display: inline-block; 84 | } 85 | 86 | audio:not([controls]) { 87 | display: none; 88 | height: 0; 89 | } 90 | 91 | mark { 92 | background: #ff0; 93 | color: #000; 94 | } 95 | 96 | pre { 97 | white-space: pre-wrap; 98 | } 99 | 100 | sub, 101 | sup { 102 | font-size: 75%; 103 | line-height: 0; 104 | position: relative; 105 | vertical-align: baseline; 106 | } 107 | 108 | sup { 109 | top: -0.5em; 110 | } 111 | 112 | sub { 113 | bottom: -0.25em; 114 | } 115 | 116 | fieldset { 117 | border: 1px solid #c0c0c0; 118 | margin: 0 2px; 119 | padding: 0.35em 0.625em 0.75em; 120 | } 121 | 122 | legend { 123 | border: 0; 124 | padding: 0; 125 | } 126 | 127 | // 清除IE 128 | input::-ms-clear, 129 | input::-ms-reveal { 130 | display: none; 131 | } 132 | 133 | button::-moz-focus-inner, 134 | input::-moz-focus-inner { 135 | border: 0; 136 | padding: 0; 137 | } 138 | 139 | textarea { 140 | overflow: auto; 141 | vertical-align: top; 142 | } 143 | 144 | table { 145 | border-collapse: collapse; 146 | border-spacing: 0; 147 | } 148 | 149 | .@{css-prefix}hide { 150 | display: none; 151 | } 152 | 153 | .popper__arrow { 154 | 155 | &, 156 | &:after { 157 | position: absolute; 158 | display: block; 159 | width: 0; 160 | height: 0; 161 | border-color: transparent; 162 | border-style: solid; 163 | } 164 | } 165 | 166 | @media (min-width: 768px) { 167 | ::-webkit-scrollbar { 168 | width: var(--ti-common-scrollbar-width); 169 | height: var(--ti-common-scrollbar-height); 170 | } 171 | 172 | ::-webkit-scrollbar-track-piece { 173 | background: var(--ti-common-scrollbar-track-piece-bg-color); 174 | } 175 | 176 | ::-webkit-scrollbar-thumb { 177 | background: var(--ti-common-scrollbar-thumb-bg-color); 178 | border-radius: var(--ti-common-scrollbar-thumb-border-radius); 179 | } 180 | 181 | ::-webkit-scrollbar-thumb:hover { 182 | background: var(--ti-common-scrollbar-thumb-hover-bg-color); 183 | } 184 | 185 | ::-webkit-scrollbar-thumb:active { 186 | background: var(--ti-common-scrollbar-thumb-active-bg-color); 187 | } 188 | 189 | .@{css-prefix}scrollbar::-webkit-scrollbar { 190 | width: 8px; 191 | height: 8px; 192 | } 193 | 194 | .@{css-prefix}scrollbar::-webkit-scrollbar-track-piece { 195 | background: transparent; 196 | border: 0; 197 | } 198 | 199 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb { 200 | background: #bfbfbf; 201 | border-radius: 4px; 202 | } 203 | 204 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb:hover { 205 | background: #999999; 206 | } 207 | 208 | .@{css-prefix}scrollbar::-webkit-scrollbar-thumb:active { 209 | background: #999999; 210 | } 211 | 212 | .@{css-prefix}min-scrollbar::-webkit-scrollbar { 213 | width: 4px; 214 | height: 4px; 215 | } 216 | 217 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-track-piece { 218 | background: transparent; 219 | border: 0; 220 | } 221 | 222 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb { 223 | background: #bfbfbf; 224 | border-radius: 2px; 225 | } 226 | 227 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb:hover { 228 | background: #999999; 229 | } 230 | 231 | .@{css-prefix}min-scrollbar::-webkit-scrollbar-thumb:active { 232 | background: #999999; 233 | } 234 | } 235 | } -------------------------------------------------------------------------------- /packages/components/theme/src/countdown/index.less: -------------------------------------------------------------------------------- 1 | @import '../custom.less'; 2 | @import './vars.less'; 3 | 4 | @countdown-prefix-cls: ~'@{css-prefix}countdown__container'; 5 | 6 | .@{countdown-prefix-cls} { 7 | color: var(--ti-countdown-font-color); 8 | } 9 | -------------------------------------------------------------------------------- /packages/components/theme/src/countdown/vars.less: -------------------------------------------------------------------------------- 1 | :root,:host { 2 | --ti-countdown-font-color: #333; 3 | } 4 | -------------------------------------------------------------------------------- /packages/components/theme/src/custom.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | @css-prefix: tiny-; 14 | @css-prefix-iconfont: tiny-icon; 15 | 16 | // 组件前缀 17 | @button-prefix-cls: ~'@{css-prefix}button'; 18 | @input-prefix-cls: ~'@{css-prefix}input'; 19 | @picker-panel-prefix-cls: ~'@{css-prefix}picker-panel'; 20 | @scrollbar-prefix-cls: ~'@{css-prefix}scrollbar'; 21 | @svg-prefix-cls: ~'@{css-prefix}svg'; 22 | -------------------------------------------------------------------------------- /packages/components/theme/src/mixins/button.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 - present TinyVue Authors. 3 | * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. 4 | * 5 | * Use of this source code is governed by an MIT-style license. 6 | * 7 | * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, 8 | * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR 9 | * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. 10 | * 11 | */ 12 | 13 | .button-size(@font-size, @height, @padding, @minimal-width) { 14 | height: @height; 15 | line-height: calc(@height - 2px); 16 | font-size: @font-size; 17 | padding: 0 @padding; 18 | min-width: @minimal-width; 19 | 20 | &.is-round { 21 | border-radius: calc(@height / 2); 22 | } 23 | 24 | &.is-circle { 25 | border-radius: 50%; 26 | } 27 | } 28 | 29 | .button-circle-size(@size, @padding) { 30 | min-width: @size; 31 | min-height: @size; 32 | height: auto; 33 | line-height: 1; 34 | padding: @padding; 35 | } 36 | 37 | .button-color(@color, @border-color, @background-color) { 38 | color: @color; 39 | fill: @color; 40 | border-color: @border-color; 41 | background-color: @background-color; 42 | } 43 | 44 | .button-type(@color, @normal-bg-color, @normal-border-color,@hover-bg-color, @hover-border-color, @active-color, @disabled-color, @disabled-bg-color, @plain-text-color, @plain-hover-text-color, @plain-bg-color, @plain-hover-bg-color,@plain-border-color, @plain-hover-border-color, @plain-dis-bg-color, @disabled-border-color: @disabled-bg-color) { 45 | color: @color; 46 | fill: @color; 47 | border-color: @normal-border-color; 48 | background-color: @normal-bg-color; 49 | 50 | &:hover { 51 | color: @color; 52 | fill: @color; 53 | border-color: @hover-border-color; 54 | background-color: @hover-bg-color; 55 | } 56 | 57 | &:focus, 58 | &:active, 59 | &.is-active { 60 | color: @color; 61 | fill: @color; 62 | border-color: @active-color; 63 | background-color: @active-color; 64 | outline: 0; 65 | } 66 | 67 | &.is-disabled, 68 | &.is-disabled:active, 69 | &.is-disabled:focus, 70 | &.is-disabled:hover { 71 | color: @disabled-color; 72 | fill: @disabled-color; 73 | border-color: @disabled-border-color; 74 | background-color: @disabled-bg-color; 75 | } 76 | 77 | &.is-plain { 78 | color: @plain-text-color; 79 | fill: @plain-text-color; 80 | border-color: @plain-border-color; 81 | background-color: @plain-bg-color; 82 | 83 | &:hover { 84 | color: @plain-hover-text-color; 85 | fill: @plain-hover-text-color; 86 | border-color: @plain-hover-border-color; 87 | background-color: @plain-hover-bg-color; 88 | } 89 | 90 | &:focus, 91 | &:active, 92 | &.is-active { 93 | color: @color; 94 | fill: @color; 95 | border-color: @active-color; 96 | background-color: @active-color; 97 | outline: 0; 98 | } 99 | 100 | &.is-disabled, 101 | &.is-disabled:active, 102 | &.is-disabled:focus, 103 | &.is-disabled:hover { 104 | color: var(--ti-button-plain-disabled-text-color); 105 | fill: @disabled-bg-color; 106 | border-color: @disabled-bg-color; 107 | background-color: @plain-dis-bg-color; 108 | } 109 | } 110 | } 111 | 112 | .button-text(@color, @hover-bg-color, @active-color, @disabled-color, @hover-weight) { 113 | color: @color; 114 | font-size: var(--ti-common-font-size-1); 115 | border-color: transparent; 116 | background-color: transparent; 117 | 118 | &:hover { 119 | color: @hover-bg-color; 120 | font-weight: @hover-weight; 121 | border-color: transparent; 122 | background-color: transparent; 123 | } 124 | 125 | &:focus, 126 | &:active, 127 | &.is-active { 128 | color: @active-color; 129 | border-color: transparent; 130 | background-color: transparent; 131 | } 132 | 133 | &.is-disabled, 134 | &.is-disabled:active, 135 | &.is-disabled:focus, 136 | &.is-disabled:hover { 137 | color: @disabled-color; 138 | border-color: transparent; 139 | background-color: transparent; 140 | font-weight: normal; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /packages/components/vue/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-vue3 2 | 3 | This is a micro-frontend application based on the Vue3 framework. It mainly demonstrates the cross-end capability of OpenTiny. 4 | 5 | ## Local startup 6 | 7 | Start by running the following command: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | Congratulations on the success of the launch! -------------------------------------------------------------------------------- /packages/components/vue/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/vue 2 | 3 | 这是一个基于 vue 框架的组件库,同时支持 Vue2 和 Vue3 4 | 5 | ### 1. 安装 6 | 7 | 执行以下命令,安装 Vue 3 版本的 @opentiny/vue 组件库: 8 | 9 | ```shell 10 | npm i @opentiny/vue@3 11 | ``` 12 | 13 | 执行以下命令,安装 Vue 2 版本的 TinyVue 组件库: 14 | 15 | ```shell 16 | npm i @opentiny/vue@2 17 | ``` 18 | 19 | ### 2. 引入和使用 20 | 21 | 在`App.vue`文件中使用 @opentiny/vue 组件。 22 | 23 | ```html 24 | 27 | 28 | 31 | ``` 32 | -------------------------------------------------------------------------------- /packages/components/vue/button/index.js: -------------------------------------------------------------------------------- 1 | import Button from './src/index' 2 | import { version } from './package.json' 3 | 4 | /* istanbul ignore next */ 5 | Button.install = function (Vue) { 6 | Vue.component(Button.name, Button) 7 | } 8 | 9 | Button.version = version 10 | 11 | /* istanbul ignore next */ 12 | if (process.env.BUILD_TARGET === 'runtime') { 13 | if (typeof window !== 'undefined' && window.Vue) { 14 | Button.install(window.Vue) 15 | } 16 | } 17 | 18 | export default Button 19 | -------------------------------------------------------------------------------- /packages/components/vue/button/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/vue-button", 3 | "version": "3.9.0", 4 | "description": "", 5 | "module": "index.js", 6 | "sideEffects": false, 7 | "type": "module", 8 | "dependencies": { 9 | "@opentiny/vue-common": "workspace:~", 10 | "@opentiny/theme": "workspace:~", 11 | "@opentiny/theme-mobile": "workspace:~", 12 | "@opentiny/theme-watch": "workspace:~", 13 | "@opentiny/renderless": "workspace:~" 14 | }, 15 | "license": "MIT" 16 | } 17 | -------------------------------------------------------------------------------- /packages/components/vue/button/src/index.js: -------------------------------------------------------------------------------- 1 | import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common' 2 | import PcTemplate from './pc.vue' 3 | import MobileTemplate from './mobile.vue' 4 | import WatchTemplate from './watch.vue' 5 | 6 | const template = (mode) => { 7 | if (mode === 'mobile') { 8 | return MobileTemplate 9 | } else if (mode === 'watch') { 10 | return WatchTemplate 11 | } else { 12 | return PcTemplate 13 | } 14 | } 15 | 16 | export default defineComponent({ 17 | name: $prefix + 'Button', 18 | inject: { 19 | buttonGroup: { 20 | default: '' 21 | } 22 | }, 23 | props: { 24 | ...$props, 25 | type: { 26 | type: String, 27 | default: 'default' 28 | }, 29 | tabindex: { type: String, default: '1' }, 30 | icon: { 31 | type: [Object, String], 32 | default: '' 33 | }, 34 | text: { 35 | type: String, 36 | default: '' 37 | }, 38 | resetTime: { 39 | type: Number, 40 | default: 1000 41 | }, 42 | nativeType: { 43 | type: String, 44 | default: 'button' 45 | }, 46 | size: { 47 | type: String, 48 | default: '', 49 | validator(val) { 50 | return ['large', 'medium', 'small', 'mini', ''].includes(val) 51 | } 52 | }, 53 | round: Boolean, 54 | plain: Boolean, 55 | circle: Boolean, 56 | loading: Boolean, 57 | disabled: Boolean, 58 | autofocus: Boolean, 59 | buttonClass: { 60 | type: String, 61 | default: '' 62 | } 63 | }, 64 | setup(props, context) { 65 | return $setup({ props, context, template }) 66 | } 67 | }) 68 | -------------------------------------------------------------------------------- /packages/components/vue/button/src/mobile.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 54 | -------------------------------------------------------------------------------- /packages/components/vue/button/src/pc.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 63 | -------------------------------------------------------------------------------- /packages/components/vue/button/src/watch.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 43 | -------------------------------------------------------------------------------- /packages/components/vue/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/vue-common", 3 | "private": true, 4 | "version": "3.9.0", 5 | "main": "src/index.js", 6 | "sideEffects": false, 7 | "type": "module", 8 | "dependencies": { 9 | "@opentiny/vue-locale": "~3.9.0", 10 | "@opentiny/vue-renderless": "~3.9.0", 11 | "@opentiny/theme": "workspace:~", 12 | "@opentiny/theme-mobile": "workspace:~", 13 | "tailwind-merge": "^1.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/components/vue/common/src/adapter/index.js: -------------------------------------------------------------------------------- 1 | import vue from 'virtual:common/adapter/vue' 2 | 3 | export * from 'virtual:common/adapter/vue' 4 | 5 | export default vue 6 | -------------------------------------------------------------------------------- /packages/components/vue/common/src/adapter/teleport.js: -------------------------------------------------------------------------------- 1 | let globalId = 0 2 | 3 | const getHasComment = (state) => (comment) => { 4 | const childNodes = Array.from(state.parent.childNodes) 5 | 6 | for (let i = 0; i < childNodes.length; i++) { 7 | if (childNodes[i].textContent === comment) { 8 | return true 9 | } 10 | } 11 | } 12 | 13 | const getGetFragment = 14 | ({ hasComment, startComment, state, endComment }) => 15 | (commentFlag) => { 16 | const fragment = document.createDocumentFragment() 17 | 18 | commentFlag && 19 | !hasComment(startComment) && 20 | fragment.appendChild(document.createComment(startComment)) 21 | 22 | state.nodes.forEach((node) => fragment.appendChild(node)) 23 | 24 | commentFlag && 25 | !hasComment(endComment) && 26 | fragment.appendChild(document.createComment(endComment)) 27 | 28 | return fragment 29 | } 30 | 31 | const getDisable = 32 | ({ instance, getFragment, state, startComment, endComment }) => 33 | () => { 34 | instance.$el.appendChild(getFragment()) 35 | 36 | const indices = [] 37 | 38 | Array.from(state.parent.childNodes).forEach((child, i) => { 39 | if (child.nodeType === 8) { 40 | if ( 41 | child.textContent === startComment || 42 | child.textContent === endComment 43 | ) { 44 | indices.push(i) 45 | } 46 | } 47 | }) 48 | 49 | const minIndex = Math.min(...indices) 50 | const maxIndex = Math.max(...indices) 51 | 52 | Array.from(state.parent.childNodes) 53 | .slice(minIndex, maxIndex + 1) 54 | .reverse() 55 | .forEach((child) => state.parent.removeChild(child)) 56 | 57 | state.parent = null 58 | } 59 | 60 | const getMove = 61 | ({ state, props, disable, getFragment }) => 62 | () => { 63 | state.waiting = false 64 | state.parent = document.querySelector(props.to) 65 | 66 | if (!state.parent) { 67 | disable() 68 | state.waiting = true 69 | 70 | return 71 | } 72 | 73 | if (props.where === 'before') { 74 | state.parent.prepend(getFragment(true)) 75 | } else { 76 | state.parent.appendChild(getFragment(true)) 77 | } 78 | } 79 | 80 | const getTeardownObserver = (state) => () => { 81 | if (state.observer) { 82 | state.observer.disconnect() 83 | state.observer = null 84 | } 85 | } 86 | 87 | const getOnMutations = 88 | ({ state, disable, props, move }) => 89 | (mutations) => { 90 | let shouldMove = false 91 | 92 | for (let i = 0; i < mutations.length; i++) { 93 | const mutation = mutations[i] 94 | const filteredAddedNodes = Array.from(mutation.addedNodes).filter( 95 | (node) => !state.nodes.includes(node) 96 | ) 97 | 98 | if (Array.from(mutation.removedNodes).includes(state.parent)) { 99 | disable() 100 | state.waiting = !props.disabled 101 | } else if (state.waiting && filteredAddedNodes.length > 0) { 102 | shouldMove = true 103 | } 104 | } 105 | 106 | shouldMove && move() 107 | } 108 | 109 | const getBootObserver = 110 | ({ state, onMutations }) => 111 | () => { 112 | if (state.observer) return 113 | 114 | state.observer = new MutationObserver((mutations) => onMutations(mutations)) 115 | 116 | state.observer.observe(document.body, { 117 | attributes: false, 118 | characterData: false, 119 | childList: true, 120 | subtree: true 121 | }) 122 | } 123 | 124 | const getAfterUpdated = 125 | ({ state, instance, props, bootObserver, maybeMove }) => 126 | () => { 127 | state.nodes = Array.from(instance.$el.childNodes) 128 | 129 | !props.disabled && bootObserver() 130 | maybeMove() 131 | } 132 | 133 | const getWatchDisabled = 134 | ({ disable, teardownObserver, bootObserver, move }) => 135 | (value) => { 136 | if (value) { 137 | disable() 138 | teardownObserver() 139 | 140 | return 141 | } 142 | 143 | bootObserver() 144 | move() 145 | } 146 | 147 | export default ({ 148 | reactive, 149 | watch, 150 | getCurrentInstance, 151 | onUpdated, 152 | onMounted, 153 | onBeforeUnmount, 154 | h, 155 | defineComponent 156 | }) => 157 | defineComponent({ 158 | name: 'Vue2Teleport', 159 | props: { 160 | to: { type: String, required: true }, 161 | where: { type: String, default: 'after' }, 162 | disabled: Boolean 163 | }, 164 | setup(props) { 165 | const state = reactive({ 166 | nodes: [], 167 | waiting: false, 168 | observer: null, 169 | parent: null, 170 | id: ++globalId 171 | }) 172 | const instance = getCurrentInstance()?.proxy 173 | const startComment = `[${state.id}]vue2-teleporter-start` 174 | const endComment = `[${state.id}]vue2-teleporter-end` 175 | 176 | const hasComment = getHasComment(state) 177 | const getFragment = getGetFragment({ 178 | hasComment, 179 | startComment, 180 | state, 181 | endComment 182 | }) 183 | const disable = getDisable({ 184 | instance, 185 | getFragment, 186 | state, 187 | startComment, 188 | endComment 189 | }) 190 | const move = getMove({ state, props, disable, getFragment }) 191 | const maybeMove = () => !props.disabled && move() 192 | const teardownObserver = getTeardownObserver(state) 193 | const onMutations = getOnMutations({ state, disable, props, move }) 194 | const bootObserver = getBootObserver({ state, onMutations }) 195 | const afterUpdated = getAfterUpdated({ 196 | state, 197 | instance, 198 | props, 199 | bootObserver, 200 | maybeMove 201 | }) 202 | const watchDisabled = getWatchDisabled({ 203 | disable, 204 | teardownObserver, 205 | bootObserver, 206 | move 207 | }) 208 | 209 | watch(() => props.to, maybeMove) 210 | watch(() => props.where, maybeMove) 211 | watch(() => props.disabled, watchDisabled) 212 | 213 | onUpdated(afterUpdated) 214 | onMounted(afterUpdated) 215 | onBeforeUnmount(() => { 216 | disable() 217 | teardownObserver() 218 | }) 219 | 220 | return () => 221 | h( 222 | 'div', 223 | { 224 | class: 'vue2-teleporter', 225 | style: { 'visibility:hidden;display:none;': !props.disabled } 226 | }, 227 | (typeof instance?.$slots.default === 'function' 228 | ? instance.$slots.default() 229 | : instance.$slots.default) || null 230 | ) 231 | } 232 | }) 233 | -------------------------------------------------------------------------------- /packages/components/vue/common/src/adapter/utils.js: -------------------------------------------------------------------------------- 1 | export const emitter = () => { 2 | let listeners = {} 3 | 4 | const on = (event, callback, once = false) => { 5 | if (event && typeof event === 'string' && typeof callback === 'function') { 6 | const callbacks = listeners[event] || [] 7 | 8 | listeners[event] = callbacks 9 | callbacks.push(callback) 10 | callback.once = once 11 | } 12 | } 13 | 14 | const emitter = { 15 | emit(eventName) { 16 | const callbacks = listeners[eventName] 17 | 18 | if (callbacks) { 19 | callbacks.forEach((callback) => 20 | callback.apply(null, [].slice.call(arguments, 1)) 21 | ) 22 | 23 | listeners[eventName] = callbacks.filter((callback) => !callback.once) 24 | } 25 | }, 26 | on, 27 | once(event, callback) { 28 | on(event, callback, true) 29 | }, 30 | off(event, callback) { 31 | if (event && typeof event === 'string') { 32 | const callbacks = listeners[event] 33 | 34 | if (typeof callback === 'function') { 35 | listeners[event] = callbacks.filter((cb) => cb !== callback) 36 | } else { 37 | delete listeners[event] 38 | } 39 | } else { 40 | listeners = {} 41 | } 42 | } 43 | } 44 | 45 | return emitter 46 | } 47 | 48 | export const bindFilter = (props, attrs = {}) => { 49 | const properties = {} 50 | 51 | for (let name in props) { 52 | if (name.indexOf('_') !== 0) { 53 | properties[name] = props[name] 54 | } 55 | } 56 | 57 | for (let name in attrs) { 58 | properties[name] = attrs[name] 59 | } 60 | 61 | return properties 62 | } 63 | 64 | /** 65 | * 根据类名生成对应的hover、active等类名 66 | * 67 | * getElementStatusClass('border-color', 'hover') // 'border-color hover:border-color-hover' 68 | * getElementStatusClass(['border-color'], ['hover', 'active']) // 'border-color hover:border-color-hover active:border-color-active' 69 | * 70 | * @method 71 | * @param {String|Array} className - 类名 72 | * @param {String|Array} status - 状态 73 | * @returns {String} - 类名拼接的字符串 74 | */ 75 | export const getElementStatusClass = (className, status) => { 76 | if (!className || !status) return 77 | 78 | let classNames = [] 79 | if (typeof className === 'string') { 80 | classNames.push(className) 81 | } else if (Array.isArray(className)) { 82 | classNames = className 83 | } 84 | 85 | let statusList = [] 86 | if (typeof status === 'string') { 87 | statusList.push(status) 88 | } else if (Array.isArray(status)) { 89 | statusList = status 90 | } 91 | 92 | let res = [] 93 | statusList.forEach((status) => 94 | classNames.forEach((name) => res.push(`${status}:${name}-${status}`)) 95 | ) 96 | 97 | return classNames.concat(res).join(' ') 98 | } 99 | 100 | /** 101 | * 根据key值获取对应的classes类名配置 102 | * 103 | * getElementCssClass({ button: 'border-color' }, 'button') // 'border-color' 104 | * getElementCssClass({ button: 'border-color' }, { 'button': true }) // 'border-color' 105 | * 106 | * @method 107 | * @param {Object} classes - 类名集合 108 | * @param {String|Object} key - 状态 109 | * @returns {String} - 类名配置值 110 | */ 111 | export const getElementCssClass = (classes = {}, key) => { 112 | if (typeof key === 'object') { 113 | const keys = Object.keys(key) 114 | let cls = '' 115 | keys.forEach((k) => { 116 | if (key[k] && classes[k]) cls += `${classes[k]} ` 117 | }) 118 | return cls 119 | } else { 120 | return classes[key] || '' 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /packages/components/vue/common/src/adapter/vue2/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import * as hooks from '@vue/composition-api' 3 | import { 4 | bindFilter, 5 | emitter, 6 | getElementCssClass, 7 | getElementStatusClass 8 | } from '../utils' 9 | import teleport from '../teleport' 10 | 11 | const Teleport = teleport(hooks) 12 | 13 | export { 14 | emitter, 15 | bindFilter, 16 | getElementCssClass, 17 | getElementStatusClass, 18 | Teleport 19 | } 20 | 21 | Vue.use(hooks.default) 22 | 23 | export const defineAsyncComponent = ({ 24 | loader, 25 | loadingComponent, 26 | errorComponent, 27 | delay, 28 | timeout 29 | }) => { 30 | return () => ({ 31 | component: loader(), 32 | loading: loadingComponent, 33 | error: errorComponent, 34 | delay, 35 | timeout 36 | }) 37 | } 38 | 39 | export const markRaw = (ref) => ref 40 | 41 | export const renderComponent = ({ 42 | view = null, 43 | component = null, 44 | props, 45 | context: { attrs, listeners: on, slots }, 46 | extend = {} 47 | }) => { 48 | return () => 49 | hooks.h( 50 | (view && view.value) || component, 51 | Object.assign( 52 | { 53 | props, 54 | attrs, 55 | [extend.isSvg ? 'nativeOn' : 'on']: on, 56 | scopedSlots: { ...slots } 57 | }, 58 | extend 59 | ) 60 | ) 61 | } 62 | 63 | export const rootConfig = () => hooks.getCurrentInstance()?.proxy.$root 64 | 65 | export const getComponentName = () => { 66 | // 此处组件最多为两层组件,所以对多获取到父级组件即可 67 | const instance = hooks.getCurrentInstance() 68 | let componentName = 69 | instance?.vnode?.componentOptions?.Ctor?.extendOptions?.name 70 | if (!componentName) { 71 | componentName = 72 | instance?.parent?.vnode?.componentOptions?.Ctor?.extendOptions?.name 73 | } 74 | 75 | return componentName || '' 76 | } 77 | 78 | export const appContext = () => Vue 79 | 80 | export const appProperties = () => Vue.prototype 81 | 82 | export const useRouter = (instance = hooks.getCurrentInstance()?.proxy) => { 83 | return { 84 | route: instance?.$route, 85 | router: instance?.$router 86 | } 87 | } 88 | 89 | const emitEvent = (vm) => { 90 | const broadcast = (vm, componentName, eventName, params) => { 91 | vm.$children.forEach((child) => { 92 | const name = child.$options.componentName 93 | 94 | if (name === componentName) child.$emit(eventName, params) 95 | else broadcast(child, componentName, eventName, params) 96 | }) 97 | } 98 | 99 | return { 100 | dispatch(componentName, eventName, params) { 101 | let parent = vm.$parent || vm.$root 102 | let name = parent.$options.componentName 103 | 104 | while (parent && (!name || name !== componentName)) { 105 | parent = parent.$parent 106 | 107 | if (parent) name = parent.$options.componentName 108 | } 109 | 110 | if (parent) parent.$emit(...[eventName].concat(params)) 111 | }, 112 | broadcast(componentName, eventName, params) { 113 | broadcast(vm, componentName, eventName, params) 114 | } 115 | } 116 | } 117 | 118 | const parent = (vm) => (handler) => { 119 | let parent = vm.$parent 120 | let level = 0 121 | 122 | const parentObject = (parent) => { 123 | return { 124 | level, 125 | vm: createVm({}, parent), 126 | el: parent.$el, 127 | options: parent.$options 128 | } 129 | } 130 | 131 | if (typeof handler !== 'function') return parent ? parentObject(parent) : {} 132 | 133 | level++ 134 | 135 | while (parent) { 136 | if (handler(parentObject(parent))) break 137 | 138 | parent = parent.$parent 139 | level++ 140 | } 141 | } 142 | 143 | const children = (vm) => (handler) => { 144 | if (typeof handler !== 'function') return generateChildren(vm.$children) 145 | 146 | let layer = 1 147 | 148 | const broadcast = ($children) => { 149 | const level = layer++ 150 | 151 | if ( 152 | $children.some((child) => { 153 | return handler({ 154 | level, 155 | vm: createVm({}, child), 156 | el: child.$el, 157 | options: child.$options, 158 | isLevel1: level === 1 159 | }) 160 | }) 161 | ) 162 | return 163 | 164 | $children.forEach((child) => broadcast(child.$children)) 165 | } 166 | 167 | broadcast(vm.$children) 168 | } 169 | 170 | const generateChildren = ($children) => { 171 | const children = [] 172 | 173 | children.refs = {} 174 | 175 | $children.forEach((child) => { 176 | const vm = createVm({}, child) 177 | 178 | children.push(vm) 179 | child.$vnode.data.ref && (children.refs[child.$vnode.data.ref] = vm) 180 | }) 181 | 182 | return children 183 | } 184 | 185 | const defineProperties = (vm, instance, filter) => { 186 | for (const name in instance) { 187 | if (typeof filter === 'function' && filter(name)) continue 188 | 189 | Object.defineProperty(vm, name, { 190 | configurable: true, 191 | enumerable: true, 192 | get: () => instance[name], 193 | set: (value) => (instance[name] = value) 194 | }) 195 | } 196 | 197 | return vm 198 | } 199 | 200 | const filter = (name) => 201 | name.indexOf('$') === 0 || name.indexOf('_') === 0 || name === 'constructor' 202 | 203 | const customEmit = (context, emit) => { 204 | return function (...args) { 205 | emit.apply(context, args) 206 | 207 | // vue3 下 emit('update:modelValue') 会同时触发 input 事件,vue2 不会 208 | if (args[0] === 'update:modelValue') 209 | emit.apply(context, ['input'].concat(args.slice(1))) 210 | } 211 | } 212 | 213 | const createVm = (vm, instance, context = undefined) => { 214 | context || defineProperties(vm, instance, filter) 215 | 216 | Object.defineProperties(vm, { 217 | $attrs: { get: () => instance.$attrs }, 218 | $children: { get: () => generateChildren(instance.$children) }, 219 | $constants: { get: () => instance._constants }, 220 | $emit: { get: () => customEmit(instance, instance.$emit) }, 221 | $el: { get: () => instance.$el }, 222 | $listeners: { get: () => instance.$listeners }, 223 | $mode: { get: () => instance._tiny_mode }, 224 | $nextTick: { get: () => hooks.nextTick }, 225 | $off: { get: () => instance.$off.bind(instance) }, 226 | $on: { get: () => instance.$on.bind(instance) }, 227 | $once: { get: () => instance.$once.bind(instance) }, 228 | $options: { 229 | get: () => ({ componentName: instance.$options.componentName }) 230 | }, 231 | $parent: { get: () => instance.$parent && createVm({}, instance.$parent) }, 232 | $refs: { get: () => instance.$refs }, 233 | $renderless: { get: () => instance.tiny_renderless }, 234 | $scopedSlots: { get: () => instance.$scopedSlots }, 235 | $set: { get: () => instance.$set }, 236 | $slots: { get: () => instance.$scopedSlots }, 237 | $template: { get: () => instance.tiny_template } 238 | }) 239 | 240 | return vm 241 | } 242 | 243 | export const tools = (context, mode) => { 244 | const instance = hooks.getCurrentInstance()?.proxy 245 | const root = instance?.$root 246 | const { route, router } = useRouter(instance) 247 | const i18n = root?.$i18n 248 | const { dispatch, broadcast } = emitEvent(instance) 249 | const parentHandler = parent(instance) 250 | const childrenHandler = children(instance) 251 | const vm = createVm({}, instance, context) 252 | const emit = context.emit 253 | const parentVm = instance.$parent ? createVm({}, instance.$parent) : null 254 | 255 | const setParentAttribute = ({ name, value }) => { 256 | instance.$parent[name] = value 257 | parentVm[name] = value 258 | } 259 | 260 | const defineInstanceProperties = (props) => { 261 | Object.defineProperties(vm, props) 262 | Object.defineProperties(instance, props) 263 | } 264 | 265 | const defineParentInstanceProperties = (props) => { 266 | parentVm && Object.defineProperties(parentVm, props) 267 | } 268 | 269 | hooks.onBeforeMount(() => defineProperties(vm, instance, filter)) 270 | 271 | return { 272 | vm, 273 | emit: customEmit(context, emit), 274 | emitter, 275 | route, 276 | router, 277 | dispatch, 278 | broadcast, 279 | parentHandler, 280 | childrenHandler, 281 | refs: context.refs, 282 | i18n, 283 | slots: context.slots, 284 | scopedSlots: context.slots, 285 | attrs: context.attrs, 286 | parent: parentVm, 287 | nextTick: hooks.nextTick, 288 | constants: instance?._constants, 289 | mode, 290 | isPCMode: mode === 'pc', 291 | isMobileMode: mode === 'mobile', 292 | service: instance?.$service, 293 | getService: () => instance?.$getService(vm), 294 | setParentAttribute, 295 | defineInstanceProperties, 296 | defineParentInstanceProperties 297 | } 298 | } 299 | 300 | const mapping = (content, before, after) => { 301 | if (typeof content[before] !== 'undefined') { 302 | const fn = content[before] 303 | 304 | content[after] = (el, binding, vnode) => { 305 | binding.instance = vnode.context 306 | fn(el, binding, vnode) 307 | } 308 | 309 | delete content[before] 310 | } 311 | } 312 | 313 | export const directive = (directives) => { 314 | for (const name in directives) { 315 | const content = directives[name] 316 | 317 | mapping(content, 'beforeMount', 'bind') 318 | mapping(content, 'updated', 'update') 319 | mapping(content, 'unmounted', 'unbind') 320 | } 321 | 322 | return directives 323 | } 324 | 325 | const bindVnodeData = ({ props, data, name, attr = name }) => { 326 | Object.defineProperty(props, attr, { 327 | get: () => data[name], 328 | set: (value) => (data[name] = value) 329 | }) 330 | } 331 | 332 | export const parseVnode = (vnode) => { 333 | const props = {} 334 | const data = 335 | (vnode.componentOptions && vnode.componentOptions.propsData) || {} 336 | 337 | for (const name in data) bindVnodeData({ props, data, name }) 338 | 339 | vnode.props = props 340 | vnode.type = { name: vnode.componentOptions && vnode.componentOptions.tag } 341 | 342 | return vnode 343 | } 344 | 345 | export const h = hooks.h 346 | 347 | export const createComponentFn = (design) => { 348 | return ({ component, propsData, el }) => { 349 | const comp = Object.assign(component, { 350 | provide: { [design.configKey]: design.configInstance } 351 | }) 352 | return new (Vue.extend(comp))({ propsData, el }).$mount() 353 | } 354 | } 355 | 356 | export const defineComponent = hooks.defineComponent 357 | 358 | export default hooks 359 | 360 | export const isVue2 = true 361 | 362 | export const isVue3 = false 363 | -------------------------------------------------------------------------------- /packages/components/vue/common/src/adapter/vue2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue2-common", 3 | "private": true, 4 | "version": "5.0.2-mf.0", 5 | "description": "", 6 | "main": "lib/index.js", 7 | "module": "index.ts", 8 | "devDependencies": { 9 | "@vue/composition-api": "~1.2.2", 10 | "vue": "~2.6.14", 11 | "vue-router": "^3.6.4" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/components/vue/common/src/adapter/vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-common", 3 | "private": true, 4 | "version": "5.0.2-mf.0", 5 | "description": "", 6 | "main": "index.ts", 7 | "devDependencies": { 8 | "@vue/runtime-core": "^3.2.31", 9 | "vue": "^3.3.4" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/components/vue/common/src/csscls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 简单合并 tailwind 类对象为字符串值 3 | * 4 | * @param cssClassObject tailwind 类对象 5 | * @returns string 6 | */ 7 | const stringifyCssClassObject = (cssClassObject) => { 8 | const allCssClass = [] 9 | 10 | Object.keys(cssClassObject).forEach( 11 | (cssClass) => cssClassObject[cssClass] && allCssClass.push(cssClass) 12 | ) 13 | 14 | return allCssClass.join('\u{20}') 15 | } 16 | 17 | /** 18 | * 简单合并 tailwind 类数组为字符串值 19 | * 20 | * @param cssClassArray tailwind 类数组 21 | * @returns string 22 | */ 23 | const stringifyCssClassArray = (cssClassArray) => { 24 | const allCssClass = [] 25 | 26 | cssClassArray.forEach((cssClass) => { 27 | if (typeof cssClass === 'string') { 28 | allCssClass.push(cssClass) 29 | } else if (typeof cssClass === 'object') { 30 | allCssClass.push(stringifyCssClassObject(cssClass)) 31 | } 32 | }) 33 | 34 | return allCssClass.join('\u{20}') 35 | } 36 | 37 | /** 38 | * 简单合并 tailwind 类对象为字符串值,去重处理留给 tailwind-merge 处理 39 | * 40 | * @param {*} cssClasses tailwind 类集合 41 | * @returns string 42 | */ 43 | export const stringifyCssClass = (cssClasses) => { 44 | if (!cssClasses || (Array.isArray(cssClasses) && !cssClasses.length)) 45 | return '' 46 | 47 | const allCssClass = [] 48 | 49 | cssClasses.forEach((cssClass) => { 50 | if (cssClass) { 51 | if (typeof cssClass === 'string') { 52 | allCssClass.push(cssClass) 53 | } else if (Array.isArray(cssClass)) { 54 | allCssClass.push(stringifyCssClassArray(cssClass)) 55 | } else if (typeof cssClass === 'object') { 56 | allCssClass.push(stringifyCssClassObject(cssClass)) 57 | } 58 | } 59 | }) 60 | 61 | return allCssClass.join('\u{20}') 62 | } 63 | -------------------------------------------------------------------------------- /packages/components/vue/common/src/index.js: -------------------------------------------------------------------------------- 1 | import hooks from './adapter' 2 | import { 3 | appContext, 4 | appProperties, 5 | bindFilter, 6 | createComponentFn, 7 | getElementCssClass, 8 | getElementStatusClass 9 | } from './adapter' 10 | import { 11 | defineAsyncComponent, 12 | directive, 13 | emitter, 14 | h, 15 | markRaw, 16 | Teleport 17 | } from './adapter' 18 | import { 19 | parseVnode, 20 | renderComponent, 21 | rootConfig, 22 | tools, 23 | useRouter, 24 | getComponentName 25 | } from './adapter' 26 | import { t } from '@opentiny/vue-locale' 27 | import { stringifyCssClass } from './csscls' 28 | import { twMerge } from 'tailwind-merge' 29 | import '@opentiny/theme/base/index.less' 30 | 31 | import { defineComponent, isVue2, isVue3 } from './adapter' 32 | 33 | export { version } from '../package.json' 34 | 35 | export { defineComponent, isVue2, isVue3, appProperties } 36 | 37 | export const $prefix = 'Tiny' 38 | 39 | export const $props = { 40 | tiny_mode: String, 41 | tiny_mode_root: Boolean, 42 | tiny_template: [Function, Object], 43 | tiny_renderless: Function, 44 | tiny_theme: String, 45 | tiny_chart_theme: Object 46 | } 47 | 48 | export const props = [ 49 | 'tiny_mode', 50 | 'tiny_mode_root', 51 | 'tiny_template', 52 | 'tiny_renderless', 53 | '_constants', 54 | 'tiny_theme', 55 | 'tiny_chart_theme' 56 | ] 57 | 58 | export const resolveMode = (props, context) => { 59 | let isRightMode = (mode) => 60 | ~['pc', 'mobile', 'mobile-first', 'watch'].indexOf(mode) 61 | let config = rootConfig(context) 62 | let tinyModeProp = 63 | typeof props.tiny_mode === 'string' ? props.tiny_mode : null 64 | let tinyModeInject = hooks.inject('TinyMode', null) 65 | let tinyModeGlobal = config.tiny_mode && config.tiny_mode.value 66 | 67 | if (!isRightMode(tinyModeProp)) tinyModeProp = null 68 | if (!isRightMode(tinyModeInject)) tinyModeInject = null 69 | if (!isRightMode(tinyModeGlobal)) tinyModeGlobal = null 70 | 71 | let tinyMode = tinyModeProp || tinyModeInject || tinyModeGlobal || 'pc' 72 | 73 | if (props.tiny_mode_root) { 74 | hooks.provide('TinyMode', tinyMode) 75 | } 76 | 77 | let instance = hooks.getCurrentInstance() 78 | 79 | if (isVue2) { 80 | instance = instance.proxy 81 | } 82 | 83 | Object.defineProperty(instance, '_tiny_mode', { value: tinyMode }) 84 | 85 | return tinyMode 86 | } 87 | 88 | const resolveTheme = ({ props, context, utils }) => { 89 | const isRightTheme = (theme) => ~['tiny', 'saas'].indexOf(theme) 90 | const config = rootConfig(context) 91 | let tinyThemeProp = 92 | typeof props.tiny_theme === 'string' ? props.tiny_theme : null 93 | let tinyThemeInject = hooks.inject('TinyTheme', null) 94 | let tinyThemeGlobal = config.tiny_theme && config.tiny_theme.value 95 | 96 | if (!isRightTheme(tinyThemeProp)) tinyThemeProp = null 97 | if (!isRightTheme(tinyThemeInject)) tinyThemeInject = null 98 | if (!isRightTheme(tinyThemeGlobal)) tinyThemeGlobal = null 99 | 100 | const tinyTheme = 101 | tinyThemeProp || tinyThemeInject || tinyThemeGlobal || 'tiny' 102 | 103 | return (utils.vm.theme = tinyTheme) 104 | } 105 | 106 | const resolveChartTheme = ({ props, context, utils }) => { 107 | const config = rootConfig(context) 108 | let tinyChartProp = 109 | typeof props.tiny_chart_theme === 'object' ? props.tiny_chart_theme : null 110 | let tinyChartInject = hooks.inject('TinyChartTheme', null) 111 | let tinyChartGlobal = config.tiny_chart_theme && config.tiny_chart_theme.value 112 | 113 | const tinyChartTheme = 114 | tinyChartProp || tinyChartInject || tinyChartGlobal || null 115 | 116 | return (utils.vm.chart_theme = tinyChartTheme) 117 | } 118 | 119 | export const $setup = ({ props, context, template, extend = {} }) => { 120 | const view = hooks.computed(() => { 121 | if (typeof props.tiny_template !== 'undefined') return props.tiny_template 122 | 123 | const component = template(resolveMode(props, context), props) 124 | 125 | return typeof component === 'function' 126 | ? defineAsyncComponent(component) 127 | : component 128 | }) 129 | 130 | initComponent() 131 | 132 | return renderComponent({ view, props, context, extend }) 133 | } 134 | 135 | export const mergeClass = (...cssClasses) => 136 | twMerge(stringifyCssClass(cssClasses)) 137 | 138 | // 提供给没有renderless层的组件使用(比如TinyVuePlus组件) 139 | export const design = { 140 | configKey: Symbol('designConfigKey'), 141 | configInstance: null 142 | } 143 | 144 | // 注入规范配置 145 | export const provideDesignConfig = (designConfig) => { 146 | if (Object.keys(designConfig).length) { 147 | hooks.provide(design.configKey, designConfig) 148 | design.configInstance = designConfig 149 | } 150 | } 151 | 152 | const createComponent = createComponentFn(design) 153 | 154 | export const setup = ({ 155 | props, 156 | context, 157 | renderless, 158 | api, 159 | extendOptions = { framework: isVue3 ? 'Vue3' : 'Vue2' }, 160 | mono = false, 161 | classes = {} 162 | }) => { 163 | const render = 164 | typeof props.tiny_renderless === 'function' 165 | ? props.tiny_renderless 166 | : renderless 167 | 168 | // 获取组件级配置和全局配置(inject需要带有默认值,否则控制台会报警告) 169 | const globalDesignConfig = hooks.inject(design.configKey, {}) 170 | const designConfig = 171 | globalDesignConfig?.components?.[getComponentName().replace($prefix, '')] 172 | 173 | const utils = { 174 | $prefix, 175 | t, 176 | ...tools(context, resolveMode(props, context)), 177 | mergeClass, 178 | designConfig, 179 | globalDesignConfig 180 | } 181 | 182 | resolveTheme({ props, context, utils }) 183 | resolveChartTheme({ props, context, utils }) 184 | const sdk = render(props, hooks, utils, extendOptions) 185 | 186 | // 加载全局配置,合并api 187 | if (typeof designConfig?.renderless === 'function') { 188 | Object.assign( 189 | sdk, 190 | designConfig.renderless(props, hooks, utils, sdk, extendOptions) 191 | ) 192 | } 193 | 194 | const attrs = { 195 | t, 196 | vm: utils.vm, 197 | f: bindFilter, 198 | a: filterAttrs, 199 | d: utils.defineInstanceProperties, 200 | dp: utils.defineParentInstanceProperties, 201 | gcls: (key) => getElementCssClass(classes, key), 202 | m: mergeClass 203 | } 204 | 205 | /** 206 | * 修复 render 函数下 this.slots 不会动态更新的问题(vue3 环境没有问题) 207 | * 解决方法:在 instance 下注入 slots、scopedSlots 208 | * 注意:renderless 下尽量使用 vm.$refs、vm.$slots 209 | */ 210 | attrs.d({ 211 | slots: { get: () => utils.vm.$slots }, 212 | scopedSlots: { get: () => utils.vm.$scopedSlots } 213 | }) 214 | 215 | attrs.dp({ 216 | slots: { get: () => utils.parent.$slots }, 217 | scopedSlots: { get: () => utils.parent.$scopedSlots } 218 | }) 219 | 220 | initComponent() 221 | 222 | Array.isArray(api) && 223 | api.forEach((name) => { 224 | const value = sdk[name] 225 | 226 | if (typeof value !== 'undefined') { 227 | attrs[name] = value 228 | // 只有单层组件,才需要给setup传递: mono:true 229 | // 双层组件,需要把内层的api复制到外层,这样用户应用的ref才能拿到组件的api 230 | if (!mono) { 231 | utils.setParentAttribute({ name, value }) 232 | } 233 | } 234 | }) 235 | 236 | return attrs 237 | } 238 | 239 | export const svg = ({ name = 'Icon', component }) => { 240 | return (propData) => 241 | markRaw( 242 | defineComponent({ 243 | name: $prefix + name, 244 | setup: (props, context) => { 245 | const { 246 | fill, 247 | width, 248 | height, 249 | 'custom-class': customClass 250 | } = context.attrs || {} 251 | const mergeProps = Object.assign({}, props, propData || null) 252 | const mode = resolveMode(mergeProps, context) 253 | const isMobileFirst = mode === 'mobile-first' 254 | const tinyTag = { 'data-tag': isMobileFirst ? 'tiny-svg' : null } 255 | const attrs = isVue3 ? tinyTag : { attrs: tinyTag } 256 | const className = isMobileFirst 257 | ? mergeClass( 258 | 'h-4 w-4 inline-block', 259 | customClass || '', 260 | mergeProps.class || '' 261 | ) 262 | : 'tiny-svg' 263 | const extend = Object.assign( 264 | { 265 | style: { fill, width, height }, 266 | class: className, 267 | isSvg: true 268 | }, 269 | attrs 270 | ) 271 | 272 | // 解决本地运行会报大量警告的问题 273 | if (process.env.BUILD_TARGET) { 274 | extend.nativeOn = context.listeners 275 | } 276 | 277 | return renderComponent({ 278 | component, 279 | props: mergeProps, 280 | context, 281 | extend 282 | }) 283 | } 284 | }) 285 | ) 286 | } 287 | 288 | export const filterAttrs = (attrs, filters, include) => { 289 | const props = {} 290 | 291 | for (let name in attrs) { 292 | const find = filters.some((r) => new RegExp(r).test(name)) 293 | 294 | if ((include && find) || (!include && !find)) { 295 | props[name] = attrs[name] 296 | } 297 | } 298 | 299 | return props 300 | } 301 | 302 | export let setupComponent = {} 303 | 304 | export const initComponent = () => { 305 | for (let name in setupComponent) { 306 | const component = setupComponent[name] 307 | 308 | if (typeof component.install === 'function') { 309 | component.install(appContext()) 310 | } 311 | 312 | if (typeof component.init === 'function') { 313 | component.init(appProperties()) 314 | } 315 | } 316 | 317 | setupComponent = {} 318 | } 319 | 320 | export const $install = (component) => { 321 | component.install = function (Vue) { 322 | Vue.component(component.name, component) 323 | } 324 | } 325 | 326 | export { 327 | h, 328 | hooks, 329 | directive, 330 | parseVnode, 331 | useRouter, 332 | emitter, 333 | createComponent, 334 | defineAsyncComponent, 335 | getElementStatusClass, 336 | Teleport 337 | } 338 | 339 | export default { 340 | h, 341 | directive, 342 | parseVnode, 343 | useRouter, 344 | emitter, 345 | createComponent, 346 | defineAsyncComponent, 347 | filterAttrs, 348 | initComponent, 349 | setupComponent, 350 | svg, 351 | $prefix, 352 | $props, 353 | props, 354 | $setup, 355 | setup, 356 | hooks, 357 | getElementStatusClass, 358 | $install 359 | } 360 | -------------------------------------------------------------------------------- /packages/components/vue/config-provider/hooks/use-config.ts: -------------------------------------------------------------------------------- 1 | import type { ConfigProviderProps } from '../src/props' 2 | import { configProviderContextKey } from '../index' 3 | import { hooks } from '@opentiny/vue-common' 4 | 5 | export function useConfig(): ConfigProviderProps | {} { 6 | return hooks.inject(configProviderContextKey) ?? {} 7 | } 8 | -------------------------------------------------------------------------------- /packages/components/vue/config-provider/index.js: -------------------------------------------------------------------------------- 1 | import ConfigProvider from './src/index.vue' 2 | import { version } from './package.json' 3 | 4 | export const configProviderContextKey = Symbol('CONFIG_PROVIDER_CONTEXT_KEY') 5 | 6 | /* istanbul ignore next */ 7 | ConfigProvider.install = function (Vue) { 8 | Vue.component(ConfigProvider.name, ConfigProvider) 9 | } 10 | 11 | ConfigProvider.version = version 12 | 13 | /* istanbul ignore next */ 14 | if (process.env.BUILD_TARGET === 'runtime') { 15 | if (typeof window !== 'undefined' && window.Vue) { 16 | ConfigProvider.install(window.Vue) 17 | } 18 | } 19 | 20 | export default ConfigProvider 21 | -------------------------------------------------------------------------------- /packages/components/vue/config-provider/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/vue-config-provider", 3 | "version": "3.9.0", 4 | "main": "ndex.js", 5 | "module": "index.js", 6 | "sideEffects": false, 7 | "type": "module", 8 | "dependencies": { 9 | "@opentiny/vue-common": "workspace:~" 10 | }, 11 | "license": "MIT" 12 | } -------------------------------------------------------------------------------- /packages/components/vue/config-provider/src/index.vue: -------------------------------------------------------------------------------- 1 | 102 | -------------------------------------------------------------------------------- /packages/components/vue/countdown/index.js: -------------------------------------------------------------------------------- 1 | import Countdown from './src/pc.vue' 2 | import { version } from './package.json' 3 | 4 | /* istanbul ignore next */ 5 | Countdown.install = function (Vue) { 6 | Vue.component(Countdown.name, Countdown) 7 | } 8 | 9 | Countdown.version = version 10 | 11 | /* istanbul ignore next */ 12 | if (process.env.BUILD_TARGET === 'runtime') { 13 | if (typeof window !== 'undefined' && window.Vue) { 14 | Countdown.install(window.Vue) 15 | } 16 | } 17 | 18 | export default Countdown 19 | -------------------------------------------------------------------------------- /packages/components/vue/countdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/vue-countdown", 3 | "version": "3.9.0", 4 | "description": "", 5 | "module": "index.js", 6 | "sideEffects": false, 7 | "type": "module", 8 | "dependencies": { 9 | "@opentiny/vue-common": "workspace:~", 10 | "@opentiny/theme": "workspace:~", 11 | "@opentiny/renderless": "workspace:~" 12 | }, 13 | "license": "MIT" 14 | } 15 | -------------------------------------------------------------------------------- /packages/components/vue/countdown/src/pc.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 55 | -------------------------------------------------------------------------------- /packages/components/vue/index.js: -------------------------------------------------------------------------------- 1 | import Button from '@opentiny/vue-button' 2 | import Countdown from '@opentiny/react-countdown' 3 | import ConfigProvider from '@opentiny/react-config-provider' 4 | 5 | export { Button, Countdown, ConfigProvider } 6 | -------------------------------------------------------------------------------- /packages/components/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/vue", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@opentiny/vue-button": "workspace:~", 14 | "@opentiny/vue-countdown": "workspace:~" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/README.md: -------------------------------------------------------------------------------- 1 | # element-to-opentiny 2 | 3 | This is a Vue2 project based on the ElementUI component library, using the following components of the ElementUI component library: 4 | 1. Carousel 5 | 2. Select 6 | 3. DatePicker 7 | 4. Input 8 | 5. Button 9 | 6. Table 10 | 7. Tabs 11 | 8. Form 12 | 9. TimePicker 13 | 10. Switch 14 | 11. Checkbox 15 | 12. Radio 16 | 17 | ## Development 18 | 19 | You can start by executing the following command from the root of the project: 20 | 21 | ```shell 22 | pnpm --filter element-to-opentiny dev 23 | ``` 24 | 25 | Or you can execute the following command under the `packages/element-to-opentiny` sub-package project to launch: 26 | 27 | ```shell 28 | npm run dev 29 | ``` 30 | 31 | The effect after startup is as follows: 32 | 33 | ![vue2-element](./public/assets/vue2-element.png) 34 | 35 | Now we need to upgrade this project to a Vue3 project that uses the OpenTiny component library. 36 | 37 | It is mainly divided into the following steps: 38 | 39 | 1. Replace a component with a component of OpenTiny 40 | 2. Replace all components of a page with components of OpenTiny 41 | 3. Replace all components of the entire application with components of OpenTiny. 42 | 4. Upgrade the project from Vue2 to Vue3 43 | 44 | ## Replace a component with a component of OpenTiny 45 | 46 | Install the `@opentiny/vue@2` component library first. 47 | 48 | ```shell 49 | npm i @ opentiny/vue@2 50 | ``` 51 | 52 | Import the OpenTiny Vue component library into the `main.js` file. 53 | 54 | ```ts 55 | import TinyVue from'@ opentiny/vue' 56 | 57 | Vue.use(TinyVue) 58 | ``` 59 | 60 | Take the Button component of the table page as an example, replace the `el-button` with `tiny-button` in the `ListPage.vue` file. 61 | 62 | ```html 63 | Search 64 | -> 65 | Search 66 | ``` 67 | 68 | The effect is as follows: 69 | 70 | ![el-button-to-tiny-button](./public/assets/el-button-to-tiny-button.png) 71 | 72 | ## Replace all components of a page with components of OpenTiny 73 | 74 | Take the `ListPage` table page as an example, directly change the `el-` component prefix to `tiny-` of the entire template. 75 | 76 | The effect is as follows: 77 | 78 | ![one-page-el-to-tiny](./public/assets/one-page-el-to-tiny.png) 79 | 80 | ## Replace all components of the entire application with those of OpenTiny 81 | 82 | The page involved: 83 | 84 | - HomePage: Carousel 85 | - ListPage: Select / DatePicker / Input / Button / Table 86 | - FormPage: Form / TimePicker / Switch / Checkbox / Radio 87 | - App: Tabs 88 | 89 | The replacement method is the same as the previous one. To replace `el-` with `tiny-`, you should pay attention to: 90 | 91 | - `tabs` component `tab-click -> click`, `el-tab-pane -> tiny-tab-item` 92 | - change `label` of `tab-pane` to `title` of `tab-item` 93 | 94 | The effect is as follows: 95 | 96 | ![all-pages-element-to-opentiny](./public/assets/all-pages-element-to-opentiny.png) 97 | 98 | ## Upgrade the project from Vue2 to Vue3 99 | 100 | The steps are as follows: 101 | 102 | Step 1: Install gogocode 103 | 104 | ```shell 105 | npm install gogocode-cli -g 106 | ``` 107 | 108 | Step 2: Convert the source code 109 | 110 | ```shell 111 | gogocode -s ./src -t gogocode-plugin-vue -o ./src 112 | ``` 113 | 114 | Step 3: Upgrade dependency 115 | 116 | ```shell 117 | gogocode -s package.json -t gogocode-plugin-vue -o package.json 118 | ``` 119 | 120 | Step 4: Upgrade the OpenTiny Vue component library to version 3.0 121 | 122 | ```shell 123 | npm i @ opentiny/vue@3 124 | ``` 125 | 126 | The component code does not need to be modified, and the Vue2 project is smoothly upgraded to Vue3. 127 | 128 | ![vue2-to-vue3](./public/assets/vue2-to-vue3.png) 129 | 130 | Congratulations on completing the upgrade!🎉 131 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # element-to-opentiny 2 | 3 | 这是一个基于 ElementUI 组件库的 Vue2 项目,使用了 ElementUI 组件库的以下组件: 4 | 1. Carousel 5 | 2. Select 6 | 3. DatePicker 7 | 4. Input 8 | 5. Button 9 | 6. Table 10 | 7. Tabs 11 | 8. Form 12 | 9. TimePicker 13 | 10. Switch 14 | 11. Checkbox 15 | 12. Radio 16 | 17 | ## 本地启动 18 | 19 | 可以通过在项目根目录执行以下命令进行启动: 20 | 21 | ```shell 22 | pnpm --filter element-to-opentiny dev` 23 | ``` 24 | 25 | 或者你也可以在 `packages/element-to-opentiny` 子包项目下面执行以下命令进行启动: 26 | 27 | ```shell 28 | npm run dev 29 | ``` 30 | 31 | 启动之后效果如下: 32 | 33 | ![vue2-element](./public/assets/vue2-element.png) 34 | 35 | 现在我们需要将这个项目升级到使用 OpenTiny 组件库的 Vue3 项目。 36 | 37 | 主要分成以下几个步骤: 38 | 39 | 1. 将一个组件替换成 OpenTiny 的组件 40 | 2. 将一个页面的所有组件替换成 OpenTiny 的组件 41 | 3. 将整个应用的所有组件替换成 OpenTiny 的组件 42 | 4. 将项目由 Vue2 升级到 Vue3 43 | 44 | ## 将一个组件替换成 OpenTiny 的组件 45 | 46 | 先安装 `@opentiny/vue@2` 组件库。 47 | 48 | ```shell 49 | npm i @opentiny/vue@2 50 | ``` 51 | 52 | 在 `main.js` 文件中引入 OpenTiny Vue 组件库。 53 | 54 | ```ts 55 | import TinyVue from '@opentiny/vue' 56 | 57 | Vue.use(TinyVue) 58 | ``` 59 | 60 | 以表格页面的 Button 组件为例,将 `src/components/ListPage.vue` 文件中的 `el-button` 换成 `tiny-button`。 61 | 62 | ```html 63 | 搜索 64 | -> 65 | 搜索 66 | ``` 67 | 68 | 效果如下: 69 | 70 | ![el-button-to-tiny-button](./public/assets/el-button-to-tiny-button.png) 71 | 72 | ## 将一个页面的所有组件替换成 OpenTiny 的组件 73 | 74 | 以 `ListPage` 表格页面为例,直接把整个模板的 `el-` 组件前缀改成 `tiny-` 组件前缀即可。 75 | 76 | 效果如下: 77 | 78 | ![one-page-el-to-tiny](./public/assets/one-page-el-to-tiny.png) 79 | 80 | ## 将整个应用的所有组件替换成 OpenTiny 的组件 81 | 82 | 涉及页面: 83 | 84 | - HomePage 首页:Carousel 85 | - ListPage 表格页面:Select、DatePicker、Input、Button、Table 86 | - FormPage 表单页面:Form、TimePicker、Switch、Checkbox、Radio 87 | - App 应用首页:Tabs 88 | 89 | 替换方式和前面的方式一样,将 `el-` 替换成 `tiny-`,需要注意: 90 | 91 | - `tabs` 组件的 `tab-click -> click`,`el-tab-pane -> tiny-tab-item` 92 | - `tab-pane` 的 `label` 改成 `tab-item` 的 `title` 93 | 94 | 效果如下: 95 | 96 | ![all-pages-element-to-opentiny](./public/assets//all-pages-element-to-opentiny.png) 97 | 98 | ## 将项目由 Vue2 升级到 Vue3 99 | 100 | 步骤如下: 101 | 102 | 第一步:安装 gogocode 103 | 104 | ```shell 105 | npm install gogocode-cli -g 106 | ``` 107 | 108 | 第二步:转换源码 109 | 110 | ```shell 111 | gogocode -s ./src -t gogocode-plugin-vue -o ./src 112 | ``` 113 | 114 | 第三步:升级依赖 115 | 116 | ```shell 117 | gogocode -s package.json -t gogocode-plugin-vue -o package.json 118 | ``` 119 | 120 | 第四步:升级 OpenTiny Vue 组件库到 3.0 版本 121 | 122 | ```shell 123 | npm i @opentiny/vue@3 124 | ``` 125 | 126 | 组件代码无需做任何修改,完成 Vue2 项目平滑升级到 Vue3。 127 | 128 | ![vue2-to-vue3](./public/assets/vue2-to-vue3.png) 129 | 130 | 恭喜你完成升级!🎉 131 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "element-to-opentiny", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview" 8 | }, 9 | "dependencies": { 10 | "element-ui": "^2.15.13", 11 | "vue": "2.6.14", 12 | "vue-router": "^3.6.5" 13 | }, 14 | "devDependencies": { 15 | "vue-template-compiler": "2.6.14", 16 | "vite-plugin-vue2": "^2.0.3", 17 | "vite": "^4.4.8" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/public/assets/all-pages-element-to-opentiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/element-to-opentiny/public/assets/all-pages-element-to-opentiny.png -------------------------------------------------------------------------------- /packages/element-to-opentiny/public/assets/el-button-to-tiny-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/element-to-opentiny/public/assets/el-button-to-tiny-button.png -------------------------------------------------------------------------------- /packages/element-to-opentiny/public/assets/one-page-el-to-tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/element-to-opentiny/public/assets/one-page-el-to-tiny.png -------------------------------------------------------------------------------- /packages/element-to-opentiny/public/assets/vue2-element.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/element-to-opentiny/public/assets/vue2-element.png -------------------------------------------------------------------------------- /packages/element-to-opentiny/public/assets/vue2-to-vue3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/element-to-opentiny/public/assets/vue2-to-vue3.png -------------------------------------------------------------------------------- /packages/element-to-opentiny/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/element-to-opentiny/public/favicon.ico -------------------------------------------------------------------------------- /packages/element-to-opentiny/src/App.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 43 | 44 | 55 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/element-to-opentiny/src/assets/logo.png -------------------------------------------------------------------------------- /packages/element-to-opentiny/src/components/FormPage.vue: -------------------------------------------------------------------------------- 1 | 47 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 41 | 42 | 43 | 59 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/src/components/HomePage.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/src/components/ListPage.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 80 | 81 | 90 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | import App from './App.vue' 5 | import VueRouter from 'vue-router' 6 | 7 | Vue.use(ElementUI) 8 | 9 | const router = new VueRouter({ 10 | routes: [ 11 | { 12 | path: '/', 13 | redirect: '/home' 14 | }, 15 | { 16 | path: '/home', 17 | component: () => import('./components/HomePage.vue') 18 | }, 19 | { 20 | path: '/form', 21 | component: () => import('./components/FormPage.vue') 22 | }, 23 | { 24 | path: '/list', 25 | component: () => import('./components/ListPage.vue') 26 | } 27 | ] 28 | }) 29 | 30 | Vue.config.productionTip = false 31 | Vue.use(VueRouter) 32 | 33 | new Vue({ 34 | router, 35 | render: (h) => h(App) 36 | }).$mount('#app') 37 | -------------------------------------------------------------------------------- /packages/element-to-opentiny/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { createVuePlugin } from 'vite-plugin-vue2' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [createVuePlugin()], 7 | server: { 8 | port: 2200, 9 | host: 'localhost' 10 | }, 11 | define: { 12 | 'process.env': { ...process.env } 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /packages/home/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-home 2 | 3 | This is a Vue3 framework-based micro front-end (WUOUGH) main-application, which demonstrates the cross-framework capability of OpenTiny. 4 | 5 | ## Local startup 6 | 7 | Start by running the following command: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | Congratulations on the success of the launch! -------------------------------------------------------------------------------- /packages/home/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-home 2 | 3 | 这是一个基于Vue3框架的微前端(WUJIE)主应用,主要演示OpenTiny的跨框架能力: 4 | 5 | ## 本地启动 6 | 7 | 通过运行以下命令启动: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | 恭喜你启动成功!🎉 14 | -------------------------------------------------------------------------------- /packages/home/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OpenTiny - App 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/home/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/docs-home", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "vue": "^3.2.13", 12 | "vue-router": "^4.0.3", 13 | "wujie-vue3": "^1.0.18" 14 | }, 15 | "devDependencies": { 16 | "@vitejs/plugin-vue": "^4.2.3", 17 | "@vitejs/plugin-vue-jsx": "^3.0.1", 18 | "vite": "^4.4.8" 19 | }, 20 | "author": "", 21 | "license": "ISC" 22 | } 23 | -------------------------------------------------------------------------------- /packages/home/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/home/public/favicon.ico -------------------------------------------------------------------------------- /packages/home/src/App.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 50 | 51 | 150 | -------------------------------------------------------------------------------- /packages/home/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/home/src/assets/logo.png -------------------------------------------------------------------------------- /packages/home/src/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | font-size: 17.5px; 4 | padding: 0px; 5 | width: auto; 6 | height: 100%; 7 | position: relative; 8 | } 9 | #app { 10 | font-family: Avenir, Helvetica, Arial, sans-serif; 11 | -webkit-font-smoothing: antialiased; 12 | -moz-osx-font-smoothing: grayscale; 13 | overflow: auto; 14 | color: #2c3e50; 15 | --el-color-primary: #0239d0; 16 | } 17 | 18 | #nav { 19 | padding: 30px; 20 | text-align: center; 21 | font-size: 20px; 22 | line-height: 1; 23 | } 24 | 25 | .content { 26 | margin: 0 auto; 27 | } 28 | .content > p { 29 | margin: 30px 0; 30 | } 31 | 32 | h3 { 33 | padding-bottom: 0.3rem; 34 | border-bottom: 1px solid #eaecef; 35 | font-weight: 600; 36 | } 37 | 38 | #nav a { 39 | font-weight: bold; 40 | color: #2c3e50; 41 | } 42 | 43 | #nav a.router-link-exact-active { 44 | color: #0239d0; 45 | } 46 | -------------------------------------------------------------------------------- /packages/home/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import WujieVue from 'wujie-vue3' 3 | import App from './App.vue' 4 | import router from './router' 5 | import './index.css' 6 | 7 | const app = createApp(App) 8 | 9 | app.use(WujieVue).use(router).mount('#app') 10 | -------------------------------------------------------------------------------- /packages/home/src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from 'vue-router' 2 | import Home from '../views/Home.vue' 3 | import React from '../views/React.vue' 4 | import Solid from '../views/Solid.vue' 5 | import Vue2 from '../views/Vue2.vue' 6 | import Vue3 from '../views/Vue3.vue' 7 | const basename = process.env.NODE_ENV === 'production' ? '/demo-main-vue/' : '' 8 | 9 | const routes = [ 10 | { 11 | path: '/home', 12 | name: 'home', 13 | component: Home 14 | }, 15 | { 16 | path: '/', 17 | redirect: '/home' 18 | }, 19 | { 20 | path: '/react', 21 | name: 'react', 22 | component: React 23 | }, 24 | { 25 | path: '/solid', 26 | name: 'solid', 27 | component: Solid 28 | }, 29 | { 30 | path: '/vue2', 31 | name: 'vue2', 32 | component: Vue2 33 | }, 34 | { 35 | path: '/vue3', 36 | name: 'vue3', 37 | component: Vue3 38 | } 39 | ] 40 | 41 | const router = createRouter({ 42 | history: createWebHashHistory(), 43 | base: basename, 44 | routes 45 | }) 46 | 47 | export default router 48 | -------------------------------------------------------------------------------- /packages/home/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 42 | 43 | 69 | -------------------------------------------------------------------------------- /packages/home/src/views/React.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | -------------------------------------------------------------------------------- /packages/home/src/views/Solid.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/home/src/views/Vue2.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/home/src/views/Vue3.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/home/vite.config.js: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue' 2 | 3 | export default { 4 | resolve: { 5 | extensions: ['.js', '.jsx', '.vue'] 6 | }, 7 | server: { 8 | port: 5173, 9 | host: 'localhost', 10 | open: true 11 | }, 12 | plugins: [vue()], 13 | define: { 14 | 'process.env': Object.assign({}, process.env) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-react 2 | 3 | This is a React framework-based micro front-end (WUOUGH) sub-application, which demonstrates the cross-framework capability of OpenTiny. 4 | 5 | ## Local startup 6 | 7 | Start by running the following command: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | Congratulations on the success of the launch! -------------------------------------------------------------------------------- /packages/react/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-react 2 | 3 | 这是一个基于React框架的微前端(WUJIE)子应用,主要演示OpenTiny的跨框架能力: 4 | 5 | ## 本地启动 6 | 7 | 通过运行以下命令启动: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | 恭喜你启动成功!🎉 14 | -------------------------------------------------------------------------------- /packages/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OpenTiny - React 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/docs-react", 3 | "private": true, 4 | "version": "1.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@opentiny/react": "workspace:~", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "@opentiny/react-common": "workspace:~" 16 | }, 17 | "devDependencies": { 18 | "@vitejs/plugin-react": "^4.0.1", 19 | "eslint": "^8.44.0", 20 | "eslint-plugin-react-hooks": "^4.6.0", 21 | "vite": "^4.4.0", 22 | "vite-plugin-react": "^4.0.1", 23 | "vite-plugin-svgr": "^3.2.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/react/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/react/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Countdown } from '@opentiny/react' 2 | import './style.css' 3 | 4 | const operation = { 5 | start: () => { }, 6 | reset: () => { } 7 | } 8 | 9 | const operate = ({ start, reset }) => { 10 | operation.start = start 11 | operation.reset = reset 12 | } 13 | 14 | function App() { 15 | return ( 16 | <> 17 |
18 |

React

19 |
20 |
21 |
22 | 23 | 24 |
25 |
26 |
27 | 28 | ) 29 | } 30 | 31 | export default App 32 | -------------------------------------------------------------------------------- /packages/react/src/main.jsx: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | 5 | ReactDOM.createRoot(document.getElementById('root')).render() 6 | -------------------------------------------------------------------------------- /packages/react/src/style.css: -------------------------------------------------------------------------------- 1 | html,body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | .demo-box { 6 | position: relative; 7 | overflow: hidden; 8 | } 9 | 10 | .demo-titile { 11 | width: 80px; 12 | line-height: 36px; 13 | font-size: 24px; 14 | margin: 0; 15 | padding: 0 5px; 16 | background: #999; 17 | text-align: center; 18 | color: #fefefe; 19 | position: absolute; 20 | left: 0; 21 | top: 0; 22 | } 23 | 24 | .demo-container { 25 | margin-top: 40px; 26 | } 27 | 28 | .tiny-countdown { 29 | font-size: 50px; 30 | text-align: center; 31 | } 32 | .btn-box { 33 | text-align: center; 34 | margin-top: 20px; 35 | } 36 | 37 | .tiny-countdown__container { 38 | --ti-countdown-font-color:#19caad; 39 | } -------------------------------------------------------------------------------- /packages/react/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import svgr from 'vite-plugin-svgr' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), svgr()], 8 | server: { 9 | port: 2001, 10 | host: 'localhost' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /packages/solid/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-solid 2 | 3 | This is a Solid framework-based micro front-end (WUOUGH) sub-application, which demonstrates the cross-framework capability of OpenTiny. 4 | 5 | ## Local startup 6 | 7 | Start by running the following command: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | Congratulations on the success of the launch! -------------------------------------------------------------------------------- /packages/solid/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-solid 2 | 3 | 这是一个基于Solid框架的微前端(WUJIE)子应用,主要演示OpenTiny的跨框架能力: 4 | 5 | ## 本地启动 6 | 7 | 通过运行以下命令启动: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | 恭喜你启动成功!🎉 14 | -------------------------------------------------------------------------------- /packages/solid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OpenTiny - Solid 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/solid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/docs-solid", 3 | "private": true, 4 | "version": "1.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "solid-js": "^1.7.8", 13 | "@opentiny/solid": "workspace:~" 14 | }, 15 | "devDependencies": { 16 | "vite": "^4.4.5", 17 | "vite-plugin-solid": "^2.7.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/solid/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/solid/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { createSignal } from 'solid-js' 2 | import { Button, Countdown } from '@opentiny/solid' 3 | 4 | const operation = { 5 | start: () => { }, 6 | reset: () => { }, 7 | stop: () => { } 8 | } 9 | 10 | const operate = ({ start, reset }) => { 11 | operation.start = start 12 | operation.reset = reset 13 | } 14 | function App() { 15 | 16 | return ( 17 | <> 18 |
19 |

Solid

20 |
21 |
22 |
23 | 24 | 25 |
26 |
27 |
28 | 29 | 30 | ) 31 | } 32 | 33 | export default App 34 | -------------------------------------------------------------------------------- /packages/solid/src/assets/solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/solid/src/index.jsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | import { render } from 'solid-js/web' 3 | import App from './App' 4 | import './style.css' 5 | 6 | const root = document.getElementById('root') 7 | 8 | render(() => , root) 9 | -------------------------------------------------------------------------------- /packages/solid/src/style.css: -------------------------------------------------------------------------------- 1 | html,body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | .demo-box { 6 | position: relative; 7 | overflow: hidden; 8 | } 9 | 10 | .demo-titile { 11 | width: 80px; 12 | line-height: 36px; 13 | font-size: 24px; 14 | margin: 0; 15 | padding: 0 5px; 16 | background: #999; 17 | text-align: center; 18 | color: #fefefe; 19 | position: absolute; 20 | left: 0; 21 | top: 0; 22 | } 23 | 24 | .demo-container { 25 | margin-top: 40px; 26 | } 27 | 28 | .tiny-countdown { 29 | font-size: 50px; 30 | text-align: center; 31 | } 32 | .btn-box { 33 | text-align: center; 34 | margin-top: 20px; 35 | } -------------------------------------------------------------------------------- /packages/solid/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import solid from 'vite-plugin-solid' 3 | 4 | export default defineConfig({ 5 | plugins: [solid()], 6 | server: { 7 | port: 2002, 8 | host: 'localhost' 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /packages/vue2/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-vue2 2 | 3 | This is a Vue2 framework-based micro front-end (WUOUGH) sub-application, which demonstrates the cross-framework capability of OpenTiny. 4 | 5 | ## Local startup 6 | 7 | Start by running the following command: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | Congratulations on the success of the launch! -------------------------------------------------------------------------------- /packages/vue2/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-vue2 2 | 3 | 这是一个基于Vue2框架的微前端(WUJIE)子应用,主要演示OpenTiny的跨框架能力: 4 | 5 | ## 本地启动 6 | 7 | 通过运行以下命令启动: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | 恭喜你启动成功!🎉 14 | -------------------------------------------------------------------------------- /packages/vue2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OpenTiny - Vue2 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/vue2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/docs-vue2", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview" 8 | }, 9 | "dependencies": { 10 | "@opentiny/vue-button": "workspace:~", 11 | "@opentiny/vue-locale": "~2.9.0", 12 | "@opentiny/vue-countdown": "workspace:~", 13 | "vue": "2.6.14" 14 | }, 15 | "devDependencies": { 16 | "vue-template-compiler": "2.6.14", 17 | "vite-plugin-vue2": "^2.0.3", 18 | "vite": "^4.4.8" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/vue2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/vue2/public/favicon.ico -------------------------------------------------------------------------------- /packages/vue2/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/vue2/src/App.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 48 | 90 | -------------------------------------------------------------------------------- /packages/vue2/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentiny/cross-framework-component/00134876078a926367acff82536a74f3a72c1dcd/packages/vue2/src/assets/logo.png -------------------------------------------------------------------------------- /packages/vue2/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | new Vue({ 7 | render: (h) => h(App) 8 | }).$mount('#app') 9 | -------------------------------------------------------------------------------- /packages/vue2/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { createVuePlugin } from 'vite-plugin-vue2' 3 | import path from 'node:path' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [createVuePlugin()], 8 | server: { 9 | port: 2003, 10 | host: 'localhost' 11 | }, 12 | resolve: { 13 | alias: { 14 | 'virtual:common/adapter/vue': path.resolve( 15 | __dirname, 16 | `../components/vue/common/src/adapter/vue2/index` 17 | ) 18 | } 19 | }, 20 | define: { 21 | 'process.env': { ...process.env } 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /packages/vue3/README.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-vue3 2 | 3 | This is a micro-frontend application based on the Vue3 framework. It mainly demonstrates the cross-end capability of OpenTiny. 4 | 5 | ## Local startup 6 | 7 | Start by running the following command: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | Congratulations on the success of the launch! -------------------------------------------------------------------------------- /packages/vue3/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @opentiny/docs-vue3 2 | 3 | 这是一个基于Vue3框架的微前端(WUJIE)子应用,主要演示OpenTiny的跨端能力: 4 | 5 | ## 本地启动 6 | 7 | 通过运行以下命令启动: 8 | 9 | ```shell 10 | pnpm dev 11 | ``` 12 | 13 | 恭喜你启动成功!🎉 14 | -------------------------------------------------------------------------------- /packages/vue3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OpenTiny - Vue3 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opentiny/docs-vue3", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@opentiny/vue-button": "workspace:~", 12 | "@opentiny/vue-config-provider": "workspace:~", 13 | "@opentiny/vue-icon": "~3.9.0", 14 | "@opentiny/vue-modal": "~3.9.0", 15 | "@opentiny/vue-checkbox": "~3.9.0", 16 | "@opentiny/vue-checkbox-group": "~3.9.0", 17 | "@opentiny/vue-countdown": "workspace:~", 18 | "@opentiny/vue-zzc-theme": "~0.0.1", 19 | "vue": "^3.2.41" 20 | }, 21 | "devDependencies": { 22 | "@vitejs/plugin-vue": "^4.2.3", 23 | "vite": "^4.4.8" 24 | } 25 | } -------------------------------------------------------------------------------- /packages/vue3/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/vue3/src/App.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 50 | 51 | 135 | -------------------------------------------------------------------------------- /packages/vue3/src/DemoPoc.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 143 | -------------------------------------------------------------------------------- /packages/vue3/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/vue3/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import './style.css' 3 | import App from './App.vue' 4 | import '@opentiny/vue-zzc-theme/index.css' 5 | 6 | createApp(App).mount('#app') 7 | -------------------------------------------------------------------------------- /packages/vue3/src/views/mobile.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 45 | -------------------------------------------------------------------------------- /packages/vue3/src/views/pc.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 34 | -------------------------------------------------------------------------------- /packages/vue3/src/views/watch.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 70 | -------------------------------------------------------------------------------- /packages/vue3/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import path from 'node:path' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | server: { 9 | port: 2004, 10 | host: 'localhost', 11 | https: false, 12 | proxy: { 13 | '/api': { 14 | target: '*', 15 | changeOrigin: true 16 | } 17 | } 18 | }, 19 | resolve: { 20 | alias: { 21 | 'virtual:common/adapter/vue': path.resolve( 22 | __dirname, 23 | `../components/vue/common/src/adapter/vue3/index` 24 | ) 25 | } 26 | }, 27 | define: { 28 | 'process.env': { ...process.env } 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/** 3 | -------------------------------------------------------------------------------- /setup.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process') 2 | 3 | exec('npm run dev:vue3', __dirname) 4 | exec('npm run dev:react', __dirname) 5 | exec('npm run dev:solid', __dirname) 6 | exec('npm run dev:vue2', __dirname) 7 | exec('npm run dev:home', __dirname) 8 | 9 | console.log('home', 'http://localhost:5173/') 10 | console.log('react', 'http://localhost:2001/') 11 | console.log('solid', 'http://localhost:2002/') 12 | console.log('vue2', 'http://localhost:2003/') 13 | console.log('vue3', 'http://localhost:2004/') 14 | --------------------------------------------------------------------------------