签入版本

This commit is contained in:
2026-10-10 19:17:02 +08:00
parent 3423dea03f
commit 32f941b631
232 changed files with 4264 additions and 6251 deletions
@@ -27,7 +27,6 @@
| Region | `components/Region/` | 行政区划 | `value`、`level` |
| Cropper | `components/Cropper/` | 头像裁剪 | `image`、`aspectRatio` |
| NumberInput | `components/numberInput/` | 数字输入 | `value`、`min`、`max` |
| PriceInput | `components/priceInput/` | 价格输入 | `value`、`precision` |
## Excel
@@ -41,7 +40,6 @@
| 组件 | 路径 | 说明 |
|------|------|------|
| Authority | `components/Authority/` | 权限组件(根据权限显示/隐藏) |
| CheckToken | `components/CheckToken/` | Token 校验组件 |
| Lockscreen | `components/Lockscreen/` | 锁屏组件 |
| Websocket | `components/Websocket/` | WebSocket 组件 |
@@ -51,20 +49,34 @@
```vue
<template>
<BasicTable @register="registerTable" />
<BasicTable
:columns="columns"
:request="loadDataTable"
:row-key="(row) => row.id"
ref="tableRef"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
</template>
</template>
</BasicTable>
</template>
<script setup>
import { BasicTable, useTable } from '@/components/Table';
import { ref } from 'vue';
import { getExamplePage } from '@/api/tool/example';
const [registerTable] = useTable({
api: getExamplePage,
columns: [
{ title: 'ID', field: 'id', width: 80 },
{ title: '名称', field: 'name' },
{ title: '状态', field: 'statusText', width: 100 },
],
});
const tableRef = ref();
const columns = [
{ title: 'ID', dataIndex: 'id', width: 80 },
{ title: '名称', dataIndex: 'name' },
{ title: '状态', dataIndex: 'statusText', width: 100 },
{ title: '操作', dataIndex: 'action', key: 'action', width: 180 },
];
const loadDataTable = async (params) => await getExamplePage(params);
const handleEdit = (record) => {};
</script>
```
@@ -80,8 +92,13 @@ import { BasicForm, useForm } from '@/components/Form';
const [registerForm] = useForm({
schemas: [
{ field: 'name', label: '名称', component: 'Input', required: true },
{ field: 'status', label: '状态', component: 'Select', componentProps: { options: [...] } },
{
name: 'name',
label: '名称',
component: 'Input',
rules: [{ required: true, message: '请输入名称', trigger: ['blur'] }],
},
{ name: 'status', label: '状态', component: 'Select', componentProps: { options: [...] } },
],
});
</script>
@@ -91,15 +108,19 @@ const [registerForm] = useForm({
```vue
<template>
<BasicModal @register="registerModal" @ok="handleSubmit" />
<basicModal @register="registerModal" @ok="handleSubmit">
<template #default>
<!-- 表单内容 -->
</template>
</basicModal>
</template>
<script setup>
import { BasicModal, useModal } from '@/components/Modal';
import { basicModal, useModal } from '@/components/Modal';
const [registerModal, { openModal, closeModal }] = useModal();
const handleAdd = () => openModal(true, { isUpdate: false });
const handleAdd = () => openModal();
const handleSubmit = async () => { /* ... */ closeModal(); };
</script>
```
@@ -74,33 +74,13 @@ await userStore.logout();
| 字段 | 说明 |
|------|------|
| `navMode` | 导航模式(侧边/顶部) |
| `navTheme` | 导航主题(暗色/亮色) |
| `isShowMenuBar` | 是否显示菜单栏 |
| `isCollapse` | 菜单是否折叠 |
---
## ossConfigStore
**文件**:`store/modules/ossConfig.ts`
| 字段 | 说明 |
|------|------|
| `ossConfig` | 云存储配置 |
路由守卫中初始化,用于文件上传。
---
## designSettingStore
**文件**:`store/modules/designSetting.ts`
| 字段 | 说明 |
|------|------|
| `appTheme` | 主题色 |
| `darkMode` | 是否暗色模式 |
| `layout` | 布局模式(side 侧边 / top 顶部 / mix 拆分) |
| `navTheme` | 导航主题(light / dark) |
| `headerTheme` | 标题栏主题 |
| `menuSetting` | 菜单配置(宽度、折叠、固定等) |
| `multiTabsSetting` | 多标签页配置 |
| `permissionMode` | 菜单权限模式(ROLE / BACK) |
| `isWaterMark` | 是否显示水印 |
---
@@ -114,13 +94,29 @@ await userStore.logout();
| Action | 说明 |
|--------|------|
| `addTab(route)` | 添加标签 |
| `removeTab(route)` | 关闭标签 |
| `closeOtherTabs(route)` | 关闭其他标签 |
| `addTabs(route)` | 添加标签 |
| `closeCurrentTab(route)` | 关闭当前标签 |
| `closeOtherTabs(route, activeKey)` | 关闭其他标签 |
| `closeAllTabs()` | 关闭所有标签 |
---
## lockscreenStore
**文件**:`store/modules/lockscreen.ts`
| 字段 | 说明 |
|------|------|
| `isLock` | 是否锁屏 |
| `lockTime` | 锁屏剩余时间 |
| Action | 说明 |
|--------|------|
| `setLock(flag)` | 设置锁屏状态(持久化到 Storage) |
| `setLockTime(time)` | 设置锁屏时间 |
---
## 在组件外使用
```typescript
@@ -230,8 +230,6 @@ src/utils/http/axios/
| asyncRouteStore | `asyncRoute.ts` | 动态路由、keep-alive 缓存列表 |
| projectSettingStore | `projectSetting.ts` | 项目配置(主题、布局等) |
| tabsViewStore | `tabsView.ts` | 标签页导航状态 |
| designSettingStore | `designSetting.ts` | 主题设计配置 |
| ossConfigStore | `ossConfig.ts` | 云存储配置 |
| lockscreenStore | `lockscreen.ts` | 锁屏状态 |
**userStore 核心流程:**
@@ -330,9 +328,10 @@ src/utils/http/axios/
| Hook | 文件 | 说明 |
|------|------|------|
| `usePermission` | `hooks/web/usePermission.ts` | 权限判断(hasPermission、hasSomePermission) |
| `useTable` | `hooks/web/useTable.ts` | 表格通用逻辑(分页、搜索、刷新) |
| `useForm` | `hooks/web/useForm.ts` | 表单通用逻辑(提交、重置、校验) |
| `usePermission` | `hooks/web/usePermission.ts` | 权限判断(hasPermission、hasEveryPermission、hasSomePermission) |
| `usePage` | `hooks/web/usePage.ts` | 分页参数管理 |
| `useECharts` | `hooks/web/useECharts.ts` | ECharts 图表挂载与销毁 |
| `useForm` | `components/Form/src/hooks/useForm.ts` | 表单注册(BasicForm 配合 `@register` 使用) |
### Directives(自定义指令)
@@ -347,7 +346,7 @@ src/utils/http/axios/
|------|------|------|
| HTTP | `utils/http/axios/` | Axios 封装 |
| Storage | `utils/Storage.ts` | localStorage 封装 |
| Dict | `utils/dict.ts` | 字典工具(从 Store 获取字典值) |
| useLockFn | `utils/useLockFn.ts` | 防重复提交(请求级加锁) |
## 数据流向
@@ -242,8 +242,12 @@ const [genderOptions, statusOptions] = await Promise.all([
### 封装为通用 Hook
:::warning 说明
`ui/src/hooks/web/useDict.ts` 当前项目中尚不存在,以下为建议的封装写法,可按需自行创建。
:::
```typescript
// ui/src/hooks/web/useDict.ts
// ui/src/hooks/web/useDict.ts(自行创建)
import { ref, onMounted } from 'vue';
import { getDictItemList } from '@/api/common/index';
@@ -28,7 +28,6 @@
## 文件结构
```
thinkphp6/
├── docker/
│ ├── Dockerfile # PHP-FPM 应用镜像定义
│ ├── nginx.conf # Nginx 站点配置
+1 -3
View File
@@ -240,9 +240,7 @@ ui/src/
│ ├── asyncRoute.ts // 动态路由 Store(菜单/keep-alive)
│ ├── projectSetting.ts // 项目配置 Store
│ ├── tabsView.ts // 标签页 Store
│ ├── designSetting.ts // 设计/主题配置 Store
│ ├── lockscreen.ts // 锁屏 Store
│ └── ossConfig.ts // 云存储配置 Store
│ └── lockscreen.ts // 锁屏 Store
├── router/ // 路由配置
│ ├── index.ts // 路由实例
│ ├── base.ts // 静态路由(登录/404)
@@ -8,28 +8,46 @@
```vue
<template>
<BasicTable @register="registerTable" />
<BasicTable
:columns="columns"
:request="loadDataTable"
:row-key="(row) => row.id"
ref="tableRef"
scroll-x="1200"
>
<template #tableTitle>
<a-button type="primary" @click="handleAdd">新增</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<a-space>
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
<a-button type="link" danger @click="handleDelete(record.id)">删除</a-button>
</a-space>
</template>
</template>
</BasicTable>
</template>
<script setup>
import { BasicTable, useTable } from '@/components/Table';
import { ref } from 'vue';
import { getExamplePage, exampleDelete } from '@/api/tool/example';
const [registerTable] = useTable({
api: getExamplePage, // 数据接口
columns: [ // 列定义
{ title: 'ID', field: 'id', width: 80 },
{ title: '名称', field: 'name' },
{ title: '状态', field: 'statusText', width: 100 },
{ title: '创建时间', field: 'createTime', width: 180 },
],
actionColumn: { // 操作列
width: 200,
actions: [
{ label: '编辑', onClick: handleEdit },
{ label: '删除', onClick: handleDelete, type: 'danger' },
],
},
});
const tableRef = ref();
const columns = [
{ title: 'ID', dataIndex: 'id', width: 80 },
{ title: '名称', dataIndex: 'name' },
{ title: '状态', dataIndex: 'statusText', width: 100 },
{ title: '创建时间', dataIndex: 'createTime', width: 180 },
{ title: '操作', dataIndex: 'action', key: 'action', width: 200, fixed: 'right' },
];
// BasicTable 数据加载:返回值直接交给表格渲染
const loadDataTable = async (params) => await getExamplePage(params);
const handleAdd = () => {};
const handleEdit = (record) => {};
const handleDelete = async (id) => {};
</script>
```
@@ -17,13 +17,13 @@ import { BasicForm, useForm } from '@/components/Form';
const [registerForm] = useForm({
schemas: [
{
field: 'name',
name: 'name',
label: '名称',
component: 'Input',
required: true,
rules: [{ required: true, message: '请输入名称', trigger: ['blur'] }],
},
{
field: 'status',
name: 'status',
label: '状态',
component: 'Select',
componentProps: {
@@ -42,17 +42,21 @@ const [registerForm] = useForm({
```vue
<template>
<BasicModal @register="registerModal" @ok="handleSubmit" />
<basicModal @register="registerModal" @ok="handleSubmit">
<template #default>
<!-- 表单内容 -->
</template>
</basicModal>
</template>
<script setup>
import { BasicModal, useModal } from '@/components/Modal';
import { basicModal, useModal } from '@/components/Modal';
const [registerModal, { openModal, closeModal }] = useModal();
// 打开弹窗
const handleAdd = () => {
openModal(true, { isUpdate: false });
openModal();
};
// 提交
@@ -68,12 +72,11 @@ const handleSubmit = async () => {
```vue
<template>
<BasicUpload v-model="formData.avatar" :action="uploadUrl" />
<UploadImg v-model:image-url="formData.avatar" name="example" :fileSize="20" />
</template>
<script setup>
import { BasicUpload } from '@/components/Upload';
const uploadUrl = '/api/upload/uploadFile';
import UploadImg from '@/components/Upload/Image.vue';
</script>
```
@@ -81,11 +84,18 @@ const uploadUrl = '/api/upload/uploadFile';
```vue
<template>
<Editor v-model="formData.content" />
<Editor ref="editorRef" :height="editorHeight" name="content" />
</template>
<script setup>
import Editor from '@/components/Editor/index.vue';
import { ref } from 'vue';
import Editor from '@/components/Editor/tinymce.vue';
const editorRef = ref();
const editorHeight = 400;
// 提交时读取编辑器内容(真实项目回填:editorRef.value.myValue = html)
const getContent = () => editorRef.value?.myValue;
</script>
```
@@ -93,11 +103,13 @@ import Editor from '@/components/Editor/index.vue';
```vue
<template>
<Password v-model="formData.password" />
<Password :value="formData.password" :repeat="true" />
</template>
<script setup>
import { Password } from '@/components/Password';
// 组件内部维护输入值与强度校验,仅通过 value 单向传入初始值(无 v-model)
</script>
```
@@ -77,10 +77,10 @@ interface MenuItem {
const tabsViewStore = useTabsViewStore();
// 添加标签
tabsViewStore.addTab(route);
tabsViewStore.addTabs(route);
// 关闭标签
tabsViewStore.removeTab(route);
tabsViewStore.closeCurrentTab(route);
```
## 响应式布局
@@ -11,8 +11,6 @@
| userStore | `store/modules/user.ts` | Token / 用户信息 / 权限列表 / 登录登出 |
| asyncRouteStore | `store/modules/asyncRoute.ts` | 动态路由 / keep-alive 缓存 |
| projectSettingStore | `store/modules/projectSetting.ts` | 项目配置 |
| ossConfigStore | `store/modules/ossConfig.ts` | 云存储配置 |
| designSettingStore | `store/modules/designSetting.ts` | 主题设计 |
| lockscreenStore | `store/modules/lockscreen.ts` | 锁屏 |
| tabsViewStore | `store/modules/tabsView.ts` | 标签页 |
@@ -15,7 +15,6 @@ Docker 方案适合快速体验项目功能,无需手动安装 PHP、MySQL 等
## 文件结构
```
thinkphp6/
├── docker-compose.yml # 容器编排(项目根目录)
├── docker/
│ ├── Dockerfile # PHP-FPM 镜像定义