npm package: @kscommon/qz-ui
Description: qz-ui 是一个采用 vue3 语法编写，基于 [element-plus](https://element-plus.org/zh-CN/) 进行二次封装的低代码组件库
Latest version: 1.2.2

## 介绍
qz-ui 是一个采用 vue3 语法编写，基于 [element-plus](https://element-plus.org/zh-CN/) 进行二次封装的低代码组件库

## 命令及介绍
```bash
安装依赖
npm i

新建一个组件，只需控制台输入相关组件信息， 会根据该信息自动创建组件相关文件
npm run gen

进行开发组件，packages 下编写代码， 并实时预览
npm run dev

进行组件库打包
npm run build:lib

打包并自动修改包版本号后发布到npm
npm run deploy

进行组件库打包并输出打包分析
npm run build:r

组件文档打包 可部署到静态网站
npm run build:doc

更新组件信息, 自动生成组件配置文件、重新生成路由、配置等信息
npm run cfg

生成每个组件的配置文件
npm run build:compCfg
```

## 组件开发：
#### Q: 如何进行一个新的组件开发？
##### 1 安装依赖后运行 npm run gen; 会生成组件相关文件; 以 qz-button 为例, enter确认
> PS D:\work_new\dev\resources\qz-ui-components> npm run gen<br>

输入我们的组件名， 一般以Qz开头

>? 请输入你要新建的组件名（纯英文，大写开头）：QzButton

这里会包含很多组件类型， 这里我们选择 Basic

>? 请选择新建的组件类型? (Use arrow keys)<br>
>\> { "type": "Basic", "desc": "基础" }<br>
>   { "type": "Layout", "desc": "布局" }<br>
>   { "type": "Form", "desc": "表单" }<br>
>   { "type": "Data", "desc": "数据展示" }<br>
>   { "type": "Navigation", "desc": "导航" }<br>
>   { "type": "Feedback", "desc": "操作反馈" }<br>
>   { "type": "Others", "desc": "其他" }<br>
>(Move up and down to reveal more choices)<br>

输入我们的组件中文名

>? 请输入你要新建的组件名（中文）： 按钮<br>

输入我们的组件功能描述

>? 请输入组件的功能描述： 这个是我的自定义按钮<br>

输入组件作者

>? 请输入组件作者： xks<br>

组件创建成功会进行提示，接下来就是组件开发了

>组件新建完毕，请前往 packages/QzButton 目录进行开发<br>
>创建QzButton组件配置文件cfg成功！<br>
##### 2 接下来在packages/QzButton/src/index.vue 编写组件代码进行开发； 由于使用了自动导入， vue相关的api可以直接使用无需导入；  在模板中使用类名 qz-button qz-ui-custom-component以防止样式污染； props在cfg.js中定义

```vue
<script setup>
import { ElButton } from 'element-plus';
// import { useAppCore } from 'shared'; // 导入AppCore 为了运行态与组件建立联系 一般不需要此操作
// import { ref, computed } from 'vue'; // 自动导入 不需要此操作
import { getCfg } from '../cfg.js'; // 导入组件属性
const props = defineProps(getCfg().props); // 使用组件属性
defineOptions({ // 定义组件配置
    name: 'QzButton',
    inheritAttrs: false, // 是否继承属性 配置json与按钮某些属性校验可能存在冲突
});
const emit = defineEmits(['click']); // 定义emit
const handleClick = (...args) => {
    emit('click', ...args);
};
// useAppCore({ id: props?.config?.id, type: 'Button', context: { click: handleClick }, pageId: props.pageId }); // 使用AppCore 传入组件自定义context供外部使用
</script>

<template>
    <el-button type="primary" v-bind="props" class="qz-button qz-ui-custom-component" @click="handleClick">
        <slot>{{ props.label }}</slot>
        <template v-for="slot in Object.keys($slots)" #[slot]="scope" :key="slot">
            <template v-if="slot !== 'default'">
                <slot v-bind="scope" :key="slot" :name="slot" />
            </template>
            <template v-else>
                <slot v-bind="scope" :key="slot" :name="slot">{{ props.label }}</slot>
            </template>
        </template>
    </el-button>
</template>

```

##### 让我们看看cfg.js 文件中的内容
```js
import { transformCfg } from 'utils/getCompCfg.js';

export const getQzButtonCfg = () => {
    return {
        props: [ // props在这里定义， 配置态和组件需要使用
            {
                label: '文本', // key 说明label
                value: 'label', // 使用的字段key
                verifyType: 'string', // 校验类型
                defaultValue: '按钮', // 默认值，如果不需要不写此项
                display: true,
            },
            {
                label: '圆形按钮',
                value: 'circle',
                verifyType: 'boolean',
                defaultValue: false,
                type: 'switch',
                display: false, // 不需要展示该项到配置态 但是组件内部需要使用
            },
        ],
        events: [ // events在这里定义， 配置态需要使用
            {
                label: '点击',
                value: 'onClick',
            },
        ],
        methods: [ // methods在这里定义， 配置态需要使用
            {
                label: '点击',
                value: 'click',
            },
        ],
    };
};
export const getCfg = () => {
    return transformCfg(getQzButtonCfg()); // 将上述配置转换为组件可以使用的props
};
```

##### 3 样式编写在packages/QzButton/src/index.scss中； 将组件样式与组件分隔开， 以及后续自定义主题样式

```scss
.qz-button.qz-ui-custom-component {
    --qz-button-custom-xxx: red; 
    color: var(--qz-button-custom-xxx)
    // 在这里编写你的样式代码 直接使用在 variables.scss 文件中定义的scss变量 及css 变量; 可在组件内部及全局variables.scss中定义
}
```

#### Q： 如何实时查看效果？ 
在packages/QzButton/doc 目录编写 demo.vue组件， 可编写多个demo 组件， 推荐命名以 base.demo.vue、xxx.demo.vue命名;
在 packages/QzButton/doc/README.md 引入你的 xxx.demo.vue 组件 Preview 组件包裹

```html
// README.md
<script setup>
// 在这里导入你的组件demo
import demo from './demo.vue'
import baseDemo from './base.demo.vue'
</script>


<Preview comp-name="QzButton" demo-name="demo">
  <demo />
</Preview>

<Preview comp-name="QzButton" demo-name="base.demo">
  <base-demo />
</Preview>

```
#### Q： 组件创建时信息输入错误怎么办？ 手动删除路由和导入？
因为此时路由和相关文件导入等配置已经生成，手动删除可能存在问题; 如果你此时还没有进行组件开发， 只需要在packages/list.json中删除组件对应的对象信息
```js
// packages/list.json
[
  { // 删除这个对象
    "compName": "QzButton",
    "compType": "{ \"type\": \"Basic\", \"desc\": \"基础\" }",
    "compZhName": "按钮",
    "compDesc": "按钮",
    "compAuthor": "xks",
    "compClassName": "qz-button"
  },
  ... // 省略其他项
```

然后运行命令 npm run cfg 即可更新路由及各文件的依赖导入导出信息， 无需其他操作
```bash
PS D:\work_new\dev\resources\qz-ui-components> npm run cfg

> @kscommon/qz-ui@1.1.9 cfg D:\work_new\dev\resources\qz-ui-components
> node ./script/genNewComp/index.js update

更新文件成功！
```

## 文件及目录介绍：
<span style="display:none" id="组件介绍"></span>

#### 目录介绍：

```bash
build
├──base.config.js         // 基础打包配置
├──doc.config.js         // 文档打包配置
├──lib.config.js         // 库打包配置
├──lib.css.js         // 样式打包配置
├──lib.disperse.js    // 组件块打包配置
dist                  // 组件打包输出目录
docs                  // 组件文档目录
packages              // 组件包目录
├── locale            // 国际化
├── shared            // 公共模块
├── utils             // 工具函数等
├── styles            // 主题样式
├── Button            // 组件
│   ├── docs
│   │   ├── README.md  // 组件文档
│   │   └── demo.vue   // 交互式预览实例
│   ├── index.js       // 模块导出文件
│   └── src
│       └── index.vue  // 组件本体
├── index.js           // 组件库导出文件
├── listCfgs.js           // 组件库组件props导出
├── listOptions.js        // 配置态配置文件
└── list.json          // 组件列表信息
play                   // 项目测试目录play
script                 // 脚本
├── genNewComp            // 新建组件的模板及脚本文件
├── compCfg.build.js       // 打包组件cfg(props)脚本
├── deploy.js             // 修改版本号并发布到npm脚本
├── genCss.js            // 拷贝及打包样式导出样式

```

---
## 使用
全局使用
```js
import { createApp } from 'vue'
import App from './App.vue'

import QzUI from '@kscommon/qz-ui';
import 'qz-ui/dist/styles/themes/default.scss';

createApp(App).use(QzUI).mount('#app')

```

在项目中按需使用
```js
import { QzButton } from 'qz-ui/dist/packages/es/QzButton'; // 或者 import { QzButton } from '@kscommon/qz-ui';
```

使用组件库中的utils、shared等
```js
import { useLocale, vQzDragSortList, taskController } from 'qz-ui/dist/packages/es/shared';
import { useLocale, unique, EventEmitter, isObject, isFunction } from 'qz-ui/dist/packages/es/utils';
import { Locale } from 'qz-ui/dist/packages/es/locale';
```
## 主题定制
qz-ui 使用 scss 定义了一套默认样式变量 default.scss, 定制主题就是编辑这个变量列表配置文件 variables.scss; 

##### 第一步：
```js
import 'element-plus/theme-chalk/src/index.scss'; // 依赖element-plus包组件样式 mian.js
// 全局使用时 需要引入themes/index.scss 的scss文件 而不能使用已编译的style.css文件
import QzUI from '@kscommon/qz-ui';
import 'qz-ui/dist/packages/styles/themes/index.scss' // import 'QzUI/dist/style.css';
``` 
```js
// 局部使用时 需要引入themes/index.scss 的scss文件 而不能使用已编译的style.css文件
import { QzButton } from 'qz-ui/dist/packages/es/QzButton';
// 或者 import { QzButton } from '@kscommon/qz-ui';
import 'qz-ui/dist/packages/QzButton/index.scss';
// import 'qz-ui/dist/styles/themes/index.scss' // 所有样式
```
##### 第二步：

项目中新建 vars.scss 文件, vite.config.js中配置该用户自定义的scss变量文件

```scss
// 项目中主题自定义样式 修改主题 可覆盖 qi-ui 样式变量
$bg: #fff;
$c: blue;
$base: #130909;

// 在此修改element-plus 主题
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  $colors: (
    'primary': (
      'base': $base,
    ),
  ),
);

```


```js
// vite.config.js
export default defineConfig({
    // 其他配置
    css: {
        preprocessorOptions: {
            scss: {
                additionalData: `@use "xxxxxx/vars.scss" as *;`
            }
        }
    }
})
```

```js
// webpack 配置
{
    test: /\.(sa|sc)ss$/,
    use: [
        {
            loader: 'sass-loader',
            options: {
                data:  `@import "xxxxxx/vars.scss";`,
            }
        }
    ]
}
```
#### 使用css变量 (推荐)
在variables.scss 中编写css 变量，在组件的index.scss中直接var(--qz-color-xxx)使用

具体方法查看头部(src/App.vue中useVarCss)


## 国际化
### 组件库目录介绍
```bash
// 组件库国际化目录
locale
├── lang
│   ├── plus
│   │   ├── en.js  // 导出 element-plus 语言包
│   │   └── zh-cn.js   // 导出 element-plus 语言包
│   ├── zh-CN.js       // 组件库中文语言包
│   └── en-US.js       // 组件库英文语言包
├── index.js           // 导出文件
```

### 项目中使用
##### 先在项目中新建以下文件结构及目录
```bash
// 项目中国际化目录
locale
├── lang
│   ├── zh-CN.js       // 项目中文语言包
│   └── en-US.js       // 项目中英文语言包
├── index.js           // 导出文件
```

```js
// en-US.js
class Lang {
    save = 'Save';
    confirm = 'Confirm';
    cancel = 'Cancel';
    userSetting = {
        'personal-document': 'personal document',
        'modify-password': 'modify password',
        'add-user': 'add user',
    // 省略其他代码

```
```js
// zh-CN.js
class Lang {
    save = '保存';
    confirm = '确认';
    cancel = '取消';
    userSetting = {
        'personal-document': '个人档案',
        'modify-password': '更改密码',
        'add-user': '新增用户',
    // 省略其他代码
```

##### 接着 App.vue中使用 element-plus 国际化
```vue
<script setup>
import { ElConfigProvider } from 'qz-ui';
import { Locale } from 'locale'; // 项目中的国际化
import zhCn from 'qz-ui/dist/packages/es/zh-cn'; // 这里导入element-plus国际化
import en from 'qz-ui/dist/packages/es/en';

const langs = {
    'zh-CN': zhCn.default,
    'en-US': en.default,
};
const cpLang = computed(() => {
    return langs[Locale.currentLang.value];
});
</script>

<template>
    <el-config-provider :locale="cpLang">
        <div id="app">
            <router-view v-slot="{ Component }">
                <Suspense>
                    <div>
                        <component :is="Component" />
                    </div>
                </Suspense>
            </router-view>
        </div>
    </el-config-provider>
</template>

```

##### 接着合并组件库和项目中的语言及导出组件库中的方法
```js
/* locale/index.js */
import ZhCNLang from 'locale/lang/zh-CN'; // 项目中的中文语言包
import EnUSLang from 'locale/lang/en-US'; // 项目中的英文语言包
import { Locale as QzLocale } from 'qz-ui/dist/packages/es/locale'; // 导入组件库中的语言类
import { useLocale as QzUseLocale } from 'qz-ui/dist/packages/es/shared'; // 导入组件库中使用切换语言方法

// 项目语言集合
export const langs = reactive({
    'zh-CN': new ZhCNLang(),
    'en-US': new EnUSLang(),
});

/* 合并组件库和项目中的语言包 */
Object.keys(langs).forEach(lang => {
    QzLocale.merge(lang, langs[lang]);
});

export const Locale = QzLocale;

export const useLocale = QzUseLocale;

export default Locale;

```

##### 组件中使用
```vue
<script setup>
import { useLocale } from 'locale/index.js'; // 导入上面的js
const { t, setLang, currentLang } = useLocale('userSetting.'); // userSetting. 取userSetting对象下的key
// setLang('en-US') // 切换到英文
</script>

<template>
    <div><span>{{ t('modify-password') }}</span></div>
</template>

```