Compare commits

...

2 Commits

Author SHA1 Message Date
张成
b122a34331 1 2025-12-19 13:14:42 +08:00
张成
463d7921c1 1 2025-11-26 17:50:14 +08:00
3 changed files with 232 additions and 16 deletions

View File

@@ -11,7 +11,7 @@
-**登录和权限管理** - 完整的登录流程和权限控制 -**登录和权限管理** - 完整的登录流程和权限控制
-**动态路由管理** - 基于权限菜单的动态路由生成 -**动态路由管理** - 基于权限菜单的动态路由生成
-**Vuex 状态管理** - 用户、应用状态管理 -**Vuex 状态管理** - 用户、应用状态管理
-**全局组件库** - Tables、Editor、Upload、TreeGrid、FieldRenderer 等 -**全局组件库** - Tables、Editor、Upload、TreeGrid、FieldRenderer、FloatPanel
-**工具库** - HTTP、日期、Token、Cookie 等工具 -**工具库** - HTTP、日期、Token、Cookie 等工具
-**内置样式** - base.less、animate.css、iconfont 等 -**内置样式** - base.less、animate.css、iconfont 等
-**响应式布局** - 支持移动端适配 -**响应式布局** - 支持移动端适配
@@ -450,6 +450,166 @@ export default {
-**浏览器兼容**:支持所有现代浏览器 -**浏览器兼容**:支持所有现代浏览器
-**内存管理**:自动清理临时 URL 对象 -**内存管理**:自动清理临时 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%,占满父容器
- ✅ 无遮罩背景,完全浮在父页面上
- ✅ 路由切换或组件销毁时自动关闭
- ✅ 支持多种位置和动画效果
- ✅ 支持自定义头部右侧内容
## 📝 业务开发示例 ## 📝 业务开发示例
### 创建业务页面 ### 创建业务页面

View File

@@ -172,6 +172,7 @@ app.$mount('#app')
-`<AsyncModal>` - 异步弹窗 -`<AsyncModal>` - 异步弹窗
-`<Editor>` - 富文本编辑器 -`<Editor>` - 富文本编辑器
-`<CommonIcon>` - 图标选择器 -`<CommonIcon>` - 图标选择器
-`<FloatPanel>` - 浮动面板
### 4. 工具方法 ### 4. 工具方法

View File

@@ -90,13 +90,27 @@ export default {
}, },
insideColumns() { insideColumns() {
// 确保 columns 是数组
if (!Array.isArray(this.columns) || this.columns.length === 0) {
return []
}
let columns = this.columns.map((item, index) => { let columns = this.columns.map((item, index) => {
// 确保 item 是对象
if (!item || typeof item !== 'object') {
console.warn(`[Tables] 列配置第 ${index} 项格式不正确:`, item)
return null
}
// 创建新对象,避免直接修改原对象
let column = { ...item }
// 设置默认对齐方式 // 设置默认对齐方式
if (!item.align) { if (!column.align) {
item.align = 'center' column.align = 'center'
if (item.children && item.children.length > 0) { if (column.children && column.children.length > 0) {
item.children = item.children.map((sonItem) => { column.children = column.children.map((sonItem) => {
if (!sonItem.align) { if (!sonItem.align) {
sonItem.align = 'center' sonItem.align = 'center'
} }
@@ -107,12 +121,12 @@ export default {
// 确保列宽设置正确,优先使用 width其次使用 minWidth // 确保列宽设置正确,优先使用 width其次使用 minWidth
// 如果都没有设置,则设置默认 minWidth // 如果都没有设置,则设置默认 minWidth
if (!item.width && !item.minWidth) { if (!column.width && !column.minWidth) {
item.minWidth = 120 column.minWidth = 120
} }
return item return column
}) }).filter(item => item !== null) // 过滤掉无效的列配置
return columns return columns
}, },
@@ -139,11 +153,27 @@ export default {
}, },
handleTableData() { handleTableData() {
let values = this.value || [] // 确保 value 是数组
let data = values.filter((p) => p) let values = Array.isArray(this.value) ? this.value : []
this.insideTableData = data.map((item, index) => {
let res = item // 过滤掉 null 和 undefined但保留其他 falsy 值(如 0、false、空字符串等
return res 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) { onChangePage(page) {
@@ -177,11 +207,28 @@ export default {
}, },
}, },
watch: { watch: {
value(val) { // 监听 value 变化
this.handleTableData() 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() { mounted() {
// 确保数据已处理
this.handleTableData() this.handleTableData()
// 自适应高度 // 自适应高度
@@ -196,6 +243,14 @@ export default {
// 动态计算偏移量 // 动态计算偏移量
this.calculateOffset() this.calculateOffset()
// 如果数据或列配置为空,输出警告
if (!this.insideTableData || this.insideTableData.length === 0) {
console.warn('[Tables] 表格数据为空,请检查 value 属性是否正确绑定')
}
if (!this.columns || this.columns.length === 0) {
console.warn('[Tables] 表格列配置为空,请检查 columns 属性是否正确绑定')
}
}) })
// 监听窗口大小变化,重新计算偏移量 // 监听窗口大小变化,重新计算偏移量