Compare commits
18 Commits
2c7d438750
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b122a34331 | ||
|
|
463d7921c1 | ||
|
|
a147fb8a28 | ||
|
|
46e654aace | ||
|
|
d1546678e1 | ||
|
|
ff2d6004b6 | ||
|
|
ce7916c097 | ||
|
|
3e40ab372b | ||
|
|
af16450eb6 | ||
|
|
9a93a6dc99 | ||
|
|
cdd78aa057 | ||
|
|
9b5f89aa10 | ||
|
|
29efa21f0a | ||
|
|
4a8c9c4a2d | ||
|
|
ae36d6da81 | ||
|
|
2066796977 | ||
|
|
bad718ecb9 | ||
|
|
869dda515d |
264
README.md
264
README.md
@@ -1,264 +0,0 @@
|
||||
# Admin Framework
|
||||
|
||||
一个基于 Vue2 的通用后台管理系统框架,包含完整的系统功能、登录、路由管理、布局等核心功能。
|
||||
|
||||
## ✨ 新版本亮点
|
||||
|
||||
**使用超级简单!只需 20 行代码即可启动完整的后台管理系统!**
|
||||
|
||||
```javascript
|
||||
// 引入框架(内置所有依赖)
|
||||
import AdminFramework from './admin-framework.js'
|
||||
import componentMap from './router/component-map.js'
|
||||
|
||||
// 一行搞定!
|
||||
const app = AdminFramework.createApp({
|
||||
title: '我的管理系统',
|
||||
apiUrl: 'http://localhost:9098/admin_api/', // uploadUrl 会自动设置
|
||||
componentMap: componentMap
|
||||
})
|
||||
|
||||
app.$mount('#app')
|
||||
```
|
||||
|
||||
**对比旧版本:**
|
||||
- 代码量减少 **75%+**(从 80+ 行到 20 行)
|
||||
- **无需手动引入** Vue、VueRouter、Vuex、ViewUI
|
||||
- **无需手动配置** 路由、状态管理
|
||||
- **自动处理** 菜单恢复、标题设置
|
||||
|
||||
👉 查看详细说明:[简化使用说明.md](./简化使用说明.md)
|
||||
|
||||
## 📦 项目结构
|
||||
|
||||
```
|
||||
admin-framework/
|
||||
├── src/ # 框架源码
|
||||
│ ├── api/ # API 接口
|
||||
│ ├── assets/ # 资源文件(样式、图片、字体)
|
||||
│ ├── components/ # 全局组件
|
||||
│ ├── config/ # 配置文件
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── store/ # Vuex 状态管理
|
||||
│ ├── utils/ # 工具函数
|
||||
│ ├── views/ # 页面组件
|
||||
│ │ ├── home/ # 主页
|
||||
│ │ ├── login/ # 登录页
|
||||
│ │ ├── system/ # 系统管理页面
|
||||
│ │ └── system/ # 高级系统页面
|
||||
│ └── index.js # 框架入口
|
||||
├── dist/ # 打包产物
|
||||
│ └── admin-framework.js # 框架打包文件(3.6 MB,内置所有依赖)
|
||||
├── demo-project/ # 完整示例项目 ⭐
|
||||
│ ├── src/
|
||||
│ │ ├── config/ # 配置
|
||||
│ │ ├── libs/ # 框架文件
|
||||
│ │ ├── views/ # 业务页面
|
||||
│ │ ├── App.vue
|
||||
│ │ └── main.js
|
||||
│ ├── README.md # Demo 使用说明
|
||||
│ └── INSTALL.md # 安装指南
|
||||
├── webpack.config.js # 构建配置
|
||||
├── package.json
|
||||
└── 完整使用文档.md # 详细文档
|
||||
|
||||
```
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 方式一:运行 Demo 项目(推荐)⭐
|
||||
|
||||
**Demo 项目现已更新为使用框架源码**,更方便调试和开发!
|
||||
|
||||
#### 🎯 一键启动
|
||||
|
||||
**Windows 用户**:
|
||||
```bash
|
||||
cd demo-project
|
||||
双击运行 start.bat
|
||||
```
|
||||
|
||||
**Linux/Mac 用户**:
|
||||
```bash
|
||||
cd demo-project
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
```
|
||||
|
||||
#### 📝 手动启动
|
||||
|
||||
```bash
|
||||
# 1. 进入 demo 项目
|
||||
cd demo-project
|
||||
|
||||
# 2. 安装依赖(首次必须)
|
||||
npm install
|
||||
|
||||
# 3. 启动开发服务器
|
||||
npm run dev
|
||||
```
|
||||
|
||||
浏览器会自动打开 `http://localhost:8080`
|
||||
|
||||
#### 📚 Demo 项目文档
|
||||
|
||||
- [快速启动.md](./demo-project/快速启动.md) - 快速启动指南 ⭐
|
||||
- [README.md](./demo-project/README.md) - 项目说明
|
||||
- [CHANGELOG.md](./demo-project/CHANGELOG.md) - 更新日志(重要)
|
||||
- [INSTALL.md](./demo-project/INSTALL.md) - 安装指南
|
||||
- [PROJECT_STRUCTURE.md](./demo-project/PROJECT_STRUCTURE.md) - 项目结构说明
|
||||
|
||||
#### ⚠️ 重要提示
|
||||
|
||||
- Demo 项目现在**直接使用框架源码**(`../../src/index.js`)
|
||||
- **首次运行必须执行** `npm install` 安装新依赖
|
||||
- 如遇到错误,删除 `node_modules` 和 `package-lock.json` 后重新安装
|
||||
|
||||
### 方式二:构建框架
|
||||
|
||||
```bash
|
||||
# 1. 安装依赖
|
||||
npm install
|
||||
|
||||
# 2. 构建框架
|
||||
npm run build
|
||||
|
||||
# 3. 产物在 dist/admin-framework.js
|
||||
```
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
### 核心功能
|
||||
- ✅ **极简使用** - 只需 20 行代码启动完整系统
|
||||
- ✅ **内置依赖** - Vue、VueRouter、Vuex、ViewUI 全部内置
|
||||
- ✅ **自动配置** - 自动初始化路由、状态管理、菜单恢复
|
||||
- ✅ **主页组件** - 欢迎页面,自动显示系统标题
|
||||
- ✅ **系统管理页面** - 用户、角色、菜单等管理
|
||||
- ✅ **登录和错误页面** - 完整的登录流程和错误处理
|
||||
- ✅ **动态路由管理** - 基于权限的路由控制
|
||||
- ✅ **Vuex 状态管理** - 用户、应用状态管理
|
||||
- ✅ **内置样式** - base.less、animate.css、iconfont
|
||||
- ✅ **工具库** - HTTP、日期、Token 等工具
|
||||
|
||||
## 📚 文档
|
||||
|
||||
- **⚡ 快速开始**:[快速开始.md](./快速开始.md) - **5 分钟上手指南**
|
||||
- **🔥 简化使用说明**:[简化使用说明.md](./简化使用说明.md) - **详细对比和说明**
|
||||
- **完整使用文档**:[完整使用文档.md](./完整使用文档.md)
|
||||
- **Demo 项目说明**:[demo-project/README.md](./demo-project/README.md)
|
||||
- **安装指南**:[demo-project/INSTALL.md](./demo-project/INSTALL.md)
|
||||
|
||||
## 🎯 Demo 项目预览
|
||||
|
||||
Demo 项目包含:
|
||||
|
||||
1. **登录页面**(`/login`)
|
||||
- 完整的登录表单
|
||||
- 自动跳转功能
|
||||
|
||||
2. **主页**(`/home`)
|
||||
- 欢迎页面
|
||||
- 显示系统标题
|
||||
|
||||
3. **业务示例**(`/business/product`)
|
||||
- 产品列表(Table)
|
||||
- CRUD 操作示例
|
||||
- Modal、Message 使用
|
||||
|
||||
4. **系统页面**(框架内置)
|
||||
- 用户管理
|
||||
- 角色管理
|
||||
- 菜单管理
|
||||
|
||||
## 🔧 使用方式
|
||||
|
||||
### 1. 在新项目中使用
|
||||
|
||||
```javascript
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Vuex from 'vuex'
|
||||
import ViewUI from 'view-design'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
import config from './config'
|
||||
|
||||
// 使用框架 - 自动完成所有初始化
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState
|
||||
})
|
||||
|
||||
// 创建 Vue 实例
|
||||
new Vue({
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
```
|
||||
|
||||
### 2. 按需使用组件
|
||||
|
||||
```javascript
|
||||
import { HomePage, SysUser, SysRole } from './libs/admin-framework.js'
|
||||
|
||||
// 在路由中使用
|
||||
const routes = [
|
||||
{ path: '/home', component: HomePage },
|
||||
{ path: '/system/user', component: SysUser }
|
||||
]
|
||||
```
|
||||
|
||||
## 📝 版本信息
|
||||
|
||||
**当前版本**: 1.0.0
|
||||
|
||||
**更新日志**:
|
||||
- v1.0.0 - 初始版本
|
||||
- ✅ 完整的系统管理功能
|
||||
- ✅ 主页组件
|
||||
- ✅ 登录和权限管理
|
||||
- ✅ 动态路由
|
||||
- ✅ 内置样式
|
||||
- ✅ Demo 示例项目
|
||||
|
||||
## 💻 技术栈
|
||||
|
||||
- Vue 2.6+
|
||||
- Vue Router 3.x
|
||||
- Vuex 3.x
|
||||
- View Design (iView) 4.x
|
||||
- Axios
|
||||
- Less
|
||||
- Webpack 5
|
||||
|
||||
## 🛠️ 开发
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 开发模式
|
||||
npm run dev
|
||||
|
||||
# 构建生产版本
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
## 👨💻 作者
|
||||
|
||||
light
|
||||
|
||||
---
|
||||
|
||||
**祝开发愉快!** 🎉
|
||||
|
||||
如有问题,请查看[完整使用文档.md](./完整使用文档.md)或查看 Demo 项目示例。
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
- ✅ **登录和权限管理** - 完整的登录流程和权限控制
|
||||
- ✅ **动态路由管理** - 基于权限菜单的动态路由生成
|
||||
- ✅ **Vuex 状态管理** - 用户、应用状态管理
|
||||
- ✅ **全局组件库** - Tables、Editor、Upload、TreeGrid、FieldRenderer 等
|
||||
- ✅ **全局组件库** - Tables、Editor、Upload、TreeGrid、FieldRenderer、FloatPanel 等
|
||||
- ✅ **工具库** - HTTP、日期、Token、Cookie 等工具
|
||||
- ✅ **内置样式** - base.less、animate.css、iconfont 等
|
||||
- ✅ **响应式布局** - 支持移动端适配
|
||||
@@ -450,6 +450,166 @@ export default {
|
||||
- ✅ **浏览器兼容**:支持所有现代浏览器
|
||||
- ✅ **内存管理**:自动清理临时 URL 对象
|
||||
|
||||
## 🎨 全局组件使用
|
||||
|
||||
### FloatPanel - 浮动面板组件
|
||||
|
||||
`FloatPanel` 是一个浮动在父窗体上的面板组件,类似于抽屉效果,常用于详情展示、表单编辑等场景。
|
||||
|
||||
**基本使用:**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<Button @click="showPanel">打开浮动面板</Button>
|
||||
|
||||
<FloatPanel
|
||||
ref="floatPanel"
|
||||
title="详情面板"
|
||||
position="right"
|
||||
:show-back="true"
|
||||
back-text="返回"
|
||||
@back="handleBack"
|
||||
>
|
||||
<div>这里是面板内容</div>
|
||||
</FloatPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
showPanel() {
|
||||
// 通过 ref 调用 show 方法显示面板
|
||||
this.$refs.floatPanel.show()
|
||||
},
|
||||
hidePanel() {
|
||||
// 通过 ref 调用 hide 方法隐藏面板
|
||||
this.$refs.floatPanel.hide()
|
||||
},
|
||||
handleBack() {
|
||||
console.log('返回按钮被点击')
|
||||
this.hidePanel()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
**属性说明:**
|
||||
|
||||
| 属性 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `title` | String | `''` | 面板标题 |
|
||||
| `width` | String/Number | `'100%'` | 面板宽度(字符串或数字),默认占满父容器 |
|
||||
| `height` | String/Number | `'100%'` | 面板高度(字符串或数字),默认占满父容器 |
|
||||
| `position` | String | `'right'` | 面板位置:`left`、`right`、`top`、`bottom`、`center` |
|
||||
| `showBack` | Boolean | `true` | 是否显示返回按钮 |
|
||||
| `showClose` | Boolean | `false` | 是否显示关闭按钮 |
|
||||
| `backText` | String | `'返回'` | 返回按钮文字 |
|
||||
| `closeOnClickBackdrop` | Boolean | `false` | 点击遮罩是否关闭 |
|
||||
| `mask` | Boolean | `false` | 是否显示遮罩(默认不显示) |
|
||||
| `zIndex` | Number | `1000` | 层级 |
|
||||
|
||||
**方法:**
|
||||
|
||||
| 方法 | 说明 | 参数 |
|
||||
|------|------|------|
|
||||
| `show(callback)` | 显示面板 | `callback`: 可选的回调函数 |
|
||||
| `hide()` | 隐藏面板 | - |
|
||||
|
||||
**事件:**
|
||||
|
||||
| 事件 | 说明 | 参数 |
|
||||
|------|------|------|
|
||||
| `back` | 点击返回按钮时触发 | - |
|
||||
|
||||
**插槽:**
|
||||
|
||||
| 插槽 | 说明 |
|
||||
|------|------|
|
||||
| `default` | 面板主体内容 |
|
||||
| `header-right` | 头部右侧内容(可用于添加自定义按钮) |
|
||||
|
||||
**位置说明:**
|
||||
|
||||
- `left`: 从左侧滑入
|
||||
- `right`: 从右侧滑入(默认)
|
||||
- `top`: 从顶部滑入
|
||||
- `bottom`: 从底部滑入
|
||||
- `center`: 居中显示,带缩放动画
|
||||
|
||||
**完整示例:**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<Button @click="openDetailPanel">查看详情</Button>
|
||||
|
||||
<FloatPanel
|
||||
ref="detailPanel"
|
||||
title="用户详情"
|
||||
position="right"
|
||||
:show-back="true"
|
||||
:show-close="true"
|
||||
back-text="返回"
|
||||
@back="handleBack"
|
||||
>
|
||||
<template #header-right>
|
||||
<Button type="primary" @click="handleSave">保存</Button>
|
||||
</template>
|
||||
|
||||
<div class="detail-content">
|
||||
<Form :model="formData" :label-width="100">
|
||||
<FormItem label="用户名">
|
||||
<Input v-model="formData.username" />
|
||||
</FormItem>
|
||||
<FormItem label="邮箱">
|
||||
<Input v-model="formData.email" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
</FloatPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
username: '',
|
||||
email: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openDetailPanel() {
|
||||
this.$refs.detailPanel.show()
|
||||
},
|
||||
handleBack() {
|
||||
this.$refs.detailPanel.hide()
|
||||
},
|
||||
handleSave() {
|
||||
// 保存逻辑
|
||||
console.log('保存数据', this.formData)
|
||||
this.$Message.success('保存成功')
|
||||
this.$refs.detailPanel.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
**特性说明:**
|
||||
|
||||
- ✅ 基于父元素定位,不会遮挡菜单
|
||||
- ✅ 宽度和高度默认 100%,占满父容器
|
||||
- ✅ 无遮罩背景,完全浮在父页面上
|
||||
- ✅ 路由切换或组件销毁时自动关闭
|
||||
- ✅ 支持多种位置和动画效果
|
||||
- ✅ 支持自定义头部右侧内容
|
||||
|
||||
## 📝 业务开发示例
|
||||
|
||||
### 创建业务页面
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## 🎯 Demo 项目
|
||||
|
||||
📦 **已提供完整的示例项目**:`demo-project/`
|
||||
📦 **已提供完整的示例项目**:`demo/`
|
||||
|
||||
一个开箱即用的完整示例,包含:
|
||||
- ✅ 框架集成配置
|
||||
@@ -18,12 +18,12 @@
|
||||
|
||||
**快速体验**:
|
||||
```bash
|
||||
cd demo-project
|
||||
cd demo
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
详细说明:[demo-project/README.md](./demo-project/README.md)
|
||||
详细说明:[demo/README.md](./demo/README.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -46,7 +46,7 @@ npm run dev
|
||||
✅ **主页组件**(欢迎页面,自动显示系统标题)
|
||||
✅ **系统管理页面**(sys 开头的所有页面和功能)
|
||||
✅ **系统 API**(system 和 system 所有 API)
|
||||
✅ **全局组件**(Tables、Editor、Upload、FieldRenderer 等)
|
||||
✅ **全局组件**(Tables、Editor、Upload、FieldRenderer、FloatPanel 等)
|
||||
✅ **布局组件**(Main、ParentView)
|
||||
✅ **文件下载**(支持 CSV 等格式,自动处理换行符)
|
||||
✅ **登录和错误页面**(Login、401、404、500)
|
||||
@@ -63,11 +63,11 @@ npm run dev
|
||||
|
||||
### 🎯 方式一:使用 Demo 项目(推荐新手)
|
||||
|
||||
我们提供了一个完整的 **demo-project** 示例项目,可以直接运行查看效果!
|
||||
我们提供了一个完整的 **demo** 示例项目,可以直接运行查看效果!
|
||||
|
||||
```bash
|
||||
# 1. 进入 demo 项目
|
||||
cd demo-project
|
||||
cd demo
|
||||
|
||||
# 2. 安装依赖
|
||||
npm install
|
||||
@@ -81,7 +81,7 @@ npm run dev
|
||||
- `/home` - 主页
|
||||
- `/business/product` - 业务示例页面
|
||||
|
||||
**详细说明**:查看 `demo-project/README.md` 和 `demo-project/INSTALL.md`
|
||||
**详细说明**:查看 `demo/README.md`
|
||||
|
||||
---
|
||||
|
||||
@@ -174,6 +174,8 @@ const app = AdminFramework.createApp({
|
||||
'order/pay_orders': PayOrdersComponent
|
||||
// 添加更多业务组件...
|
||||
},
|
||||
|
||||
HomePage: CustomHomePage, // 可选:自定义首页组件,覆盖整个首页
|
||||
onReady() {
|
||||
console.log('应用已启动!')
|
||||
}
|
||||
@@ -628,6 +630,7 @@ import {
|
||||
```javascript
|
||||
import {
|
||||
SysLog, // 系统日志管理
|
||||
SysLogOperate, // 系统操作日志
|
||||
SysParamSetup, // 参数设置
|
||||
SysRole, // 角色管理
|
||||
SysUser // 用户管理
|
||||
@@ -645,7 +648,7 @@ import {
|
||||
|
||||
#### 在路由中使用
|
||||
```javascript
|
||||
import { HomePage, SysUser, SysRole, SysMenu } from 'admin-framework'
|
||||
import { HomePage, SysUser, SysRole, SysMenu, SysLog, SysLogOperate } from 'admin-framework'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -667,6 +670,16 @@ const routes = [
|
||||
path: '/system/menu',
|
||||
name: 'sys_menu',
|
||||
component: SysMenu
|
||||
},
|
||||
{
|
||||
path: '/system/log',
|
||||
name: 'sys_log',
|
||||
component: SysLog
|
||||
},
|
||||
{
|
||||
path: '/system/log_operate',
|
||||
name: 'sys_log_operate',
|
||||
component: SysLogOperate
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -691,19 +704,35 @@ const users = await userServer.getList({ page: 1 })
|
||||
const roles = await roleServer.getList()
|
||||
```
|
||||
|
||||
#### system API
|
||||
#### system API(所有 API 都在 systemApi 中)
|
||||
```javascript
|
||||
import { systemHighApi } from 'admin-framework'
|
||||
import { systemApi } from 'admin-framework'
|
||||
|
||||
// 使用示例
|
||||
const {
|
||||
userServer,
|
||||
roleServer,
|
||||
sysLogServe,
|
||||
fileServe,
|
||||
menuServer,
|
||||
paramSetupServer,
|
||||
modelServer,
|
||||
formServer,
|
||||
formFieldServer,
|
||||
modelFieldServer,
|
||||
tableServer,
|
||||
rolePermissionServer,
|
||||
sysControlTypeServer,
|
||||
sysModuleServer,
|
||||
sysAddressServer,
|
||||
systemTypeServer,
|
||||
plaAccountServer,
|
||||
// ... 其他 API
|
||||
} = systemHighApi
|
||||
} = systemApi
|
||||
|
||||
// 调用 API
|
||||
const users = await userServer.getList({ page: 1 })
|
||||
const roles = await roleServer.getList()
|
||||
const menus = await menuServer.getTree()
|
||||
const params = await paramSetupServer.getOne('sys_title')
|
||||
```
|
||||
@@ -739,17 +768,11 @@ const params = await paramSetupServer.getOne('sys_title')
|
||||
ref="floatPanel"
|
||||
title="详情面板"
|
||||
position="right"
|
||||
:width="800"
|
||||
:show-back="true"
|
||||
back-text="返回"
|
||||
@back="handleBack"
|
||||
>
|
||||
<div>这里是面板内容</div>
|
||||
|
||||
<template slot="footer">
|
||||
<Button @click="hidePanel">取消</Button>
|
||||
<Button type="primary" @click="handleSave">保存</Button>
|
||||
</template>
|
||||
</FloatPanel>
|
||||
</div>
|
||||
</template>
|
||||
@@ -810,14 +833,14 @@ export default {
|
||||
| 属性 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `title` | String | `''` | 面板标题 |
|
||||
| `width` | String/Number | `'80%'` | 面板宽度(字符串或数字) |
|
||||
| `height` | String/Number | `'80%'` | 面板高度(字符串或数字) |
|
||||
| `width` | String/Number | `'100%'` | 面板宽度(字符串或数字),默认占满父容器 |
|
||||
| `height` | String/Number | `'100%'` | 面板高度(字符串或数字),默认占满父容器 |
|
||||
| `position` | String | `'right'` | 面板位置:`left`、`right`、`top`、`bottom`、`center` |
|
||||
| `showBack` | Boolean | `true` | 是否显示返回按钮 |
|
||||
| `showClose` | Boolean | `false` | 是否显示关闭按钮 |
|
||||
| `backText` | String | `'返回'` | 返回按钮文字 |
|
||||
| `closeOnClickBackdrop` | Boolean | `false` | 点击遮罩是否关闭 |
|
||||
| `mask` | Boolean | `true` | 是否显示遮罩 |
|
||||
| `mask` | Boolean | `false` | 是否显示遮罩(默认不显示) |
|
||||
| `zIndex` | Number | `1000` | 层级 |
|
||||
|
||||
**方法:**
|
||||
@@ -839,7 +862,6 @@ export default {
|
||||
|------|------|
|
||||
| `default` | 面板主体内容 |
|
||||
| `header-right` | 头部右侧内容 |
|
||||
| `footer` | 底部内容 |
|
||||
|
||||
**位置说明:**
|
||||
|
||||
@@ -849,6 +871,14 @@ export default {
|
||||
- `bottom`: 从底部滑入
|
||||
- `center`: 居中显示,带缩放动画
|
||||
|
||||
**特性说明:**
|
||||
|
||||
- ✅ 基于父元素定位,不会遮挡菜单
|
||||
- ✅ 宽度和高度默认 100%,占满父容器
|
||||
- ✅ 无遮罩背景,完全浮在父页面上
|
||||
- ✅ 路由切换或组件销毁时自动关闭
|
||||
- ✅ 支持多种位置和动画效果
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 开发指南
|
||||
|
||||
@@ -172,6 +172,7 @@ app.$mount('#app')
|
||||
- ✅ `<AsyncModal>` - 异步弹窗
|
||||
- ✅ `<Editor>` - 富文本编辑器
|
||||
- ✅ `<CommonIcon>` - 图标选择器
|
||||
- ✅ `<FloatPanel>` - 浮动面板
|
||||
|
||||
### 4. 工具方法
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Main from './main'
|
||||
import ParentView from './parent-view'
|
||||
|
||||
|
||||
// 导入页面组件
|
||||
import pages from '../views/index'
|
||||
const {LoginPage,Page401,Page404,Page500} = pages
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
:col="col"
|
||||
:value="row[col.key]"
|
||||
:disabled="getDisabled(col)"
|
||||
@input="handleFieldInput(col.key, $event)"
|
||||
@input="handleFieldInput(col.key, $event )"
|
||||
@change="handleFieldChange(col.key, $event)"
|
||||
/>
|
||||
</Row>
|
||||
@@ -91,11 +91,19 @@ export default {
|
||||
methods: {
|
||||
// 处理字段输入事件
|
||||
handleFieldInput(key, value) {
|
||||
// 确保接收到的是值而不是事件对象
|
||||
if (value && typeof value === 'object' && value.target) {
|
||||
value = value.target.value
|
||||
}
|
||||
this.$set(this.row, key, value)
|
||||
},
|
||||
|
||||
// 处理字段变化事件
|
||||
handleFieldChange(key, value) {
|
||||
// 确保接收到的是值而不是事件对象
|
||||
if (value && typeof value === 'object' && value.target) {
|
||||
value = value.target.value
|
||||
}
|
||||
this.$set(this.row, key, value)
|
||||
},
|
||||
|
||||
@@ -186,15 +194,23 @@ export default {
|
||||
.forEach((col) => {
|
||||
let defaultVal = row[col.key]
|
||||
|
||||
// 如果值是 undefined 或 null,不赋值,保持原状态
|
||||
if (defaultVal === undefined || defaultVal === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// 传递过来什么值就保持什么值
|
||||
if (col.data_type === 'number') {
|
||||
defaultVal = parseFloat(defaultVal) || 0
|
||||
Vue.set(this.row, col.key, defaultVal)
|
||||
// 保持原始数值,包括 0
|
||||
const numVal = parseFloat(defaultVal)
|
||||
Vue.set(this.row, col.key, isNaN(numVal) ? defaultVal : numVal)
|
||||
} else if (col.data_type === 'date') {
|
||||
Vue.set(this.row, col.key, dayjs(defaultVal).toDate())
|
||||
} else if (col.data_type === 'boolean') {
|
||||
Vue.set(this.row, col.key, defaultVal === 1 || defaultVal === true)
|
||||
// 保持原始布尔值,包括 false
|
||||
Vue.set(this.row, col.key, defaultVal === 1 || defaultVal === true || defaultVal === '1')
|
||||
} else {
|
||||
defaultVal = defaultVal !== undefined ? defaultVal : ''
|
||||
// 保持原始值,包括空字符串 ''
|
||||
Vue.set(this.row, col.key, defaultVal)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="width: 100%;">
|
||||
<!-- 使用组件映射表来简化条件渲染 -->
|
||||
<component
|
||||
:is="getComponentName(col.com)"
|
||||
@@ -10,7 +10,6 @@
|
||||
:style="getComponentStyle(col.com)"
|
||||
@input="handleInput"
|
||||
@on-change="handleChange"
|
||||
@change="handleChange"
|
||||
>
|
||||
<!-- Select 组件的选项 -->
|
||||
<template v-if="col.com === 'Select'">
|
||||
@@ -94,24 +93,8 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 确保 Radio 的值类型正确
|
||||
// 直接返回原始值,不进行类型转换
|
||||
radioValue() {
|
||||
|
||||
if (this.col.com === 'Radio') {
|
||||
const source = this.getRadioSource(this.col)
|
||||
if (source.length > 0 && this.value !== undefined && this.value !== null) {
|
||||
const firstItem = source[0]
|
||||
const firstValue = this.getRadioValue(firstItem)
|
||||
// 如果第一个选项的值是数字类型,确保 value 也是数字
|
||||
if (typeof firstValue === 'number') {
|
||||
return Number(this.value)
|
||||
}
|
||||
// 如果第一个选项的值是字符串类型,确保 value 也是字符串
|
||||
if (typeof firstValue === 'string') {
|
||||
return String(this.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.value
|
||||
}
|
||||
},
|
||||
@@ -261,39 +244,21 @@ export default {
|
||||
|
||||
// 处理输入事件
|
||||
handleInput(value) {
|
||||
// 确保 Radio 的值类型正确
|
||||
if (this.col.com === 'Radio') {
|
||||
const source = this.getRadioSource(this.col)
|
||||
if (source.length > 0) {
|
||||
const firstItem = source[0]
|
||||
// 如果第一个选项的值是数字类型,确保 value 也是数字
|
||||
const firstValue = this.getRadioValue(firstItem)
|
||||
if (typeof firstValue === 'number' && typeof value === 'string') {
|
||||
value = Number(value)
|
||||
} else if (typeof firstValue === 'string' && typeof value === 'number') {
|
||||
value = String(value)
|
||||
}
|
||||
}
|
||||
// 如果接收到的是事件对象,提取值
|
||||
if (value && typeof value === 'object' && value.target) {
|
||||
value = value.target.value
|
||||
}
|
||||
// 直接传递值,不进行类型转换
|
||||
this.$emit('input', value)
|
||||
},
|
||||
|
||||
// 处理变化事件
|
||||
handleChange(value) {
|
||||
// 确保 Radio 的值类型正确
|
||||
if (this.col.com === 'Radio') {
|
||||
const source = this.getRadioSource(this.col)
|
||||
if (source.length > 0) {
|
||||
const firstItem = source[0]
|
||||
// 如果第一个选项的值是数字类型,确保 value 也是数字
|
||||
const firstValue = this.getRadioValue(firstItem)
|
||||
if (typeof firstValue === 'number' && typeof value === 'string') {
|
||||
value = Number(value)
|
||||
} else if (typeof firstValue === 'string' && typeof value === 'number') {
|
||||
value = String(value)
|
||||
}
|
||||
}
|
||||
// 如果接收到的是事件对象,提取值
|
||||
if (value && typeof value === 'object' && value.target) {
|
||||
value = value.target.value
|
||||
}
|
||||
// 直接传递值,不进行类型转换
|
||||
this.$emit('change', value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,13 +90,27 @@ export default {
|
||||
},
|
||||
|
||||
insideColumns() {
|
||||
let columns = this.columns.map((item, index) => {
|
||||
// 设置默认对齐方式
|
||||
if (!item.align) {
|
||||
item.align = 'center'
|
||||
// 确保 columns 是数组
|
||||
if (!Array.isArray(this.columns) || this.columns.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (item.children && item.children.length > 0) {
|
||||
item.children = item.children.map((sonItem) => {
|
||||
let columns = this.columns.map((item, index) => {
|
||||
// 确保 item 是对象
|
||||
if (!item || typeof item !== 'object') {
|
||||
console.warn(`[Tables] 列配置第 ${index} 项格式不正确:`, item)
|
||||
return null
|
||||
}
|
||||
|
||||
// 创建新对象,避免直接修改原对象
|
||||
let column = { ...item }
|
||||
|
||||
// 设置默认对齐方式
|
||||
if (!column.align) {
|
||||
column.align = 'center'
|
||||
|
||||
if (column.children && column.children.length > 0) {
|
||||
column.children = column.children.map((sonItem) => {
|
||||
if (!sonItem.align) {
|
||||
sonItem.align = 'center'
|
||||
}
|
||||
@@ -107,12 +121,12 @@ export default {
|
||||
|
||||
// 确保列宽设置正确,优先使用 width,其次使用 minWidth
|
||||
// 如果都没有设置,则设置默认 minWidth
|
||||
if (!item.width && !item.minWidth) {
|
||||
item.minWidth = 120
|
||||
if (!column.width && !column.minWidth) {
|
||||
column.minWidth = 120
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
return column
|
||||
}).filter(item => item !== null) // 过滤掉无效的列配置
|
||||
|
||||
return columns
|
||||
},
|
||||
@@ -139,11 +153,27 @@ export default {
|
||||
},
|
||||
|
||||
handleTableData() {
|
||||
let values = this.value || []
|
||||
let data = values.filter((p) => p)
|
||||
this.insideTableData = data.map((item, index) => {
|
||||
let res = item
|
||||
return res
|
||||
// 确保 value 是数组
|
||||
let values = Array.isArray(this.value) ? this.value : []
|
||||
|
||||
// 过滤掉 null 和 undefined,但保留其他 falsy 值(如 0、false、空字符串等)
|
||||
let data = values.filter((p) => p !== null && p !== undefined)
|
||||
|
||||
// 使用 $set 确保响应式更新
|
||||
this.$set(this, 'insideTableData', data.map((item, index) => {
|
||||
// 确保每个数据项都是对象
|
||||
if (typeof item !== 'object' || item === null) {
|
||||
return { value: item }
|
||||
}
|
||||
return item
|
||||
}))
|
||||
|
||||
// 强制更新表格(如果表格已渲染)
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.tablesMain) {
|
||||
// 触发表格重新渲染
|
||||
this.$refs.tablesMain.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
onChangePage(page) {
|
||||
@@ -177,11 +207,28 @@ export default {
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
this.handleTableData()
|
||||
// 监听 value 变化
|
||||
value: {
|
||||
handler(val) {
|
||||
this.handleTableData()
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
// 监听 columns 变化,确保列配置更新时表格重新渲染
|
||||
columns: {
|
||||
handler() {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.tablesMain) {
|
||||
this.$refs.tablesMain.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 确保数据已处理
|
||||
this.handleTableData()
|
||||
|
||||
// 自适应高度
|
||||
@@ -196,6 +243,14 @@ export default {
|
||||
|
||||
// 动态计算偏移量
|
||||
this.calculateOffset()
|
||||
|
||||
// 如果数据或列配置为空,输出警告
|
||||
if (!this.insideTableData || this.insideTableData.length === 0) {
|
||||
console.warn('[Tables] 表格数据为空,请检查 value 属性是否正确绑定')
|
||||
}
|
||||
if (!this.columns || this.columns.length === 0) {
|
||||
console.warn('[Tables] 表格列配置为空,请检查 columns 属性是否正确绑定')
|
||||
}
|
||||
})
|
||||
|
||||
// 监听窗口大小变化,重新计算偏移量
|
||||
|
||||
14
src/index.js
14
src/index.js
@@ -25,11 +25,11 @@ import storeModules, { createStore } from './store'
|
||||
import { createBaseRoutes, setupRouterGuards, createRouter, getRoutes } from './router'
|
||||
|
||||
|
||||
import components ,{ registerGlobalComponents} from './components/index'
|
||||
import components ,{ registerGlobalComponents, registerComponents} from './components/index'
|
||||
|
||||
import pages from './views/index'
|
||||
|
||||
const { LoginPage, Page401, Page404, Page500, setupComponentMap } = pages
|
||||
const { LoginPage, Page401, Page404, Page500, setupComponentMap, HomePage } = pages
|
||||
|
||||
|
||||
// 导入页面组件
|
||||
@@ -44,6 +44,7 @@ class AdminFramework {
|
||||
this.config = {}
|
||||
this.store = null
|
||||
this.router = null
|
||||
this.HomePage = pages.HomePage // 默认使用框架内置的 HomePage
|
||||
|
||||
this.tools = tools
|
||||
this.uiTool = uiTool
|
||||
@@ -53,7 +54,7 @@ class AdminFramework {
|
||||
|
||||
this.createBaseRoutes = createBaseRoutes
|
||||
this.setupRouterGuards = setupRouterGuards
|
||||
|
||||
this.registerComponents = registerComponents
|
||||
this.pages = pages
|
||||
this.components = components
|
||||
this.systemApi = systemApi
|
||||
@@ -93,6 +94,7 @@ class AdminFramework {
|
||||
* @param {String} config.apiUrl - API base URL
|
||||
* @param {String} config.uploadUrl - upload URL (可选,默认为 apiUrl + 'upload')
|
||||
* @param {Object} config.componentMap - custom component map (optional)
|
||||
* @param {Component} config.HomePage - custom home page component (optional)
|
||||
* @param {Function} config.onReady - callback when app is ready (optional)
|
||||
* @returns {Object} Vue instance
|
||||
*/
|
||||
@@ -102,6 +104,11 @@ class AdminFramework {
|
||||
config.uploadUrl = config.apiUrl + (config.apiUrl.endsWith('/') ? 'upload' : '/upload')
|
||||
}
|
||||
|
||||
// 如果提供了自定义 HomePage,使用自定义的,否则使用默认的
|
||||
if (config.HomePage) {
|
||||
this.HomePage = config.HomePage
|
||||
}
|
||||
|
||||
// 设置配置
|
||||
this.config = config
|
||||
|
||||
@@ -175,6 +182,7 @@ class AdminFramework {
|
||||
Main: framework.Main,
|
||||
ParentView: framework.ParentView,
|
||||
Page404: framework.Page404,
|
||||
HomePage: framework.HomePage,
|
||||
authorityMenus: authorityMenus
|
||||
})
|
||||
console.log('Menu restored')
|
||||
|
||||
@@ -44,7 +44,7 @@ export default {
|
||||
menuList: state => state.menuList
|
||||
},
|
||||
actions: {
|
||||
async setAuthorityMenus({ state, commit }, { Main, ParentView, Page404, authorityMenus, menuIds }) {
|
||||
async setAuthorityMenus({ state, commit }, { Main, ParentView, Page404, HomePage, authorityMenus, menuIds }) {
|
||||
// 如果传入了 authorityMenus,直接使用;否则从接口获取
|
||||
let menus = authorityMenus
|
||||
|
||||
@@ -95,9 +95,9 @@ export default {
|
||||
commit('setAuthorityMenus', JSON.stringify(menus))
|
||||
|
||||
// 生成路由菜单(传递 HomePage 组件)
|
||||
// 从框架导出的组件中获取 HomePage
|
||||
const HomePage = window.framework && HomePage ? HomePage : null
|
||||
let mainMenu = uiTool.getRoutes(Main, ParentView, Page404, HomePage)
|
||||
// 优先使用传入的 HomePage,如果没有则从框架实例中获取
|
||||
const homePageComponent = HomePage || (window.framework && window.framework.HomePage) || null
|
||||
let mainMenu = uiTool.getRoutes(Main, ParentView, Page404, homePageComponent)
|
||||
console.log('生成的主菜单:', mainMenu)
|
||||
|
||||
if (mainMenu && mainMenu.children) {
|
||||
@@ -182,10 +182,13 @@ export default {
|
||||
|
||||
// 调用 authorityMenus 接口获取完整菜单数据
|
||||
// 如果接口失败,会使用默认菜单配置和 menuIds 进行过滤
|
||||
// 从框架实例获取 HomePage
|
||||
const HomePage = (window.framework && window.framework.HomePage) || null
|
||||
await dispatch('setAuthorityMenus', {
|
||||
Main,
|
||||
ParentView,
|
||||
Page404,
|
||||
HomePage,
|
||||
menuIds
|
||||
})
|
||||
|
||||
|
||||
@@ -24,8 +24,14 @@ import Page500 from './error-page/500.vue'
|
||||
|
||||
// 设置组件映射的方法
|
||||
export function setupComponentMap(customMap = {}, uiTool) {
|
||||
|
||||
|
||||
if (!customMap["home/index"]) {
|
||||
customMap["home/index"] = HomePage
|
||||
}
|
||||
|
||||
|
||||
const componentMap = {
|
||||
'home/index': HomePage,
|
||||
'system/sys_log': SysLog,
|
||||
'system/sys_param_setup': SysParamSetup,
|
||||
'system/sys_role': SysRole,
|
||||
@@ -48,8 +54,8 @@ export function setupComponentMap(customMap = {}, uiTool) {
|
||||
}
|
||||
|
||||
export default {
|
||||
HomePage,
|
||||
SysLog,
|
||||
SysLogOperate,
|
||||
SysParamSetup,
|
||||
SysRole,
|
||||
SysUser,
|
||||
|
||||
@@ -251,7 +251,16 @@ export default {
|
||||
let menuTree = uiTool.transformTree(menuRows)
|
||||
this.gridOption.menuData = this.mapTree(menuTree)
|
||||
|
||||
this.$store.dispatch('setAuthorityMenus')
|
||||
// 菜单更新后重新加载权限菜单
|
||||
const framework = this.$framework || window.framework
|
||||
if (framework) {
|
||||
this.$store.dispatch('user/setAuthorityMenus', {
|
||||
Main: framework.components.Main,
|
||||
ParentView: framework.components.ParentView,
|
||||
Page404: framework.pages.Page404,
|
||||
HomePage: framework.HomePage
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
async initCol() {
|
||||
|
||||
Reference in New Issue
Block a user