优化
This commit is contained in:
@@ -302,6 +302,7 @@
|
|||||||
"useMediaQuery": true,
|
"useMediaQuery": true,
|
||||||
"useMemoize": true,
|
"useMemoize": true,
|
||||||
"useMemory": true,
|
"useMemory": true,
|
||||||
|
"useMobileTable": true,
|
||||||
"useModel": true,
|
"useModel": true,
|
||||||
"useMounted": true,
|
"useMounted": true,
|
||||||
"useMouse": true,
|
"useMouse": true,
|
||||||
|
|||||||
2
auto-imports.d.ts
vendored
2
auto-imports.d.ts
vendored
@@ -336,6 +336,7 @@ declare global {
|
|||||||
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
|
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
|
||||||
const useMemoize: typeof import('@vueuse/core')['useMemoize']
|
const useMemoize: typeof import('@vueuse/core')['useMemoize']
|
||||||
const useMemory: typeof import('@vueuse/core')['useMemory']
|
const useMemory: typeof import('@vueuse/core')['useMemory']
|
||||||
|
const useMobileTable: typeof import('./src/composables/useMobileTable.js')['useMobileTable']
|
||||||
const useModel: typeof import('vue')['useModel']
|
const useModel: typeof import('vue')['useModel']
|
||||||
const useMounted: typeof import('@vueuse/core')['useMounted']
|
const useMounted: typeof import('@vueuse/core')['useMounted']
|
||||||
const useMouse: typeof import('@vueuse/core')['useMouse']
|
const useMouse: typeof import('@vueuse/core')['useMouse']
|
||||||
@@ -747,6 +748,7 @@ declare module 'vue' {
|
|||||||
readonly useMediaQuery: UnwrapRef<typeof import('@vueuse/core')['useMediaQuery']>
|
readonly useMediaQuery: UnwrapRef<typeof import('@vueuse/core')['useMediaQuery']>
|
||||||
readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
|
readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
|
||||||
readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
|
readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
|
||||||
|
readonly useMobileTable: UnwrapRef<typeof import('./src/composables/useMobileTable.js')['useMobileTable']>
|
||||||
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
||||||
readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
|
readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
|
||||||
readonly useMouse: UnwrapRef<typeof import('@vueuse/core')['useMouse']>
|
readonly useMouse: UnwrapRef<typeof import('@vueuse/core')['useMouse']>
|
||||||
|
|||||||
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -80,6 +80,7 @@ declare module 'vue' {
|
|||||||
ProductCard: typeof import('./src/components/product/ProductCard.vue')['default']
|
ProductCard: typeof import('./src/components/product/ProductCard.vue')['default']
|
||||||
ProductDocumentationDialog: typeof import('./src/components/admin/ProductDocumentationDialog.vue')['default']
|
ProductDocumentationDialog: typeof import('./src/components/admin/ProductDocumentationDialog.vue')['default']
|
||||||
ProductFormDialog: typeof import('./src/components/admin/ProductFormDialog.vue')['default']
|
ProductFormDialog: typeof import('./src/components/admin/ProductFormDialog.vue')['default']
|
||||||
|
ResponsiveActionColumn: typeof import('./src/components/common/ResponsiveActionColumn.vue')['default']
|
||||||
RichTextEditor: typeof import('./src/components/common/RichTextEditor.vue')['default']
|
RichTextEditor: typeof import('./src/components/common/RichTextEditor.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
|||||||
@@ -396,6 +396,138 @@
|
|||||||
.filter-buttons {
|
.filter-buttons {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== 移动端表格优化 ===== */
|
||||||
|
/* 表格容器允许横向滚动 */
|
||||||
|
.table-wrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
/* 隐藏滚动条但保持滚动功能 */
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移除固定列效果 - 通过覆盖 Element Plus 的固定列样式 */
|
||||||
|
.list-page-container .el-table .el-table__fixed,
|
||||||
|
.list-page-container .el-table .el-table__fixed-right {
|
||||||
|
position: static !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 固定列的表头和表体都改为静态定位 */
|
||||||
|
.list-page-container .el-table .el-table__fixed-header-wrapper,
|
||||||
|
.list-page-container .el-table .el-table__fixed-body-wrapper,
|
||||||
|
.list-page-container .el-table .el-table__fixed-footer-wrapper {
|
||||||
|
position: static !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格单元格在移动端优化 */
|
||||||
|
.list-page-container .el-table th,
|
||||||
|
.list-page-container .el-table td {
|
||||||
|
padding: 12px 8px !important;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮组在移动端改为紧凑布局 */
|
||||||
|
.list-page-container .el-table .el-table__cell .flex.gap-2,
|
||||||
|
.list-page-container .el-table .el-table__cell .flex.items-center,
|
||||||
|
.list-page-container .el-table .el-table__cell .flex.space-x-2 {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮在移动端缩小 */
|
||||||
|
.list-page-container .el-table .el-button--small {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格列宽度优化 - 允许更灵活的宽度 */
|
||||||
|
.list-page-container .el-table .el-table__cell {
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作列宽度自适应,不设置最小宽度 */
|
||||||
|
.list-page-container .el-table .el-table__cell[data-label="操作"],
|
||||||
|
.list-page-container .el-table th:last-child,
|
||||||
|
.list-page-container .el-table td:last-child {
|
||||||
|
min-width: auto !important;
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 隐藏部分次要列在移动端 - 通过类名控制 */
|
||||||
|
.list-page-container .el-table .el-table__cell.hidden-mobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮在移动端自动换行,避免溢出 */
|
||||||
|
.list-page-container .el-table .el-table__cell .el-button + .el-button {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格在移动端允许横向滚动 */
|
||||||
|
.list-page-container .el-table {
|
||||||
|
min-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作列在移动端不设置最小宽度,允许换行 */
|
||||||
|
.list-page-container .el-table .el-table__cell[data-label="操作"] {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮组在移动端更紧凑 */
|
||||||
|
.list-page-container .el-table .el-table__cell .flex {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 下拉菜单按钮在移动端优化 */
|
||||||
|
.list-page-container .el-table .el-dropdown .el-button {
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 超小屏幕进一步优化 */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.list-page-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-header {
|
||||||
|
padding: 16px 12px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-title {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filters {
|
||||||
|
padding: 16px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-table {
|
||||||
|
padding: 0 12px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格单元格进一步缩小 */
|
||||||
|
.list-page-container .el-table th,
|
||||||
|
.list-page-container .el-table td {
|
||||||
|
padding: 10px 6px !important;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮更紧凑 */
|
||||||
|
.list-page-container .el-table .el-button--small {
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮组更紧凑 */
|
||||||
|
.list-page-container .el-table .el-table__cell .flex.gap-2,
|
||||||
|
.list-page-container .el-table .el-table__cell .flex.items-center {
|
||||||
|
gap: 4px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 动画效果 */
|
/* 动画效果 */
|
||||||
|
|||||||
@@ -316,7 +316,6 @@ import RichTextEditor from '@/components/common/RichTextEditor.vue'
|
|||||||
import { Rank, Search } from '@element-plus/icons-vue'
|
import { Rank, Search } from '@element-plus/icons-vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
import draggable from 'vuedraggable'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ import { onMounted, onUnmounted, ref } from 'vue'
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 固定配置
|
// 固定配置
|
||||||
const FETCH_PAGE_SIZE = 5// 每次获取的记录数
|
const FETCH_PAGE_SIZE = 8// 每次获取的记录数
|
||||||
const BASE_EMIT_INTERVAL = 4700// 基础5秒,避免弹幕重叠
|
const BASE_EMIT_INTERVAL = 5500// 基础5秒,避免弹幕重叠
|
||||||
const RANDOM_EMIT_RANGE = 1000 // 0-1秒随机范围
|
const RANDOM_EMIT_RANGE = 2000 // 0-1秒随机范围
|
||||||
|
|
||||||
const enabled = ref(true)
|
const enabled = ref(true)
|
||||||
const danmakuWrapper = ref(null)
|
const danmakuWrapper = ref(null)
|
||||||
|
|||||||
@@ -38,6 +38,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { useMobileTable } from '@/composables/useMobileTable'
|
||||||
|
import { watch, nextTick, onMounted } from 'vue'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -48,6 +51,23 @@ defineProps({
|
|||||||
default: ''
|
default: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 移动端表格优化
|
||||||
|
const { isMobile, removeFixedColumns } = useMobileTable()
|
||||||
|
|
||||||
|
// 监听表格内容变化,重新应用优化
|
||||||
|
watch(() => isMobile.value, () => {
|
||||||
|
nextTick(() => {
|
||||||
|
removeFixedColumns()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 在组件挂载后应用优化
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
removeFixedColumns()
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
143
src/components/common/ResponsiveActionColumn.vue
Normal file
143
src/components/common/ResponsiveActionColumn.vue
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<template>
|
||||||
|
<div class="responsive-action-column">
|
||||||
|
<!-- 桌面端:显示所有按钮 -->
|
||||||
|
<div v-if="!isMobile" class="action-buttons-desktop">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 移动端:主要操作按钮 + 更多操作下拉菜单 -->
|
||||||
|
<div v-else class="action-buttons-mobile">
|
||||||
|
<!-- 主要操作按钮(最多显示2个) -->
|
||||||
|
<template v-for="(action, index) in primaryActions" :key="index">
|
||||||
|
<el-button
|
||||||
|
:type="action.type || 'default'"
|
||||||
|
:size="action.size || 'small'"
|
||||||
|
@click="action.handler"
|
||||||
|
>
|
||||||
|
{{ action.label }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 更多操作下拉菜单 -->
|
||||||
|
<el-dropdown v-if="moreActions.length > 0" @command="handleCommand" trigger="click">
|
||||||
|
<el-button type="info" size="small">
|
||||||
|
更多
|
||||||
|
<el-icon class="el-icon--right">
|
||||||
|
<ArrowDown />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-for="(action, index) in moreActions"
|
||||||
|
:key="index"
|
||||||
|
:command="action"
|
||||||
|
>
|
||||||
|
<el-icon v-if="action.icon" class="dropdown-item-icon">
|
||||||
|
<component :is="action.icon" />
|
||||||
|
</el-icon>
|
||||||
|
{{ action.label }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, useSlots } from 'vue'
|
||||||
|
import { useMobileTable } from '@/composables/useMobileTable'
|
||||||
|
import { ArrowDown } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 主要操作按钮数量(移动端显示)
|
||||||
|
primaryCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 2
|
||||||
|
},
|
||||||
|
// 操作按钮配置(如果使用配置方式)
|
||||||
|
actions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { isMobile } = useMobileTable()
|
||||||
|
const slots = useSlots()
|
||||||
|
|
||||||
|
// 从插槽中提取按钮信息(如果使用插槽方式)
|
||||||
|
const extractActionsFromSlots = () => {
|
||||||
|
if (!slots.default) return []
|
||||||
|
|
||||||
|
const actions = []
|
||||||
|
// 这里需要从插槽中解析按钮,但 Vue 3 的插槽是渲染函数,比较难解析
|
||||||
|
// 所以建议使用 actions prop 方式
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算主要操作和更多操作
|
||||||
|
const primaryActions = computed(() => {
|
||||||
|
if (props.actions.length > 0) {
|
||||||
|
return props.actions.slice(0, props.primaryCount)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
const moreActions = computed(() => {
|
||||||
|
if (props.actions.length > 0) {
|
||||||
|
return props.actions.slice(props.primaryCount)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理下拉菜单命令
|
||||||
|
const handleCommand = (action) => {
|
||||||
|
if (action && action.handler) {
|
||||||
|
action.handler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.responsive-action-column {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons-desktop {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons-mobile {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item-icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端按钮更紧凑 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.action-buttons-mobile .el-button {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.action-buttons-mobile .el-button {
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
101
src/composables/useMobileTable.js
Normal file
101
src/composables/useMobileTable.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端表格优化 composable
|
||||||
|
* 用于在移动端移除表格固定列,优化显示效果
|
||||||
|
*/
|
||||||
|
export function useMobileTable() {
|
||||||
|
const isMobile = ref(false)
|
||||||
|
const isTablet = ref(false)
|
||||||
|
|
||||||
|
// 检测屏幕尺寸
|
||||||
|
const checkScreenSize = () => {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
const width = window.innerWidth
|
||||||
|
isMobile.value = width < 768
|
||||||
|
isTablet.value = width >= 768 && width < 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除表格固定列
|
||||||
|
const removeFixedColumns = () => {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
|
||||||
|
// 只在移动端执行
|
||||||
|
if (!isMobile.value) {
|
||||||
|
// 桌面端恢复固定列样式
|
||||||
|
const tables = document.querySelectorAll('.list-page-container .el-table')
|
||||||
|
tables.forEach((table) => {
|
||||||
|
const fixedElements = table.querySelectorAll('.el-table__fixed, .el-table__fixed-right')
|
||||||
|
fixedElements.forEach((el) => {
|
||||||
|
el.style.position = ''
|
||||||
|
el.style.boxShadow = ''
|
||||||
|
el.style.backgroundColor = ''
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 nextTick 确保 DOM 已更新
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const tables = document.querySelectorAll('.list-page-container .el-table')
|
||||||
|
tables.forEach((table) => {
|
||||||
|
// 移除固定列元素
|
||||||
|
const fixedElements = table.querySelectorAll('.el-table__fixed, .el-table__fixed-right')
|
||||||
|
fixedElements.forEach((el) => {
|
||||||
|
el.style.position = 'static'
|
||||||
|
el.style.boxShadow = 'none'
|
||||||
|
el.style.backgroundColor = 'transparent'
|
||||||
|
el.style.zIndex = 'auto'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 移除固定列的表头、表体、表尾包装器
|
||||||
|
const fixedWrappers = table.querySelectorAll(
|
||||||
|
'.el-table__fixed-header-wrapper, .el-table__fixed-body-wrapper, .el-table__fixed-footer-wrapper'
|
||||||
|
)
|
||||||
|
fixedWrappers.forEach((el) => {
|
||||||
|
el.style.position = 'static'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 移除固定列的遮罩层
|
||||||
|
const fixedPatch = table.querySelectorAll('.el-table__fixed-right-patch, .el-table__fixed-patch')
|
||||||
|
fixedPatch.forEach((el) => {
|
||||||
|
el.style.display = 'none'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, 150)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听窗口大小变化
|
||||||
|
const handleResize = () => {
|
||||||
|
const wasMobile = isMobile.value
|
||||||
|
checkScreenSize()
|
||||||
|
// 如果移动状态发生变化,重新应用优化
|
||||||
|
if (wasMobile !== isMobile.value) {
|
||||||
|
removeFixedColumns()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkScreenSize()
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('resize', handleResize)
|
||||||
|
// 初始移除固定列
|
||||||
|
removeFixedColumns()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.removeEventListener('resize', handleResize)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
isMobile,
|
||||||
|
isTablet,
|
||||||
|
removeFixedColumns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user