create-vue-workspace 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/dist/index.mjs +910 -0
  4. package/package.json +41 -0
  5. package/templates/app-web/_env.development +2 -0
  6. package/templates/app-web/_env.production +2 -0
  7. package/templates/app-web/index.html +12 -0
  8. package/templates/app-web/package.json +25 -0
  9. package/templates/app-web/src/App.vue +18 -0
  10. package/templates/app-web/src/api/request.ts +15 -0
  11. package/templates/app-web/src/main.ts +7 -0
  12. package/templates/app-web/src/router/index.ts +16 -0
  13. package/templates/app-web/src/stores/app.ts +7 -0
  14. package/templates/app-web/src/styles/index.scss +8 -0
  15. package/templates/app-web/src/views/home-view.vue +16 -0
  16. package/templates/app-web/src/vite-env.d.ts +12 -0
  17. package/templates/app-web/tsconfig.json +7 -0
  18. package/templates/app-web/vite.config.ts +17 -0
  19. package/templates/base/README.md +53 -0
  20. package/templates/base/_changeset/config.json +11 -0
  21. package/templates/base/_editorconfig +12 -0
  22. package/templates/base/_gitignore +12 -0
  23. package/templates/base/eslint.config.mjs +25 -0
  24. package/templates/base/package.json +38 -0
  25. package/templates/base/pnpm-workspace.yaml +3 -0
  26. package/templates/base/prettier.config.mjs +8 -0
  27. package/templates/base/stylelint.config.mjs +8 -0
  28. package/templates/base/tsconfig.base.json +17 -0
  29. package/templates/base/tsconfig.json +4 -0
  30. package/templates/base/vitest.config.ts +10 -0
  31. package/templates/component/__tests__/{{fileName}}.component.spec.tsx +17 -0
  32. package/templates/component/components/{{fileName}}-sub.component.tsx +9 -0
  33. package/templates/component/composition/use-{{fileName}}.ts +46 -0
  34. package/templates/component/index.ts +12 -0
  35. package/templates/component/package.json +31 -0
  36. package/templates/component/tsconfig.json +5 -0
  37. package/templates/component/types.ts +5 -0
  38. package/templates/component/vite.config.ts +17 -0
  39. package/templates/component/{{fileName}}.component.tsx +22 -0
  40. package/templates/component/{{fileName}}.props.ts +12 -0
  41. package/templates/component/{{fileName}}.scss +11 -0
  42. package/templates/hook/use-{{kebab}}.ts +26 -0
  43. package/templates/pkg-lib/package.json +24 -0
  44. package/templates/pkg-lib/src/index.ts +5 -0
  45. package/templates/pkg-lib/tsconfig.json +4 -0
  46. package/templates/pkg-lib/vite.config.ts +17 -0
  47. package/templates/pkg-ui/package.json +31 -0
  48. package/templates/pkg-ui/src/index.ts +7 -0
  49. package/templates/pkg-ui/src/styles/index.scss +6 -0
  50. package/templates/pkg-ui/tsconfig.json +4 -0
  51. package/templates/pkg-ui/vite.config.ts +17 -0
  52. package/templates/pkg-utils/package.json +24 -0
  53. package/templates/pkg-utils/src/index.ts +20 -0
  54. package/templates/pkg-utils/tsconfig.json +4 -0
  55. package/templates/pkg-utils/vite.config.ts +13 -0
  56. package/templates/util/{{kebab}}.ts +4 -0
  57. package/templates/view/{{fileName}}.vue +15 -0
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "create-vue-workspace",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a Vue 3 pnpm monorepo (apps/* + packages/*) with independently publishable component packages.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "vue",
9
+ "vue3",
10
+ "monorepo",
11
+ "pnpm",
12
+ "workspace",
13
+ "cli",
14
+ "scaffold",
15
+ "create",
16
+ "vite",
17
+ "typescript",
18
+ "component-library",
19
+ "template"
20
+ ],
21
+ "bin": {
22
+ "create-vue-workspace": "dist/index.mjs",
23
+ "cvw": "dist/index.mjs"
24
+ },
25
+ "files": [
26
+ "dist/index.mjs",
27
+ "templates"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "dependencies": {
36
+ "@clack/prompts": "^1.8.0",
37
+ "cac": "^7.0.0",
38
+ "execa": "^10.0.1",
39
+ "picocolors": "^1.1.1"
40
+ }
41
+ }
@@ -0,0 +1,2 @@
1
+ VITE_APP_TITLE={{projectName}}
2
+ VITE_API_BASE_URL=/api
@@ -0,0 +1,2 @@
1
+ VITE_APP_TITLE={{projectName}}
2
+ VITE_API_BASE_URL=/api
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{projectName}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "{{scope}}/{{appDirName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vue-tsc --noEmit && vite build",
9
+ "preview": "vite preview",
10
+ "typecheck": "vue-tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "pinia": "^2.3.0",
14
+ "vue": "^3.5.13",
15
+ "vue-router": "^4.5.0"{{workspaceDependencies}}{{farrisDependency}}
16
+ },
17
+ "devDependencies": {
18
+ "@vitejs/plugin-vue": "^5.2.1",
19
+ "@vitejs/plugin-vue-jsx": "^4.1.1",
20
+ "sass": "^1.83.0",
21
+ "typescript": "^5.7.2",
22
+ "vite": "^6.0.7",
23
+ "vue-tsc": "^2.2.0"
24
+ }
25
+ }
@@ -0,0 +1,18 @@
1
+ <script setup lang="ts">
2
+ import { useAppStore } from './stores/app';
3
+
4
+ const appStore = useAppStore();
5
+ </script>
6
+
7
+ <template>
8
+ <main class="app">
9
+ <h1>{{ appStore.title }}</h1>
10
+ <router-view />
11
+ </main>
12
+ </template>
13
+
14
+ <style scoped>
15
+ .app {
16
+ padding: 32px;
17
+ }
18
+ </style>
@@ -0,0 +1,15 @@
1
+ /** 请求封装占位实现,按后端约定替换 baseURL、鉴权与拦截逻辑 */
2
+ const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? '/api';
3
+
4
+ export async function request<T>(url: string, init?: RequestInit): Promise<T> {
5
+ const response = await fetch(`${BASE_URL}${url}`, {
6
+ headers: { 'Content-Type': 'application/json' },
7
+ ...init,
8
+ });
9
+
10
+ if (!response.ok) {
11
+ throw new Error(`请求失败:${response.status} ${response.statusText}`);
12
+ }
13
+
14
+ return (await response.json()) as T;
15
+ }
@@ -0,0 +1,7 @@
1
+ import { createPinia } from 'pinia';
2
+ import { createApp } from 'vue';
3
+ import App from './App.vue';
4
+ import router from './router';
5
+ import './styles/index.scss';
6
+
7
+ createApp(App).use(createPinia()).use(router).mount('#app');
@@ -0,0 +1,16 @@
1
+ import { createRouter, createWebHistory } from 'vue-router';
2
+ import HomeView from '../views/home-view.vue';
3
+
4
+ const router = createRouter({
5
+ history: createWebHistory(import.meta.env.BASE_URL),
6
+ routes: [
7
+ {
8
+ path: '/',
9
+ name: 'home',
10
+ component: HomeView,
11
+ },
12
+ // cvw:routes —— 执行 `cvw g v <name>` 时,路由会插入到这一行之前
13
+ ],
14
+ });
15
+
16
+ export default router;
@@ -0,0 +1,7 @@
1
+ import { defineStore } from 'pinia';
2
+
3
+ export const useAppStore = defineStore('app', {
4
+ state: () => ({
5
+ title: '{{projectName}}',
6
+ }),
7
+ });
@@ -0,0 +1,8 @@
1
+ :root {
2
+ color-scheme: light;
3
+ }
4
+
5
+ body {
6
+ margin: 0;
7
+ font-family: system-ui, -apple-system, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
8
+ }
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ const stack = ['Vue 3', 'Vite', 'TypeScript', 'pnpm workspace'];
3
+ </script>
4
+
5
+ <template>
6
+ <section class="home">
7
+ <p>技术栈:{{ stack.join(' / ') }}</p>
8
+ </section>
9
+ </template>
10
+
11
+ <style scoped>
12
+ .home {
13
+ margin-top: 12px;
14
+ color: #555;
15
+ }
16
+ </style>
@@ -0,0 +1,12 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ interface ImportMetaEnv {
4
+ /** 应用标题 */
5
+ readonly VITE_APP_TITLE?: string;
6
+ /** 接口基地址 */
7
+ readonly VITE_API_BASE_URL?: string;
8
+ }
9
+
10
+ interface ImportMeta {
11
+ readonly env: ImportMetaEnv;
12
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "types": ["vite/client"]
5
+ },
6
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "vite.config.ts"]
7
+ }
@@ -0,0 +1,17 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import vue from '@vitejs/plugin-vue';
3
+ import vueJsx from '@vitejs/plugin-vue-jsx';
4
+ import { defineConfig } from 'vite';
5
+
6
+ export default defineConfig({
7
+ plugins: [vue(), vueJsx()],
8
+ resolve: {
9
+ // 直连 packages 源码:改组件库无需重新构建,热更新即时生效
10
+ alias: {
11
+ {{workspaceAliases}}
12
+ },
13
+ },
14
+ server: {
15
+ port: {{port}},
16
+ },
17
+ });
@@ -0,0 +1,53 @@
1
+ # {{projectName}}
2
+
3
+ 由 `create-vue-workspace` 生成的 Vue 3 pnpm monorepo 工程。
4
+
5
+ ## 目录结构
6
+
7
+ ```
8
+ {{projectName}}/
9
+ ├── apps/
10
+ │ └── {{appDirName}}/ # 主应用(永不发包)
11
+ ├── packages/ # 可复用、可发布的库与组件包
12
+ └── pnpm-workspace.yaml
13
+ ```
14
+
15
+ ## 开发
16
+
17
+ ```bash
18
+ pnpm install
19
+ pnpm dev # 启动 apps/{{appDirName}}
20
+ pnpm build # 构建 packages 与 apps
21
+ pnpm test # 运行单元测试
22
+ pnpm typecheck # 全仓类型检查
23
+ ```
24
+
25
+ ## 生成代码
26
+
27
+ ```bash
28
+ cvw g c button # 独立组件包,自动接线 packages/ui(依赖 + re-export)
29
+ cvw g pkg hooks --vue # 通用包
30
+ cvw g h theme --pkg hooks # composable
31
+ cvw g u array # 工具函数模块
32
+ cvw g v user/list # 页面并自动注册路由
33
+ cvw g app admin --port 5174
34
+
35
+ pnpm install # 新增 package 后注册 workspace
36
+ ```
37
+
38
+ ## 版本管理与发布
39
+
40
+ ```bash
41
+ pnpm changeset # 记录变更
42
+ pnpm version-packages # 升版本、生成 CHANGELOG
43
+ pnpm release # 构建并入发布
44
+ ```
45
+
46
+ `apps/*` 已在 `.changeset/config.json` 中标记为 ignore,不会参与发布。
47
+
48
+ ## 开发约定
49
+
50
+ - 内部包引用一律使用 `workspace:*`,不要写死版本号。
51
+ - 应用通过 Vite alias 直连 `packages/*/src` 源码,改库无需重新构建。
52
+ - 依赖方向单向:`utils -> 组件包 -> ui -> apps`,禁止反向依赖。
53
+ - 组件遵循《Farris UI Vue 组件开发规范》:`F` 前缀组件名、`f-` 前缀选择器、TSX + `defineComponent`。
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
3
+ "changelog": "@changesets/cli/changelog",
4
+ "commit": false,
5
+ "fixed": [],
6
+ "linked": [],
7
+ "access": "public",
8
+ "baseBranch": "main",
9
+ "updateInternalDependencies": "patch",
10
+ "ignore": ["{{scope}}/{{appDirName}}"]
11
+ }
@@ -0,0 +1,12 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ indent_style = space
6
+ indent_size = 2
7
+ end_of_line = lf
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.md]
12
+ trim_trailing_whitespace = false
@@ -0,0 +1,12 @@
1
+ node_modules
2
+ dist
3
+ coverage
4
+ *.log
5
+ .DS_Store
6
+ .idea
7
+ .vscode/*
8
+ !.vscode/extensions.json
9
+ .env.local
10
+ .env.*.local
11
+ stats.html
12
+ *.tsbuildinfo
@@ -0,0 +1,25 @@
1
+ import js from '@eslint/js';
2
+ import vue from 'eslint-plugin-vue';
3
+ import tseslint from 'typescript-eslint';
4
+
5
+ export default tseslint.config(
6
+ {
7
+ ignores: ['**/dist/**', '**/node_modules/**', '**/coverage/**'],
8
+ },
9
+ js.configs.recommended,
10
+ ...tseslint.configs.recommended,
11
+ ...vue.configs['flat/recommended'],
12
+ {
13
+ files: ['**/*.vue'],
14
+ languageOptions: {
15
+ parserOptions: {
16
+ parser: tseslint.parser,
17
+ },
18
+ },
19
+ },
20
+ {
21
+ rules: {
22
+ 'vue/multi-word-component-names': 'off',
23
+ },
24
+ },
25
+ );
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "packageManager": "pnpm@10.6.2",
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
10
+ "scripts": {
11
+ "dev": "pnpm --filter {{scope}}/{{appDirName}} dev",
12
+ "build": "pnpm build:packages && pnpm --filter {{scope}}/{{appDirName}} build",
13
+ "build:packages": "pnpm -r --filter \"./packages/*\" build",
14
+ "typecheck": "pnpm -r typecheck",
15
+ "lint": "eslint .",
16
+ "lint:style": "stylelint \"**/*.{css,scss,vue}\"",
17
+ "format": "prettier --write .",
18
+ "test": "vitest run",
19
+ "changeset": "changeset",
20
+ "version-packages": "changeset version",
21
+ "release": "pnpm build:packages && changeset publish"
22
+ },
23
+ "devDependencies": {
24
+ "@changesets/cli": "^2.27.11",
25
+ "@eslint/js": "^9.17.0",
26
+ "@vitejs/plugin-vue-jsx": "^4.1.1",
27
+ "@vue/test-utils": "^2.4.6",
28
+ "eslint": "^9.17.0",
29
+ "eslint-plugin-vue": "^9.32.0",
30
+ "jsdom": "^25.0.1",
31
+ "prettier": "^3.4.2",
32
+ "stylelint": "^16.12.0",
33
+ "stylelint-config-standard-scss": "^14.0.0",
34
+ "typescript": "^5.7.2",
35
+ "typescript-eslint": "^8.18.2",
36
+ "vitest": "^2.1.8"
37
+ }
38
+ }
@@ -0,0 +1,3 @@
1
+ packages:
2
+ - 'apps/*'
3
+ - 'packages/*'
@@ -0,0 +1,8 @@
1
+ /** @type {import('prettier').Config} */
2
+ export default {
3
+ printWidth: 120,
4
+ singleQuote: true,
5
+ semi: true,
6
+ trailingComma: 'all',
7
+ arrowParens: 'always',
8
+ };
@@ -0,0 +1,8 @@
1
+ /** @type {import('stylelint').Config} */
2
+ export default {
3
+ extends: ['stylelint-config-standard-scss'],
4
+ ignoreFiles: ['**/dist/**', '**/node_modules/**', '**/coverage/**'],
5
+ rules: {
6
+ 'selector-class-pattern': null,
7
+ },
8
+ };
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "strict": true,
8
+ "jsx": "preserve",
9
+ "jsxImportSource": "vue",
10
+ "esModuleInterop": true,
11
+ "resolveJsonModule": true,
12
+ "skipLibCheck": true,
13
+ "isolatedModules": true,
14
+ "useDefineForClassFields": true,
15
+ "noEmit": true
16
+ }
17
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "./tsconfig.base.json",
3
+ "files": []
4
+ }
@@ -0,0 +1,10 @@
1
+ import vueJsx from '@vitejs/plugin-vue-jsx';
2
+ import { defineConfig } from 'vitest/config';
3
+
4
+ export default defineConfig({
5
+ plugins: [vueJsx()],
6
+ test: {
7
+ environment: 'jsdom',
8
+ include: ['packages/**/__tests__/**/*.spec.{ts,tsx}'],
9
+ },
10
+ });
@@ -0,0 +1,17 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { describe, expect, it } from 'vitest';
3
+ import {{pascalName}} from '../{{fileName}}.component';
4
+
5
+ describe('{{selector}}', () => {
6
+ it('默认渲染根节点', () => {
7
+ const wrapper = mount({{pascalName}});
8
+
9
+ expect(wrapper.find('.{{selector}}').exists()).toBe(true);
10
+ });
11
+
12
+ it('禁用时追加禁用样式', () => {
13
+ const wrapper = mount({{pascalName}}, { props: { disabled: true } });
14
+
15
+ expect(wrapper.find('.{{selector}}-disabled').exists()).toBe(true);
16
+ });
17
+ });
@@ -0,0 +1,9 @@
1
+ import { defineComponent } from 'vue';
2
+
3
+ /** {{pascalName}} 的子组件占位实现 */
4
+ export default defineComponent({
5
+ name: '{{componentName}}Sub',
6
+ setup() {
7
+ return () => <span class="{{selector}}-sub" />;
8
+ },
9
+ });
@@ -0,0 +1,46 @@
1
+ import { computed, type ComputedRef, type Ref, type SetupContext } from 'vue';
2
+ import type { {{propsTypeName}} } from '../{{fileName}}.props';
3
+
4
+ export interface Use{{pascalName}} {
5
+ /** 组件根节点样式 */
6
+ {{camelName}}Class: ComputedRef<Record<string, boolean>>;
7
+ /** 是否展示内容 */
8
+ shouldShowContent: ComputedRef<boolean>;
9
+ /** 点击处理 */
10
+ onClick: () => void;
11
+ }
12
+
13
+ /** {{pascalName}} 组件的可复用逻辑 */
14
+ export function use{{pascalName}}Composition(
15
+ props: {{propsTypeName}},
16
+ context: SetupContext,
17
+ currentValue: Ref<string>,
18
+ ): Use{{pascalName}} {
19
+ const {{camelName}}Class = computed(() => {
20
+ const classObject = {
21
+ '{{selector}}': true,
22
+ '{{selector}}-disabled': props.disabled,
23
+ } as Record<string, boolean>;
24
+
25
+ props.customClass
26
+ .split(' ')
27
+ .filter(Boolean)
28
+ .forEach((className: string) => {
29
+ classObject[className] = true;
30
+ });
31
+
32
+ return classObject;
33
+ });
34
+
35
+ const shouldShowContent = computed(() => !props.disabled);
36
+
37
+ function onClick(): void {
38
+ if (props.disabled) {
39
+ return;
40
+ }
41
+
42
+ context.emit('update:modelValue', currentValue.value);
43
+ }
44
+
45
+ return { {{camelName}}Class, onClick, shouldShowContent };
46
+ }
@@ -0,0 +1,12 @@
1
+ import type { App } from 'vue';
2
+ import {{pascalName}} from './{{fileName}}.component';
3
+
4
+ export * from './{{fileName}}.props';
5
+
6
+ export { {{pascalName}} };
7
+
8
+ export default {
9
+ install(app: App): void {
10
+ app.component({{pascalName}}.name ?? '{{componentName}}', {{pascalName}});
11
+ },
12
+ };
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "sideEffects": ["**/*.css", "**/*.scss"],
6
+ "main": "./dist/index.mjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs"
13
+ },
14
+ "./style": "./dist/style.css"
15
+ },
16
+ "files": ["dist"],
17
+ "scripts": {
18
+ "build": "vite build",
19
+ "typecheck": "vue-tsc --noEmit"
20
+ },
21
+ "peerDependencies": {
22
+ "vue": "^3.5.0"
23
+ },
24
+ "devDependencies": {
25
+ "@vitejs/plugin-vue-jsx": "^4.1.1",
26
+ "sass": "^1.83.0",
27
+ "vite": "^6.0.7",
28
+ "vite-plugin-dts": "^4.4.0",
29
+ "vue-tsc": "^2.2.0"
30
+ }
31
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "exclude": ["dist", "node_modules"]
5
+ }
@@ -0,0 +1,5 @@
1
+ /** {{pascalName}} 组件私有类型 */
2
+ export interface {{pascalName}}Instance {
3
+ /** 实例标识 */
4
+ id: string;
5
+ }
@@ -0,0 +1,17 @@
1
+ import vueJsx from '@vitejs/plugin-vue-jsx';
2
+ import { defineConfig } from 'vite';
3
+ import dts from 'vite-plugin-dts';
4
+
5
+ export default defineConfig({
6
+ plugins: [vueJsx(), dts({ rollupTypes: true })],
7
+ build: {
8
+ lib: {
9
+ entry: 'index.ts',
10
+ formats: ['es'],
11
+ fileName: () => 'index.mjs',
12
+ },
13
+ rollupOptions: {
14
+ external: ['vue'],
15
+ },
16
+ },
17
+ });
@@ -0,0 +1,22 @@
1
+ import { defineComponent, ref, type SetupContext } from 'vue';
2
+ import { {{propsName}}, {{propsTypeName}} } from './{{fileName}}.props';
3
+ import { use{{pascalName}}Composition } from './composition/use-{{fileName}}';
4
+
5
+ import './{{fileName}}.scss';
6
+
7
+ export default defineComponent({
8
+ name: '{{componentName}}',
9
+ props: {{propsName}},
10
+ emits: ['update:modelValue'] as (string[] & ThisType<void>) | undefined,
11
+ setup(props: {{propsTypeName}}, context: SetupContext) {
12
+ const currentValue = ref(props.modelValue);
13
+ const use{{pascalName}}CompositionResult = use{{pascalName}}Composition(props, context, currentValue);
14
+ const { {{camelName}}Class, onClick, shouldShowContent } = use{{pascalName}}CompositionResult;
15
+
16
+ return () => (
17
+ <div class={ {{camelName}}Class.value } onClick={onClick}>
18
+ {shouldShowContent.value ? currentValue.value : null}
19
+ </div>
20
+ );
21
+ },
22
+ });
@@ -0,0 +1,12 @@
1
+ import type { ExtractPropTypes, PropType } from 'vue';
2
+
3
+ export const {{propsName}} = {
4
+ /** 自定义 CLASS */
5
+ customClass: { type: String as PropType<string>, default: '' },
6
+ /** 是否禁用 */
7
+ disabled: { type: Boolean as PropType<boolean>, default: false },
8
+ /** 组件值 */
9
+ modelValue: { type: String as PropType<string>, default: '' },
10
+ } as Record<string, any>;
11
+
12
+ export type {{propsTypeName}} = ExtractPropTypes<typeof {{propsName}}>;
@@ -0,0 +1,11 @@
1
+ .{{selector}} {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ padding: 0 12px;
5
+ border-radius: var(--f-border-radius, 4px);
6
+
7
+ &-disabled {
8
+ cursor: not-allowed;
9
+ opacity: 0.5;
10
+ }
11
+ }