# PIGX 前端技术文档汇总

> **更新日期**: 2025-10-23  
> **适配版本**: 5.0 以上单体和微服务版本

---

## 目录

- [一、环境配置](#一环境配置)
  - [1.1 ENV 环境配置](#11-env环境配置)
  - [1.2 前端配置项说明](#12-前端配置项说明)
- [二、项目结构](#二项目结构)
  - [2.1 目录结构](#21-目录结构)
- [三、核心功能](#三核心功能)
  - [3.1 国际化配置](#31-国际化配置)
  - [3.2 字体图标配置](#32-字体图标配置)
  - [3.3 权限管理](#33-权限管理)
  - [3.4 路由管理](#34-路由管理)
  - [3.5 标签页管理](#35-标签页管理)
  - [3.6 数据状态管理](#36-数据状态管理)
- [四、工具函数](#四工具函数)
  - [4.1 字典工具使用](#41-字典工具使用)
  - [4.2 参数工具使用](#42-参数工具使用)
  - [4.3 通用工具函数](#43-通用工具函数)
- [五、组件使用](#五组件使用)
  - [5.1 表格组件](#51-表格组件)
  - [5.2 表格分页组件](#52-表格分页组件)
  - [5.3 表格工具栏组件](#53-表格工具栏组件)
  - [5.4 上传组件](#54-上传组件)
  - [5.5 富文本组件](#55-富文本组件)
  - [5.6 代码编辑组件](#56-代码编辑组件)
  - [5.7 图表功能](#57-图表功能)
  - [5.8 左侧查询树](#58-左侧查询树)
  - [5.9 组织架构组件](#59-组织架构组件)
  - [5.10 标签列表组件](#510-标签列表组件)
  - [5.11 悬浮输入组件](#511-悬浮输入组件)
  - [5.12 文字提示组件](#512-文字提示组件)
  - [5.13 省市区街四级联动组件](#513-省市区街四级联动组件)
  - [5.14 表单校验](#514-表单校验)
- [六、首页开发](#六首页开发)
  - [6.1 首页组件开发](#61-首页组件开发)

---

## 一、环境配置

### 1.1 ENV 环境配置

| 配置项                          | 值                      | 描述                                                       |
| ------------------------------- | ----------------------- | ---------------------------------------------------------- |
| **VITE_IS_MICRO**               | `true`                  | 是否是微服务架构（重要），因为单体和微服务版本共用一套前端 |
| **VITE_GLOBAL_TITLE**           | `'PIGX ADMIN'`          | 网站主标题（修改需要清空 localstore）                      |
| **VITE_FOOTER_TITLE**           | `'©2024 pig4cloud.com'` | 网站页脚信息（修改需要清空 localstore）                    |
| **VITE_PUBLIC_PATH**            | `/`                     | 前端访问前缀                                               |
| **VITE_API_URL**                | `/api`                  | 后端请求前缀                                               |
| **VITE_ADMIN_PROXY_PATH**       | `http://localhost:9999` | ADMIN 服务地址                                             |
| **VITE_PWD_ENC_KEY**            | `'pigxpigxpigxpigx'`    | 前端加密密钥（16 位）                                      |
| **VITE_OAUTH2_PASSWORD_CLIENT** | `'pig:pig'`             | OAUTH2 密码模式客户端信息                                  |
| **VITE_OAUTH2_MOBILE_CLIENT**   | `'app:app'`             | OAUTH2 短信客户端信息                                      |
| **VITE_OAUTH2_SOCIAL_CLIENT**   | `'social:social'`       | OAUTH2 社交登录客户端信息                                  |
| **VITE_VERIFY_ENABLE**          | `true`                  | 是否开启前端滑块验证码                                     |
| **VITE_VERIFY_IMAGE_ENABLE**    | `false`                 | 是否开启前端图形验证码                                     |
| **VITE_WEBSOCKET_ENABLE**       | `false`                 | 是否开启 WebSocket 消息接收                                |
| **VITE_REGISTER_ENABLE**        | `true`                  | 是否开启注册                                               |
| **VITE_AUTO_TENANT**            | `false`                 | 是否开启租户自动选择（根据租户域名）                       |
| **VITE_I18N_ENABLE**            | `true`                  | 登录页是否开启多语言切换                                   |
| **VITE_DARK_MODE_ENABLE**       | `true`                  | 登录页是否开启暗黑模式切换                                 |
| **VITE_ENABLE_ANTI_DEBUG**      | `false`                 | 是否启用禁用浏览器调试功能（反爬）                         |
| **VITE_ANTI_DEBUG_KEY**         | `pig`                   | 绕过反爬参数值（URL 中 `?ddtk=参数值`）                    |
| **VITE_GRAY_VERSION**           | 无                      | 对应灰度路由的 header Version                              |
| **VITE_API_ENC_ENABLED**        | `false`                 | 是否开启请求加解密                                         |
| **VITE_TENANT_ENABLE**          | `true`                  | 是否开启租户功能（v5.9）                                   |

### 1.2 前端配置项说明

**配置文件**: `src/stores/themeConfig.ts`

> ⚠️ **注意**：
>
> 1. 需要每次都清理 `window.localStorage` 浏览器永久缓存
> 2. 或者点击布局配置最底部 `一键恢复默认` 按钮即可看到效果

#### 主题配置

| 配置项     | 默认值    | 描述                          |
| ---------- | --------- | ----------------------------- |
| `isDrawer` | `false`   | 是否开启布局配置抽屉          |
| `primary`  | `#2E5CF6` | 全局主题默认 primary 主题颜色 |
| `isDark`   | `false`   | 是否开启深色模式              |

#### 顶栏设置

| 配置项                 | 默认值    | 描述                     |
| ---------------------- | --------- | ------------------------ |
| `topBar`               | `#ffffff` | 默认顶栏导航背景颜色     |
| `topBarColor`          | `#606266` | 默认顶栏导航字体颜色     |
| `isTopBarColorGradual` | `false`   | 是否开启顶栏背景颜色渐变 |

#### 菜单设置

| 配置项                  | 默认值         | 描述                     |
| ----------------------- | -------------- | ------------------------ |
| `menuBar`               | `#FFFFFF`      | 默认菜单导航背景颜色     |
| `menuBarColor`          | `#505968`      | 默认菜单导航字体颜色     |
| `menuBarActiveColor`    | `rgba(242...)` | 默认菜单高亮背景色       |
| `isMenuBarColorGradual` | `false`        | 是否开启菜单背景颜色渐变 |

#### 界面设置

| 配置项               | 默认值  | 描述                     |
| -------------------- | ------- | ------------------------ |
| `isCollapse`         | `false` | 是否开启菜单水平折叠效果 |
| `isUniqueOpened`     | `true`  | 是否开启菜单手风琴效果   |
| `isFixedHeader`      | `false` | 是否开启固定 Header      |
| `isClassicSplitMenu` | `false` | 是否开启经典布局分割菜单 |
| `isLockScreen`       | `false` | 是否开启自动锁屏         |
| `lockScreenTime`     | `30`    | 开启自动锁屏倒计时(s/秒) |

#### 界面显示

| 配置项               | 默认值  | 描述                         |
| -------------------- | ------- | ---------------------------- |
| `isShowLogo`         | `true`  | 是否开启侧边栏 Logo          |
| `isBreadcrumb`       | `true`  | 是否开启 Breadcrumb          |
| `isTagsview`         | `true`  | 是否开启 Tagsview            |
| `isBreadcrumbIcon`   | `false` | 是否开启 Breadcrumb 图标     |
| `isTagsviewIcon`     | `false` | 是否开启 Tagsview 图标       |
| `isCacheTagsView`    | `true`  | 是否开启 TagsView 缓存       |
| `isSortableTagsView` | `true`  | 是否开启 TagsView 拖拽       |
| `isShareTagsView`    | `false` | 是否开启 TagsView 共用       |
| `isFooter`           | `true`  | 是否开启 Footer 底部版权信息 |
| `isGrayscale`        | `false` | 是否开启灰色模式             |
| `isInvert`           | `false` | 是否开启色弱模式             |
| `isWartermark`       | `true`  | 是否开启水印                 |
| `wartermarkText`     | `PigX`  | 水印文案                     |

#### 其它设置

| 配置项               | 默认值             | 描述                 |
| -------------------- | ------------------ | -------------------- |
| `tagsStyle`          | `tags-style-five`  | Tagsview 风格        |
| `animation`          | `slide-right`      | 主页面切换动画       |
| `columnsAsideStyle`  | `columns-round`    | 分栏高亮风格         |
| `columnsAsideLayout` | `columns-vertical` | 分栏布局风格         |
| `layout`             | `defaults`         | 布局设置             |
| `isRequestRoutes`    | `true`             | 后端控制路由是否开启 |

---

## 二、项目结构

### 2.1 目录结构

```
├── src/                          # 源代码目录
│   ├── api/                      # API 接口定义
│   ├── assets/                   # 静态资源
│   ├── components/               # 公共组件
│   ├── directive/                # Vue 自定义指令
│   ├── hooks/                    # Vue Composition API hooks
│   ├── i18n/                     # 国际化相关
│   ├── layout/                   # 布局组件
│   ├── locales/                  # 语言包
│   ├── router/                   # Vue Router 配置
│   ├── stores/                   # Pinia 状态管理
│   ├── theme/                    # 主题相关
│   ├── types/                    # TypeScript 类型定义
│   ├── utils/                    # 工具函数
│   ├── views/                    # 页面组件
│   ├── App.vue                   # 根组件
│   └── main.ts                   # 应用入口文件
├── public/                       # 公共静态资源
├── docker/                       # Docker 相关配置
├── index.html                    # HTML 入口文件
├── vite.config.ts                # Vite 配置
├── tsconfig.json                 # TypeScript 配置
├── tailwind.config.js            # Tailwind CSS 配置
├── postcss.config.js             # PostCSS 配置
├── package.json                  # 项目依赖配置
├── .env                          # 环境变量
├── .env.development              # 开发环境变量
├── .env.development.local        # 本地开发环境变量
├── .env.local                    # 本地环境变量
├── .eslintrc.js                  # ESLint 配置
└── .prettierrc.js                # Prettier 配置
```

> ⚠️ **注意**：清空浏览器永久缓存或者点击 `布局配置 -> 一键恢复默认`，前提是修改了 `/src/stores/themeConfig.ts` 配置文件内容。

---

## 三、核心功能

### 3.1 国际化配置

框架内置 `中文简体`、`英文`

#### 多国语言配置

**前端配置国际化**

> 注意：页面国际化数据最好放 `同级的目录中 i18n` 里。`/@/i18n` 为框架内置，为了更好的升级，最好别添加到里面

**后端配置国际化**

与前端 i18n 配置 ts 文件格式相同，i18n 可以通过后台进行国际化管理的配置和使用。

#### 国际化使用

**页面上使用 `.vue`，`$t('xxx.xxx')`**

```vue
<el-button>
  <span>{{ $t('message.account.accountBtnText') }}</span>
</el-button>

<el-divider content-position="left">
  {{ $t('message.layout.oneTitle') }}
</el-divider>
```

**ts 上使用 `.ts`，`i18n.global.t(xxx)`**

```typescript
import { i18n } from '/@/i18n/index';

const webTitle = i18n.global.t(router.currentRoute.value.meta.title as any);
```

**setup 里使用 ，`t(xxx)`**

```typescript
<script lang="ts">
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';

export default defineComponent({
  name: 'xxx',
  setup() {
    const { t } = useI18n();

    // 使用，xxx 为变量
    t(xxx)
  }
})
</script>
```

### 3.2 字体图标配置

#### ElementPlus 图标

使用 element plus 的图标，可去 [https://element-plus.org/zh-CN/component/icon.html](https://element-plus.org/zh-CN/component/icon.html) 复制粘贴

`<el-icon></el-icon>` 包裹着

```vue
<el-input type="text" :placeholder="$t('message.account.accountPlaceholder1')" v-model="ruleForm.userName" clearable autocomplete="off">
  <template #prefix>
    <el-icon class="el-input__icon"><ele-User /></el-icon>
  </template>
</el-input>
```

#### 阿里巴巴在线图标

**1. 创建图标项目**

1. 访问 [iconfont 官网](https://www.iconfont.cn/) 并登录
2. 创建新项目 -> 设置`FontClass/Symbol 前缀`为 `icon`，`Font Family` 为 `iconfont`
3. 添加所需图标到项目 -> 生成在线链接

**2. 设置在线链接**

代码位置：`/@/utils/setIconfont.ts`

```typescript
// `/@/utils/setIconfont.ts` cssCdnUrlList 方法中添加在线链接
// 字体图标 url （新增）
const cssCdnUrlList: Array<string> = [
	'//at.alicdn.com/t/font_2298093_y6u00apwst.css',
	'//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
];
```

**3. 界面中使用**

> ⚠️ **警告**：
>
> - iconfont（阿里巴巴素材库）：需要添加 `iconfont` 前缀，如：`iconfont xitongshezhi`
> - font-awesome：需要添加 `fa` 前缀，如：`fa xitongshezhi`

```vue
<!-- 项目使用 -->
<i class="iconfont xitongshezhi"></i>
<!-- <i class="fa xitongshezhi"></i> -->

<!-- 或者 -->
<SvgIcon name="iconfont xitongshezhi"></SvgIcon>
<!-- <SvgIcon name="fa xitongshezhi"></SvgIcon> -->
```

#### 本地图标

支持下载 svg ，放在前端的 icons 目录

前端可通过 local-文件名的形式加载

```vue
<SvgIcon name="local-gitee" :size="30" />
```

#### 阿里巴巴图标库离线

为了提高系统的稳定性和加载速度，我们可以将原本使用的阿里巴巴在线图标库转换为本地离线使用。

**1. 下载图标资源包**

[点击下载 PIGX-UI 都有依赖的阿里巴巴图标](https://minio.pigx.vip/oss/202501/1737972483.zip)

**2. 创建本地图标目录**

```bash
mkdir -p pigx-ui/public/assets/iconfont
```

**3. 复制资源文件**

将下载的资源包中的以下文件复制到 `public/assets/iconfont/` 目录：

- iconfont.css
- iconfont.ttf
- iconfont.woff
- iconfont.woff2

**4. 修改配置文件**

修改 `src/utils/setIconfont.ts` 文件中的图标引用路径：

```typescript
const cssCdnUrlList: Array<string> = [
	'/assets/iconfont/iconfont.css', // 本地图标文件
	'/assets/styles/font-awesome.min.css',
];
```

### 3.3 权限管理

#### 1. 组件方式

组件位置：`/@/components/auth`

**单个权限验证（:value="xxx"）**

```vue
<template>
	<!-- 使用 -->
	<Auth :value="'btn.add'" />
</template>

<script setup lang="ts" name="xxx">
// 局部引入
import Auth from '/@/components/auth/auth.vue';
</script>
```

**多个权限验证，满足一个则显示（:value="[xxx,xxx]"）**

```vue
<template>
	<!-- 使用 -->
	<Auths :value="['btn.addsss', 'btn.edit', 'btn.delsss', 'btn.linksss']" />
</template>

<script lang="ts">
import { defineComponent } from 'vue';
// 局部引入
import Auths from '/@/components/auth/auths.vue';

export default defineComponent({
	name: 'xxxx',
	// 局部注册
	components: { Auths },
});
</script>
```

**多个权限验证，全部满足则显示（:value="[xxx,xxx]"）**

```vue
<template>
	<!-- 使用 -->
	<AuthAll :value="['btn.add', 'btn.edit', 'btn.del', 'btn.link']" />
</template>

<script lang="ts">
import { defineComponent } from 'vue';
// 局部引入
import AuthAll from '/@/components/auth/authAll.vue';

export default defineComponent({
	name: 'xxxx',
	// 局部注册
	components: { AuthAll },
});
</script>
```

#### 2. 指令方式

指令位置：`/@/directive/authDirective.ts`

**单个权限验证（v-auth="xxx"）**

```vue
<div v-auth="'btn.add'">
  <el-button>新增</el-button>
</div>

<div v-auth="'btn.edit'">
  <el-button>编辑</el-button>
</div>

<div v-auth="'btn.del'">
  <el-button>删除</el-button>
</div>

<div v-auth="'btn.link'">
  <el-button>跳转</el-button>
</div>
```

**多个权限验证，满足一个则显示（v-auths="[xxx,xxx]"）**

```vue
<div v-auths="['btn.addsss', 'btn.edit', 'btn.delsss', 'btn.linksss']">
  <el-button>新增</el-button>
</div>

<div v-auths="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
  <el-button>编辑</el-button>
</div>
```

**多个权限验证，全部满足则显示（v-auth-all="[xxx,xxx]"）**

```vue
<div v-auth-all="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
  <el-button>新增</el-button>
</div>

<div v-auth-all="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
  <el-button>编辑</el-button>
</div>
```

#### 3. 函数方式

方法位置：`/@/utils/authFunction.ts`，用于方法中的判断

```typescript
<script lang="ts" setup>
import { ElMessage } from 'element-plus';
import { auth, auths, authAll } from '/@/utils/authFunction';

// 单个权限验证
const onAuthClick = () => {
  if (!auth('btn.add')) ElMessage.error('抱歉，您没有权限！');
  else ElMessage.success('恭喜，您有权限！');
};

// 多个权限验证，满足一个则为 true
const onAuthsClick = () => {
  if (!auths(['btn.add', 'btn.edit'])) ElMessage.error('抱歉，您没有权限！');
  else ElMessage.success('恭喜，您有权限！');
};

// 多个权限验证，全部满足则为 true
const onAuthAllClick = () => {
  if (!authAll(['btn.add', 'btn.edit'])) ElMessage.error('抱歉，您没有权限！');
  else ElMessage.success('恭喜，您有权限！');
};
</script>
```

或者在模板中使用：

```vue
<div v-if="auth('sys_file_del')">...</div>
```

### 3.4 路由管理

#### 静态路由

静态路由是指直接在前端代码中配置的路由，不需要通过后端接口动态获取。这些路由在项目编译时就已确定，适用于固定不变的页面导航结构。

在 PIGX-UI 中使用静态路由的主要原因：适合配置系统级别的页面（如登录、404 等），可以独立于后端菜单权限系统（不需要鉴权等配置）

**路由配置位置**

```
src/
  └── router/
     └── route.ts          # 路由配置文件
```

**属性配置说明**

| 配置项        | 说明                                                                       |
| ------------- | -------------------------------------------------------------------------- |
| `title`       | 菜单栏及 tagsView 栏、菜单搜索名称（国际化）                               |
| `isLink`      | 是否超链接菜单，开启外链条件：1. isLink: 链接地址不为空 2. isIframe: false |
| `isHide`      | 是否隐藏此路由                                                             |
| `isKeepAlive` | 是否缓存组件状态                                                           |
| `isAuth`      | 是否需要认证才能进入的页面                                                 |
| `isAffix`     | 是否固定在 tagsView 栏上                                                   |
| `isIframe`    | 是否内嵌窗口，开启条件：1. isIframe: true 2. isLink：链接地址不为空        |
| `icon`        | 菜单、tagsView 图标，阿里：加 `iconfont xxx`，fontawesome：加 `fa xxx`     |

#### 动态路由

点击页面上的按钮可以打开新的 Tagsview 标签页，同时也可以传递参数。

**代码实现方式**

**① 菜单管理中定义隐藏菜单**

> ⚠️ 注意为角色分配该菜单的权限。

**② 点击目标按钮触发路由跳转**

```typescript
const click = () => {
	router.push({
		path: '/app/appArticle/form',
		query: { tagsViewName: '动态路由测试', param1: '123' },
	});
};
```

**③ 在跳转的页面获取请求参数**

```typescript
const route = useRoute();

onMounted(() => {
	console.log(route.query.tagsViewName);
	console.log(route.query.param1);
});
```

#### 菜单参数方式

在多个菜单中引用同一个路由下的组件，通过路由进行区分。

**① 配置菜单（带参）**

指向 /admin/test/index 组件

```
AAA /admin/test/1
BBB /admin/test/2
```

**② 页面获取参数**

admin/test/index.vue

```typescript
const route = useRoute();

onMounted(() => {
	// test/index/参数
	const parts = route.path.split('/');
	const lastParam = parts[parts.length - 1];
	console.log(lastParam);
});
```

### 3.5 标签页管理

#### 1. 指定标签页名称

跳转函数中指定路由参数 `tagsViewName`。

```typescript
const router = useRouter();
router.push({ path: '/xxx', query: { tagsViewName: '标签名称' } });
```

#### 2. 刷新当前页面

```typescript
import { useRoute } from 'vue-router';
import mittBus from '/@/utils/mitt';

const route = useRoute();

// 刷新当前页面
const refreshPage = () => {
	mittBus.emit('onCurrentContextmenuClick', {
		contextMenuClickId: 0,
		...route,
	});
};
```

#### 3. 关闭当前页面

```typescript
// 关闭当前页面
const closePage = () => {
	mittBus.emit('onCurrentContextmenuClick', {
		contextMenuClickId: 1,
		...route,
	});
};
```

#### 4. 关闭其它页面

```typescript
// 关闭除当前页外的其它页面
const closeOthers = () => {
	mittBus.emit('onCurrentContextmenuClick', {
		contextMenuClickId: 2,
		...route,
	});
};
```

#### 5. 关闭全部页面

```typescript
// 关闭所有页面
const closeAll = () => {
	mittBus.emit('onCurrentContextmenuClick', {
		contextMenuClickId: 3,
		...route,
	});
};
```

#### 6. 全屏显示

```typescript
// 当前页面全屏显示
const fullscreen = () => {
	mittBus.emit('onCurrentContextmenuClick', {
		contextMenuClickId: 4,
		...route,
	});
};
```

**操作 ID 说明**

| 操作 ID | 功能说明       |
| ------- | -------------- |
| 0       | 刷新当前页面   |
| 1       | 关闭当前页面   |
| 2       | 关闭其它页面   |
| 3       | 关闭全部页面   |
| 4       | 当前页全屏显示 |

### 3.6 数据状态管理

框架中数据状态使用 vuex Module 模块化进行管理，您可能需要了解 [vuex 核心概念 Module](https://next.vuex.vuejs.org/zh/guide/modules.html)

#### pinia

代码位置：`/src/stores`

相关文档：[pinia 官网](https://pinia.vuejs.org/)

相关文档：[vuex 3.x 官网](https://v3.vuex.vuejs.org/zh/)

#### 全局引入

页面模块已做全局自动引入，代码位置：`/@/store/index.ts`。

```typescript
const modulesFiles = import.meta.globEager("./modules/*.ts");
const pathList: string[] = [];

for (const path in modulesFiles) {
  pathList.push(path);
}

const modules = pathList.reduce(
  (modules: { [x: string]: any }, modulePath: string) => {
    const moduleName = modulePath.replace(/^./modules/(.*).w+$/, "$1");
    const value = modulesFiles[modulePath];
    modules[moduleName] = value.default;
    return modules;
  },
  {}
);
```

#### 定义接口

**1. interface 定义**

`/@/store/interface/index.ts`，如：路由缓存列表 `KeepAliveNamesState`

```typescript
// 路由缓存列表
export interface KeepAliveNamesState {
	keepAliveNames: Array<string>;
}
```

**2. interface 使用**

在 `/@/store/modules/` 新增 `keepAliveNames.ts`，界面写入如下代码：

```typescript
import { Module } from 'vuex';
import { KeepAliveNamesState, RootStateTypes } from '/@/store/interface/index';

const keepAliveNamesModule: Module<KeepAliveNamesState, RootStateTypes> = {
	namespaced: true,
	state: {
		keepAliveNames: [],
	},
	mutations: {
		// 设置路由缓存（name字段）
		getCacheKeepAlive(state: any, data: Array<string>) {
			state.keepAliveNames = data;
		},
	},
	actions: {
		// 设置路由缓存（name字段）
		async setCacheKeepAlive({ commit }, data: Array<string>) {
			commit('getCacheKeepAlive', data);
		},
	},
};

export default keepAliveNamesModule;
```

#### 使用模块

**1. 在 .ts 中使用**

```typescript
import { store } from '/@/store/index.ts';

// dispatch
store.dispatch('keepAliveNames/setCacheKeepAlive', cacheList);

// 或者 commit
// store.commit("keepAliveNames/getCacheKeepAlive", cacheList);
```

**2. 在 .vue 中使用**

```vue
<template>
	<div v-if="getThemeConfig.isLockScreen">在 .vue 中使用</div>
</template>

<script lang="ts">
import { computed, defineComponent } from 'vue';
import { useStore } from '/@/store/index';

export default defineComponent({
	name: 'app',
	setup() {
		const store = useStore();
		// 获取布局配置信息
		const getThemeConfig = computed(() => {
			return store.state.themeConfig.themeConfig;
		});
	},
});
</script>
```

---

## 四、工具函数

### 4.1 字典工具使用

#### 1. useDict 函数

字典类型对应 sys_dict 表中字典数据

```typescript
import {useDict} from '/@/hooks/dict';

const {字典类型A, 字典类型B} = useDict('字典类型A'，'字典类型B');
```

> ⚠️ **注意**：element-plus 的 radio 和 select 等类型组件对 value 字段有强制的数据类型要求。实体字段应该使用字符串而不是数字类型来对应字典功能使用。

#### 2. 页面使用 dict-tag

```vue
<el-table-column>
  <template #default="scope">
    <dict-tag :options="字典类型" :value="scope.row.xx"></dict-tag>
  </template>
</el-table-column>
```

### 4.2 参数工具使用

#### 1. useParam 函数

| 入参                                 | 出参                                   |
| ------------------------------------ | -------------------------------------- |
| sys_public_param 表中 publicKey 字段 | sys_public_param 表中 publicValue 字段 |

```typescript
import { useParam } from '/@/hooks/param';
const param = useParam('GEN_TABLE_PREFIX');
```

#### 2. 页面使用

```vue
{{ useParam('GEN_TABLE_PREFIX') }}
```

### 4.3 通用工具函数

#### 点击复制文本

```vue
<button @click="copyText('文案内容')">复制</button>
```

```typescript
const { copyText } = commonFunction();
```

#### 格式化时间

**html 代码中直接使用 parseDate parseTime 进行格式化**

```vue
<el-table-column :label="t('tenant.startTime')" prop="startTime" show-overflow-tooltip>
  <template #default="scope">
    <span>{{ parseDate(scope.row.startTime) }}</span>
  </template>
</el-table-column>

<el-table-column :label="t('tenant.endTime')" prop="endTime" show-overflow-tooltip>
  <template #default="scope">
    <span>{{ parseTime(scope.row.endTime) }}</span>
  </template>
</el-table-column>
```

**ts 中需要手动导入**

```typescript
import { formatDate, parseDate } from '/@/utils/formatTime';
```

#### 获取后端参数

```typescript
import params from '/@/utils/params';

let result = await params.get('参数管理中的KEY');
```

---

## 五、组件使用

### 5.1 表格组件

#### 1. useTable 函数是什么

官方对自定义 hook 定义：在 Vue 应用的概念中，"组合式函数" (Composables) 是一个利用 Vue 组合式 API 来封装和复用有状态逻辑的函数。

**useTable 函数的名称就能清晰地表达其作用，它是集成了前端 element-plus 表格的一些通用函数。**

| 方法名                                                    | 描述                                                                                                                                       |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `tableStyle`                                              | 返回包含表格样式的对象，其中包含 `cellStyle` 和 `headerCellStyle` 两个属性，分别用于设置表格单元格和表头单元格的样式                       |
| `getDataList(refresh?: any)`                              | 获取数据列表，并可选择是否刷新当前页码。如果需要刷新，则将 `state.pagination.current` 重置为 1                                             |
| `sizeChangeHandle(val: number)`                           | 分页大小改变时触发的事件处理函数。会修改 `state.pagination` 中的 `size` 属性，并再次调用 `query()` 方法进行查询                            |
| `currentChangeHandle(val: number)`                        | 当前页码改变时触发的事件处理函数。会修改 `state.pagination` 中的 `current` 属性，并再次调用 `query()` 方法进行查询                         |
| `sortChangeHandle(column: any)`                           | 排序方式改变时触发的事件处理函数。根据 `column` 的内容，修改 `state.descs` 和 `state.ascs` 中的排序字段，并再次调用 `query()` 方法进行查询 |
| `downBlobFile(url: string, query: any, fileName: string)` | 用于下载文件的方法。接收三个参数：文件下载地址 `url`、请求参数 `query` 和文件名 `fileName`。返回一个 Promise 对象，用于异步处理结果        |

#### 2. 页面中如何使用

```typescript
// 使用 useTable 自动生成表格一些通用方法
const { getDataList, currentChangeHandle, sizeChangeHandle, downBlobFile, tableStyle } = useTable(state);
```

#### 3. useTable 初始化 state 如何构建

如果你想通过 useTable 生成一些通用的表格方法，那么就需要向它提供一些初始值，比如查询的 URL 和分页大小等参数。

```typescript
const state: BasicTableProps = reactive<BasicTableProps>({
	queryForm: {},
	pageList: pageList,
});
```

**完整的 BasicTable 定义属性如下**

```typescript
export interface BasicTableProps {
	// 是否在创建页面时即调用数据列表接口，默认为true
	createdIsNeed?: boolean;
	// 是否需要分页，默认为true
	isPage?: boolean;
	// 查询条件表单对象，类型为any
	queryForm?: any;
	// 数据列表数组
	dataList?: any[];
	// 分页属性对象
	pagination?: Pagination;
	// 数据列表，loading状态标志，默认为false
	dataListLoading?: boolean;
	// 数据列表多选项数组
	dataListSelections?: any[];
	// 数据列表查询接口api方法，接收任意数量参数，返回Promise
	pageList?: (...arg: any) => Promise<any>;
	// loading标志，默认为false
	loading?: Boolean;
	// 多选结果数组
	selectObjs?: any[];
	// 排序字段数组
	descs?: string[];
	// 排序方式数组
	ascs?: string[];
	// props属性对象，类型为any
	props?: any;
}
```

### 5.2 表格分页组件

#### 分页组件使用

分页组件一般和表格组件一起使用，只需要回调 useTable hook 中的函数即可

```vue
<pagination v-bind="state.pagination" @current-change="currentChangeHandle" @size-change="sizeChangeHandle"></pagination>
```

```typescript
const { currentChangeHandle, sizeChangeHandle } = useTable(state);
```

#### 属性说明

| 属性        | 类型     | 默认值                                      | 说明                 |
| ----------- | -------- | ------------------------------------------- | -------------------- |
| `current`   | `Number` | `1`                                         | 当前页码             |
| `size`      | `Number` | `10`                                        | 每页展示的条数       |
| `total`     | `Number` | `0`                                         | 数据总数             |
| `pageSizes` | `Array`  | `[1, 10, 20, 50, 100, 200]`                 | 每页展示的条数可选项 |
| `layout`    | `String` | `'total, sizes, prev, pager, next, jumper'` | 分页组件的布局       |

### 5.3 表格工具栏组件

#### 工具栏组件使用

工具栏组件一般和表格组件一起使用，只需要回调 useTable hook 中的函数即可

```vue
<right-toolbar v-model:showSearch="showSearch" :export="'sys_user_export'" @exportExcel="exportExcel" @queryTable="getDataList" />
```

```typescript
const { getDataList } = useTable(state);
```

#### 属性说明

| 属性         | 类型                | 默认值                                                             | 说明             |
| ------------ | ------------------- | ------------------------------------------------------------------ | ---------------- |
| `showSearch` | `Boolean`           | `true`                                                             | 是否显示搜索框   |
| `export`     | `String or Boolean` | `当设置为true的显示，如设置为权限字符串则根据当前用户权限动态控制` | 是否导出下载按钮 |
| `search`     | `Boolean`           | `true`                                                             | 是否显示搜索框   |
| `gutter`     | `Number`            | `10`                                                               | 列表项之间的间距 |

### 5.4 上传组件

#### 普通文件上传

```vue
<upload-file @change="success" />
```

**属性说明**

| 属性名称        | 描述                      | 类型      | 默认值                                                                              |
| --------------- | ------------------------- | --------- | ----------------------------------------------------------------------------------- |
| `modelValue`    | 组件的值                  | `string`  | `Array`                                                                             |
| `limit`         | 数量限制                  | `number`  | `5`                                                                                 |
| `fileSize`      | 文件大小限制              | `number`  | `5MB`                                                                               |
| `fileType`      | 文件类型限制              | `Array`   | `['png', 'jpg', 'jpeg', 'doc', 'xls', 'ppt', 'txt', 'pdf', 'docx', 'xlsx', 'pptx']` |
| `isShowTip`     | 是否显示提示              | `boolean` | `true`                                                                              |
| `uploadFileUrl` | 上传文件的 API 方法       | `string`  | `'/admin/sys-file/upload'`                                                          |
| `type`          | 组件类型                  | `string`  | `'default'`                                                                         |
| `data`          | 需要额外传递给 API 的数据 | `Object`  | -                                                                                   |
| `dir`           | 子目录名称                | `String`  | `a/b 格式, v5.3+`                                                                   |
| `autoUpload`    | 是否自动上传文件          | `boolean` | `true`                                                                              |

#### 图片上传

```vue
<upload-img v-model:imageUrl="formData.avatar" borderRadius="50%">
  <template #empty>
    <el-icon><Avatar /></el-icon>
    <span>请上传头像</span>
  </template>
</upload-img>
```

**属性说明**

| 属性名称        | 描述                                       | 类型                                                                             |
| --------------- | ------------------------------------------ | -------------------------------------------------------------------------------- |
| `imageUrl`      | 图片地址（双向绑定，上传会更新返回此字段） | `string (必传)`                                                                  |
| `uploadFileUrl` | 上传图片的 API 方法                        | `string (非必传)`                                                                |
| `dir`           | 子目录名称                                 | `String (非必传)`                                                                |
| `drag`          | 是否支持拖拽上传                           | `boolean (非必传，默认为 true)`                                                  |
| `disabled`      | 是否禁用上传组件                           | `boolean (非必传，默认为 false)`                                                 |
| `fileSize`      | 图片大小限制                               | `number (非必传，默认为 5M)`                                                     |
| `fileType`      | 图片类型限制                               | `File.ImageMimeType[] (非必传，默认为 ["image/jpeg", "image/png", "image/gif"])` |
| `height`        | 组件高度                                   | `string (非必传，默认为 150px)`                                                  |
| `width`         | 组件宽度                                   | `string (非必传，默认为 150px)`                                                  |
| `borderRadius`  | 组件边框圆角                               | `string (非必传，默认为 8px)`                                                    |

#### Excel 文件上传

```vue
<upload-excel
	ref="excelUploadRef"
	:title="$t('sysuser.importUserTip')"
	temp-url="/admin/sys-file/local/file/user.xlsx"
	url="/admin/user/import"
	@refreshDataList="getDataList"
/>
```

**属性说明**

| 属性名称  | 描述                        | 类型     |
| --------- | --------------------------- | -------- |
| `url`     | 导入文件后台接口            | `String` |
| `title`   | 弹出框标题                  | `String` |
| `tempUrl` | 获取当前导如 excel 模板地址 | `String` |

### 5.5 富文本组件

Wangeditor 是一款基于 Web 的所见即所得富文本编辑器，它提供了丰富的富文本编辑功能，支持插入图片、音视频等多媒体内容，并且可以通过自定义插件扩展其功能。

#### 页面使用

```vue
<editor v-model:get-html="form.content" />
```

#### 属性说明

| 属性名称        | 描述                                       | 类型      | 默认值                     |
| --------------- | ------------------------------------------ | --------- | -------------------------- |
| `disable`       | 是否禁用编辑器                             | `Boolean` | `false`                    |
| `placeholder`   | 编辑器默认占位符                           | `String`  | `"请输入内容..."`          |
| `mode`          | 编辑器模式，可选值为 default 或 simple     | `String`  | `default`                  |
| `height`        | 编辑器高度                                 | `String`  | `"310px"`                  |
| `getHtml`       | 双向绑定的属性，用于获取编辑器中 HTML 内容 | `String`  | -                          |
| `getText`       | 双向绑定的属性，用于获取编辑器中纯文本内容 | `String`  | -                          |
| `uploadFileUrl` | 上传文件的接口地址                         | `String`  | `"/admin/sys-file/upload"` |

### 5.6 代码编辑组件

#### 引入组件

```typescript
const CodeEditor = defineAsyncComponent(() => import('/@/components/CodeEditor/index.vue'));
```

#### 使用方法

```vue
<template>
	<div>
		<CodeEditor v-model="code" mode="go" theme="idea" :readOnly="false" :height="300" />
	</div>
</template>
```

#### Props

| 属性         | 类型               | 默认值   | 说明                  |
| ------------ | ------------------ | -------- | --------------------- |
| `modelValue` | `String`           | `''`     | 绑定的代码内容        |
| `mode`       | `String`           | `'go'`   | 代码语言              |
| `theme`      | `String`           | `'idea'` | 代码编辑器的主题      |
| `height`     | `[String, Number]` | `300`    | 代码编辑器的高度      |
| `options`    | `Object`           | `{}`     | CodeMirror 的配置选项 |
| `readOnly`   | `Boolean`          | `false`  | 是否只读              |

### 5.7 图表功能

Vue Echarts 是一个 Vue.js 的 ECharts 图表组件，对 EChart 进行了封装，简化了 Echart 图表的使用。

pigx-ui 默认引入了 vue-echarts ，业务代码中可以非常方便地使用相关的 EChart 图表组件【例如：日志管理】。

#### 快速使用

1. 访问 [ECharts 官网](https://echarts.apache.org/examples/zh/index.html#chart-type-bar)，选择目标类型的图表组件，获取图表的 option 配置 JSON。

2. 访问 [vue-echarts 代码生成器](https://vue-echarts.dev/#codegen)。

#### 业务页面使用 vue-echarts

```vue
<template>
	<v-chart class="h-80 w-full" :option="option" />
</template>

<script setup lang="ts" name="demo-chart">
// 直接复制 vue-echart codegen 生成的 vue 代码部分
import { use, reactive } from 'echarts/core'
import {
  BarChart
} from 'echarts/charts'
import {
  GridComponent
} from 'echarts/components'
import {
  CanvasRenderer
} from 'echarts/renderers'

use([
  GridComponent,
  BarChart,
  CanvasRenderer
])

// 定义 JSON
const option = reactive(复制 EChart option JSON)
</script>
```

### 5.8 左侧查询树

```vue
<query-tree :query="state.queryList" @node-click="handleNodeClick">
</query-tree>
```

```typescript
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'));

const state = reactive({
	queryList: (name: String) => {
		return api({ name: name });
	},
});

const handleNodeClick = (e: any) => {};
```

#### 卡槽扩展

```vue
<query-tree>
  <template #default="{ node, data }">
  </template>
</query-tree>
```

#### 属性说明

| 属性          | 类型       | 默认值                                                 | 说明                                                              |
| ------------- | ---------- | ------------------------------------------------------ | ----------------------------------------------------------------- |
| `props`       | `Object`   | `{ label: 'name', children: 'children', value: 'id' }` | 树形结构的属性配置                                                |
| `placeholder` | `String`   | `''`                                                   | 输入框的占位符                                                    |
| `loading`     | `Boolean`  | `false`                                                | 是否显示加载状态                                                  |
| `query`       | `Function` | -                                                      | 包含查询方法的函数，必须返回 Promise 类型的数据，并在组件中被调用 |

### 5.9 组织架构组件

#### 页面使用

```vue
<template>
	<org-selector v-model:orgList="userList" type="user" :multiple="false" />
</template>

<script setup>
import orgSelector from '/@/components/OrgSelector/index.vue';
</script>
```

#### 数据格式

```json
[
	{
		"type": "user",
		"id": "1",
		"name": "admin",
		"avatar": "xx.png"
	}
]
```

#### 属性说明

| 属性名       | 类型      | 默认值  | 描述                                                                                                           |
| ------------ | --------- | ------- | -------------------------------------------------------------------------------------------------------------- |
| `orgList`    | `Array`   | `[]`    | 组织列表，用于显示可选的组织                                                                                   |
| `type`       | `String`  | `user`  | 类型，可选值为 user、dept、org (部门、用户 同时可选)、role、post                                               |
| `multiple`   | `Boolean` | `true`  | 是否允许多选。如果为 `true`，则可以选择多个组织或用户；如果为 `false`，则只能选择一个组织或用户。默认为 `true` |
| `disabled`   | `Boolean` | `false` | 是否禁用组件。如果为 `true`，则组件将不可用，用户无法进行选择。默认为 `false`                                  |
| `selectSelf` | `Boolean` | `true`  | 是否包含自身。如果为 `true`，则在可选项中包含当前用户自身；如果为 `false`，则不包含当前用户自身。默认为 `true` |

### 5.10 标签列表组件

#### 使用方法

```vue
<tag-list buttonText="+手机号" v-model="phoneList" />
;

<script setup>
const phoneList = ref([]);
</script>
```

#### 属性说明

| 属性         | 类型                                     | 默认值      | 说明                   |
| ------------ | ---------------------------------------- | ----------- | ---------------------- |
| `modelValue` | `Array[String]`                          | `[]`        | 双向绑定绑定的标签数组 |
| `buttonText` | `String`                                 | `+ New Tag` | 按钮显示的文本         |
| `tagType`    | `'success', 'info', 'warning', 'danger'` | -           | 按钮显示类型           |

### 5.11 悬浮输入组件

```typescript
const PopoverInput = defineAsyncComponent(() => import('/@/components/PopoverInput/index.vue'));
```

```vue
<popover-input @confirm="onConfirm">
  <template #default>
    <el-button> 点击输入 </el-button>
  </template>
</popover-input>
```

#### 属性说明

| 属性名称      | 描述                               | 类型           | 默认值    |
| ------------- | ---------------------------------- | -------------- | --------- |
| `value`       | 值                                 | `String`       | -         |
| `type`        | 类型                               | `String`       | `'text'`  |
| `width`       | 宽度                               | `Number`       | `String`  |
| `placeholder` | 提示文字                           | `String`       | -         |
| `disabled`    | 是否禁用                           | `Boolean`      | `false`   |
| `options`     | 选项列表                           | `Array<any[]>` | `[]`      |
| `size`        | 大小                               | `'default'`    | `'small'` |
| `limit`       | 字符限制                           | `Number`       | `200`     |
| `showLimit`   | 是否显示字符限制                   | `Boolean`      | `false`   |
| `teleported`  | 是否使用传送门（teleport）进行渲染 | `Boolean`      | `true`    |

### 5.12 文字提示组件

```vue
<el-form-item label="初始浏览量" prop="visit">
  <template #label>
    浏览量<tip content="初始值" />
  </template>
</el-form-item>
```

#### 属性说明

| 属性名称    | 描述     | 类型     | 默认值        |
| ----------- | -------- | -------- | ------------- |
| `content`   | 内容     | `String` | -             |
| `placement` | 放置位置 | `String` | `'top-start'` |

### 5.13 省市区街四级联动组件

#### 省市区三级联动

```typescript
const ChinaArea = defineAsyncComponent(() => import('/@/components/ChinaArea/index.vue'));
```

```vue
<china-area v-model="data" @change="handleChange" />
;

<script setup>
// 双向绑定数据 自动回显： 北京市/市辖区/东城区
const data = ref('11,1101,110101');

// 可选 输出： 11,1101,110101
const handleChange = (val: String) => {};
</script>
```

#### 属性说明

| 属性名称  | 描述                      | 类型       |
| --------- | ------------------------- | ---------- |
| `v-model` | 双向绑定                  | `String`   |
| `type`    | 省级 1 市 2 区 3 街道 4   | `Number`   |
| `plus`    | 默认 false， 不用选择某级 | `Boolean`  |
| `@change` | 选中回调事件              | `Function` |

#### 完整区划数据

下载 [👉🏻 省市街区详细 SQL](https://minio.pigx.top/oss/202402/1708152046.sql)，导入至 sys_area 表即可。

如果此数据（来自民政部 2023 年的数据）中不存在对应地区，您可以根据规则自行新增。请注意，编码需保持唯一。

### 5.14 表单校验

#### 表单校验使用

Form 组件允许你验证用户的输入是否符合规范，来帮助你找到和纠正错误。

Form 组件提供了表单验证的功能，只需为 rules 属性传入约定的验证规则，并将 form-Item 的 prop 属性设置为需要验证的特殊键值即可。

```typescript
const rule = ref({
	字段名称: [
		// 校验用户输入的长度避免超长 0-255个字符 超长
		{ validator: rule.overLength, trigger: 'blur' },
		// 根据字段自动查询后台是否重复
		{
			validator: (rule: any, value: any, callback: any) => {
				validateExist(rule, value, callback, form.id !== '');
			},
			trigger: 'blur',
		},
	],
});
```

> ⚠️ **注意**：element-plus 的 radio 和 select 等类型组件对 value 字段有强制的数据类型要求。实体字段应该使用字符串而不是数字类型来对应字典功能使用。

#### 内置规则

| 函数名称                    | 函数作用                                        |
| --------------------------- | ----------------------------------------------- |
| `validateExist`             | 根据字段自动查询后台是否重复                    |
| `overLength`                | 校验用户输入的长度避免超长，范围为 0-255 个字符 |
| `validatorNameCn`           | 校验用户输入是否为中文、英文、数字包括下划线    |
| `validatorCapital`          | 校验用户输入是否为大写英文、下划线              |
| `validatorLowercase`        | 校验用户输入是否为小写英文、下划线              |
| `validatorLower`            | 校验用户输入是否为小写英文                      |
| `checkSpace`                | 校验输入是否包含首尾空白字符                    |
| `validatePhone`             | 校验手机号的合法性                              |
| `number`                    | 校验输入是否为数字                              |
| `letter`                    | 校验输入是否为字母                              |
| `letterAndNumber`           | 校验输入是否为字母和数字                        |
| `mobilePhone`               | 校验输入是否为正确格式的手机号码                |
| `letterStartNumberIncluded` | 校验输入是否以字母开头，可包含数字              |
| `noChinese`                 | 校验输入是否包含中文字符                        |
| `chinese`                   | 校验输入是否为中文字符                          |
| `email`                     | 校验输入是否为正确格式的电子邮箱                |
| `url`                       | 校验输入是否为正确格式的 URL                    |
| `regExp`                    | 校验输入是否符合给定的正则表达式                |

---

## 六、首页开发

### 6.1 首页组件开发

#### 开发组件

在 `src/views/home/widgets/components` 目录下创建组件

#### 挂载组件

在 `src/views/home/widgets/index.vue` 中挂载组件，将 myGrid 数组增加上一步创建的组件文件名称

```typescript
const myGrid = ['test-demo'];
```

---

## 总结

本文档汇总了 PIGX 前端技术框架的核心内容，包括：

- ✅ **环境配置**：ENV 配置、前端配置项说明
- ✅ **项目结构**：完整的目录结构说明
- ✅ **核心功能**：国际化、图标、权限、路由、标签页、状态管理
- ✅ **工具函数**：字典、参数、通用工具函数
- ✅ **组件使用**：表格、分页、上传、富文本、代码编辑、图表等 14 个常用组件
- ✅ **首页开发**：首页组件开发指南

更多详细信息请参考官方文档：[https://pig4cloud.com](https://pig4cloud.com)

---

**文档版本**: v1.0  
**最后更新**: 2025-10-23  
**维护团队**: PIGX 开发团队
