mirror of
https://github.com/XiaoDaiGua-Ray/ray-template.git
synced 2025-04-06 03:57:49 +08:00
table组件完成
This commit is contained in:
parent
26d8050fff
commit
73110d5e2a
@ -4,4 +4,5 @@ auto-imports.d.ts
|
||||
components.d.ts
|
||||
.gitignore
|
||||
.vscode
|
||||
public
|
||||
public
|
||||
yarn.*
|
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -10,5 +10,6 @@ declare module '@vue/runtime-core' {
|
||||
RayTransitionComponent: typeof import('./src/components/RayTransitionComponent/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
TableSetting: typeof import('./src/components/RayTable/src/components/TableSetting/index.vue')['default']
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
$iconSpace: 5px;
|
||||
$width: 140px;
|
||||
$activedColor: #2080f0;
|
||||
|
||||
.ray-table__setting-option--draggable {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, $width);
|
||||
grid-gap: 10px 0;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
& .draggable-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
& .draggable-item__d--icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
& .draggable-item__d--icon {
|
||||
transition: opacity 0.3s var(--r-bezier), transform 0.3s var(--r-bezier);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
& .draggable-item__d--icon,
|
||||
& .draggable-item__icon {
|
||||
padding: $iconSpace;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
& .draggable-item__icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& .draggable-item__icon {
|
||||
&.draggable-item__icon--actived {
|
||||
color: $activedColor;
|
||||
}
|
||||
}
|
||||
|
||||
& .n-ellipsis {
|
||||
max-width: 71px;
|
||||
min-width: 71px;
|
||||
}
|
||||
}
|
||||
}
|
@ -9,15 +9,157 @@
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import './index.scss'
|
||||
import { NCard, NPopover, NEllipsis, NCheckbox } from 'naive-ui'
|
||||
import RayIcon from '@/components/RayIcon/index'
|
||||
import VueDraggable from 'vuedraggable'
|
||||
|
||||
import type {
|
||||
RayTableProvider,
|
||||
SettingOptions,
|
||||
ActionOptions,
|
||||
} from '@/components/RayTable/src/type'
|
||||
|
||||
const TableSetting = defineComponent({
|
||||
name: 'TableSetting',
|
||||
setup() {
|
||||
return {}
|
||||
emits: ['columnsUpdate'],
|
||||
setup(_, { emit }) {
|
||||
const rayTableProvider = inject('rayTableProvider', {} as RayTableProvider)
|
||||
const settingOptions = ref(rayTableProvider.modelColumns.value) // 表格表头
|
||||
const disableDraggable = ref(true) // 拖拽开关
|
||||
|
||||
const handleDraggableEnd = () => {
|
||||
emit('columnsUpdate', settingOptions.value)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type 列所需固定方向
|
||||
* @param idx 当前操作栏索引位置
|
||||
*
|
||||
* @remark 操作栏锁定列
|
||||
*/
|
||||
const handleFiexClick = (type: 'left' | 'right', idx: number) => {
|
||||
const key = `${type}FiexActivated`
|
||||
const value = settingOptions.value[idx]
|
||||
|
||||
value[key] = !value[key]
|
||||
|
||||
if (value[key]) {
|
||||
value.fixed = type
|
||||
} else {
|
||||
value.fixed = void 0
|
||||
}
|
||||
|
||||
settingOptions.value[idx] = value
|
||||
|
||||
emit('columnsUpdate', settingOptions.value)
|
||||
}
|
||||
|
||||
return {
|
||||
settingOptions,
|
||||
handleDraggableEnd,
|
||||
handleFiexClick,
|
||||
disableDraggable,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return <></>
|
||||
return (
|
||||
<NPopover trigger="click" placement="bottom" showArrow={false} raw>
|
||||
{{
|
||||
trigger: () => (
|
||||
<RayIcon
|
||||
customClassName="ray-table__setting"
|
||||
name="setting"
|
||||
size="18"
|
||||
/>
|
||||
),
|
||||
default: () => (
|
||||
<NCard bordered={false} segmented={{ content: 'soft' }}>
|
||||
{{
|
||||
default: () => (
|
||||
<VueDraggable
|
||||
class={['ray-table__setting-option--draggable']}
|
||||
v-model={this.settingOptions}
|
||||
itemKey="key"
|
||||
disabled={!this.disableDraggable}
|
||||
onEnd={this.handleDraggableEnd.bind(this)}
|
||||
>
|
||||
{{
|
||||
item: ({
|
||||
element,
|
||||
index,
|
||||
}: {
|
||||
element: ActionOptions
|
||||
index: number
|
||||
}) => (
|
||||
<div class={['draggable-item']}>
|
||||
<RayIcon
|
||||
customClassName={`draggable-item__d--icon`}
|
||||
name="draggable"
|
||||
size="18"
|
||||
/>
|
||||
<NEllipsis>
|
||||
<span>{element.title}</span>
|
||||
</NEllipsis>
|
||||
<NPopover>
|
||||
{{
|
||||
trigger: () => (
|
||||
<RayIcon
|
||||
customClassName={`draggable-item__icon ${
|
||||
element.leftFiexActivated
|
||||
? 'draggable-item__icon--actived'
|
||||
: ''
|
||||
}`}
|
||||
name="left_arrow"
|
||||
size="18"
|
||||
onClick={this.handleFiexClick.bind(
|
||||
this,
|
||||
'left',
|
||||
index,
|
||||
)}
|
||||
/>
|
||||
),
|
||||
default: () => '向左固定',
|
||||
}}
|
||||
</NPopover>
|
||||
<NPopover>
|
||||
{{
|
||||
trigger: () => (
|
||||
<RayIcon
|
||||
customClassName={`draggable-item__icon ${
|
||||
element.rightFiexActivated
|
||||
? 'draggable-item__icon--actived'
|
||||
: ''
|
||||
}`}
|
||||
name="right_arrow"
|
||||
size="18"
|
||||
onClick={this.handleFiexClick.bind(
|
||||
this,
|
||||
'right',
|
||||
index,
|
||||
)}
|
||||
/>
|
||||
),
|
||||
default: () => '向右固定',
|
||||
}}
|
||||
</NPopover>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</VueDraggable>
|
||||
),
|
||||
header: () => (
|
||||
<NCheckbox v-model:checked={this.disableDraggable}>
|
||||
拖拽
|
||||
</NCheckbox>
|
||||
),
|
||||
}}
|
||||
</NCard>
|
||||
),
|
||||
}}
|
||||
</NPopover>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -10,42 +10,135 @@
|
||||
*/
|
||||
|
||||
import './index.scss'
|
||||
import { NDataTable, NCard } from 'naive-ui'
|
||||
import { NDataTable, NCard, NDropdown } from 'naive-ui'
|
||||
import props from './props'
|
||||
import RayIcon from '@/components/RayIcon/index'
|
||||
import TableSetting from './components/TableSetting/index'
|
||||
|
||||
import type { ActionOptions } from './type'
|
||||
import type { WritableComputedRef } from 'vue'
|
||||
import type { DropdownOption } from 'naive-ui'
|
||||
|
||||
const RayTable = defineComponent({
|
||||
name: 'RayTable',
|
||||
props: props,
|
||||
setup(props) {
|
||||
emits: ['update:columns', 'menuSelect'],
|
||||
setup(props, { emit }) {
|
||||
const modelRightClickMenu = computed(() => props.rightClickMenu)
|
||||
const modelColumns = computed({
|
||||
get: () => props.columns,
|
||||
set: (arr) => {
|
||||
emit('update:columns', arr)
|
||||
},
|
||||
}) as unknown as WritableComputedRef<ActionOptions[]>
|
||||
const menuConfig = reactive({
|
||||
x: 0,
|
||||
y: 0,
|
||||
showMenu: false,
|
||||
})
|
||||
let prevRightClickIndex = -1
|
||||
|
||||
provide('rayTableProvider', {
|
||||
modelRightClickMenu,
|
||||
modelColumns,
|
||||
})
|
||||
|
||||
return {}
|
||||
const handleColumnsUpdate = (arr: ActionOptions[]) => {
|
||||
modelColumns.value = arr
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key 右键菜单当前选择 `key`
|
||||
* @param option 右键菜单当前 `item`
|
||||
*
|
||||
* @remark (key: string | number, index: number,option: DropdownOption) => void
|
||||
*/
|
||||
const handleRightMenuSelect = (
|
||||
key: string | number,
|
||||
option: DropdownOption,
|
||||
) => {
|
||||
emit('menuSelect', key, prevRightClickIndex, option)
|
||||
|
||||
menuConfig.showMenu = false
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arr 表格当前行
|
||||
* @param idx 表格当前索引位置
|
||||
* @returns 自定义属性集
|
||||
*
|
||||
* @remark 集成右键菜单属性, 会自动拦截右键方法, 会自动合并自定义行属性
|
||||
*/
|
||||
const handleRowProps = (arr: ActionOptions, idx: number) => {
|
||||
const interceptRowProps = props.rowProps?.(arr, idx)
|
||||
|
||||
return {
|
||||
...interceptRowProps,
|
||||
onContextmenu: (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
prevRightClickIndex = idx
|
||||
|
||||
menuConfig.showMenu = false
|
||||
|
||||
nextTick().then(() => {
|
||||
menuConfig.showMenu = true
|
||||
|
||||
menuConfig.x = e.clientX
|
||||
menuConfig.y = e.clientY
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
handleColumnsUpdate,
|
||||
...toRefs(menuConfig),
|
||||
handleRowProps,
|
||||
handleRightMenuSelect,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<NCard bordered={false}>
|
||||
{{
|
||||
default: () => (
|
||||
<NDataTable {...this.$props}>
|
||||
{{
|
||||
empty: () => this.$slots?.empty,
|
||||
loading: () => this.$slots?.loading,
|
||||
}}
|
||||
</NDataTable>
|
||||
<div>
|
||||
<NDataTable
|
||||
{...this.$props}
|
||||
rowProps={this.handleRowProps.bind(this)}
|
||||
>
|
||||
{{
|
||||
empty: () => this.$slots?.empty,
|
||||
loading: () => this.$slots?.loading,
|
||||
}}
|
||||
</NDataTable>
|
||||
{this.showMenu ? (
|
||||
<NDropdown
|
||||
show={this.showMenu}
|
||||
placement="bottom-start"
|
||||
trigger="manual"
|
||||
x={this.x}
|
||||
y={this.y}
|
||||
options={this.rightClickMenu}
|
||||
onClickoutside={() => (this.showMenu = false)}
|
||||
onSelect={this.handleRightMenuSelect.bind(this)}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
header: () => this.title,
|
||||
'header-extra': () => (
|
||||
<RayIcon
|
||||
customClassName="ray-table__setting"
|
||||
name="setting"
|
||||
size="18"
|
||||
/>
|
||||
),
|
||||
'header-extra': () =>
|
||||
this.action ? (
|
||||
<TableSetting
|
||||
onColumnsUpdate={this.handleColumnsUpdate.bind(this)}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
),
|
||||
}}
|
||||
</NCard>
|
||||
)
|
||||
@ -53,3 +146,16 @@ const RayTable = defineComponent({
|
||||
})
|
||||
|
||||
export default RayTable
|
||||
|
||||
/**
|
||||
*
|
||||
* 完全继承 `NDataTable`
|
||||
*
|
||||
* 以实现抬头, 操作栏, 右键菜单功能拓展
|
||||
*
|
||||
* 右键菜单功能, 需要同时启用 `showMenu` 与配置菜单选项才能正常使用
|
||||
*
|
||||
* 可以通过设置 `action` 为 `false` 隐藏操作栏
|
||||
*
|
||||
* 具体拓展 `props` 方法, 可以查看 `props.ts` 中相关注释与代码
|
||||
*/
|
||||
|
@ -58,6 +58,16 @@ const rayTableProps = {
|
||||
type: Object as PropType<VNode>,
|
||||
default: () => ({}),
|
||||
},
|
||||
showMenu: {
|
||||
/**
|
||||
*
|
||||
* 是否展示右键菜单
|
||||
*
|
||||
* 默认启用
|
||||
*/
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
} as const
|
||||
|
||||
export default rayTableProps
|
||||
@ -66,5 +76,5 @@ export default rayTableProps
|
||||
*
|
||||
* `Ray Table Props`
|
||||
*
|
||||
* 继承 `naive ui Data Table`
|
||||
* 继承 `Naive UI Data Table`
|
||||
*/
|
||||
|
@ -3,13 +3,26 @@ import type {
|
||||
DropdownGroupOption,
|
||||
DropdownDividerOption,
|
||||
DropdownRenderOption,
|
||||
DataTableColumns,
|
||||
DataTableBaseColumn,
|
||||
} from 'naive-ui'
|
||||
import type { ComputedRef, WritableComputedRef } from 'vue'
|
||||
|
||||
export interface ActionOptions extends DataTableColumns {}
|
||||
export interface ActionOptions extends DataTableBaseColumn {
|
||||
leftFiexActivated?: boolean // 向左固定
|
||||
rightFiexActivated?: boolean // 向右固定
|
||||
}
|
||||
|
||||
export type DropdownMixedOption =
|
||||
| DropdownOption
|
||||
| DropdownGroupOption
|
||||
| DropdownDividerOption
|
||||
| DropdownRenderOption
|
||||
|
||||
export type SettingOptions = WritableComputedRef<ActionOptions[]>
|
||||
|
||||
export type RightClickMenu = ComputedRef<DropdownMixedOption[]>
|
||||
|
||||
export interface RayTableProvider {
|
||||
modelRightClickMenu: RightClickMenu
|
||||
modelColumns: SettingOptions
|
||||
}
|
||||
|
3
src/icons/draggable.svg
Normal file
3
src/icons/draggable.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg t="1670566360312" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="23119" width="64" height="64">
|
||||
<path fill="currentColor" d="M368 672a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m288 0a64 64 0 1 1 0 128 64 64 0 0 1 0-128zM368 448a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m288 0a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m-288-224a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m288 0a64 64 0 1 1 0 128 64 64 0 0 1 0-128z" p-id="23120"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 475 B |
3
src/icons/left_arrow.svg
Normal file
3
src/icons/left_arrow.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg t="1670552678924" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="21515" width="64" height="64">
|
||||
<path d="M473.6 490.666667L789.333333 170.666667 853.333333 230.4l-260.266666 260.266667 260.266666 260.266666-64 59.733334-315.733333-320z m-302.933333 0L490.666667 170.666667l59.733333 59.733333-260.266667 260.266667 260.266667 260.266666-59.733333 59.733334L170.666667 490.666667z" fill="currentColor" p-id="21516"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 480 B |
3
src/icons/right_arrow.svg
Normal file
3
src/icons/right_arrow.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg t="1670552691680" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="21718" width="64" height="64">
|
||||
<path d="M550.4 490.666667L230.4 170.666667 170.666667 230.4l260.266666 260.266667L170.666667 750.933333 230.4 810.666667l320-320z m298.666667 0L533.333333 170.666667 469.333333 230.4l260.266667 260.266667-260.266667 260.266666 59.733334 59.733334 320-320z" fill="currentColor" p-id="21719"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 453 B |
@ -1,4 +1,5 @@
|
||||
@import "@/styles/animate.scss";
|
||||
@import "@/styles/root.scss";
|
||||
|
||||
body,
|
||||
h1,
|
||||
|
3
src/styles/root.scss
Normal file
3
src/styles/root.scss
Normal file
@ -0,0 +1,3 @@
|
||||
:root {
|
||||
--r-bezier: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
@ -9,26 +9,173 @@
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { NLayout, NCard } from 'naive-ui'
|
||||
import { NLayout, NCard, NTag, NButton } from 'naive-ui'
|
||||
import RayTable from '@/components/RayTable/index'
|
||||
|
||||
import type { DataTableColumns } from 'naive-ui'
|
||||
|
||||
type RowData = {
|
||||
key: number
|
||||
name: string
|
||||
age: number
|
||||
address: string
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
const TableView = defineComponent({
|
||||
name: 'TableView',
|
||||
setup() {
|
||||
return {}
|
||||
const baseColumns = [
|
||||
{
|
||||
title: 'Name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
key: 'age',
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
key: 'address',
|
||||
},
|
||||
{
|
||||
title: 'Tags',
|
||||
key: 'tags',
|
||||
render: (row: RowData) => {
|
||||
const tags = row.tags.map((tagKey) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: 'info',
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => tagKey,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
return tags
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'actions',
|
||||
render: (row: RowData) =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
},
|
||||
{ default: () => 'Send Email' },
|
||||
),
|
||||
},
|
||||
]
|
||||
const actionColumns = ref<DataTableColumns<RowData>>(
|
||||
[...baseColumns].map((curr) => ({ ...curr, width: 400 })),
|
||||
)
|
||||
const tableData = ref([
|
||||
{
|
||||
key: 0,
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
tags: ['nice', 'developer'],
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['wow'],
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher'],
|
||||
},
|
||||
])
|
||||
const tableMenuOptions = [
|
||||
{
|
||||
label: '编辑',
|
||||
key: 'edit',
|
||||
},
|
||||
{
|
||||
label: () => h('span', { style: { color: 'red' } }, '删除'),
|
||||
key: 'delete',
|
||||
},
|
||||
]
|
||||
|
||||
const handleMenuSelect = (key: string | number, idx: number) => {
|
||||
if (key === 'delete') {
|
||||
tableData.value.splice(idx, 1)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tableData,
|
||||
actionColumns,
|
||||
baseColumns,
|
||||
tableMenuOptions,
|
||||
handleMenuSelect,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<NLayout>
|
||||
<NCard title="RayTable">
|
||||
该组件基于 Naive UI DataTable 组件封装. 实现右键菜单, 表格标题,
|
||||
操作栏等功能
|
||||
<p>
|
||||
该组件基于 Naive UI DataTable 组件封装. 实现右键菜单, 表格标题,
|
||||
操作栏等功能
|
||||
</p>
|
||||
<p>RayTable 完全继承 DataTable 的所有属性与方法</p>
|
||||
<p>
|
||||
相关拓展 props 属性, 可以在源码位置
|
||||
src/components/RayTable/src/props.ts 中查看相关代码与注释
|
||||
</p>
|
||||
</NCard>
|
||||
<NCard title="基础使用" style={['margin-top: 18px']}>
|
||||
<RayTable title="基础表格" />
|
||||
<RayTable
|
||||
title="基础表格"
|
||||
data={this.tableData}
|
||||
columns={this.baseColumns}
|
||||
action={false}
|
||||
/>
|
||||
</NCard>
|
||||
<NCard style={['margin-top: 18px']}>
|
||||
<RayTable />
|
||||
{{
|
||||
header: () => (
|
||||
<div>
|
||||
<p>
|
||||
使用响应式方法代理 columns 并且打开 action 则可以启用操作栏
|
||||
</p>
|
||||
<p>拖拽操作栏动态切换表格列</p>
|
||||
<p>点击左右固定按钮, 即可动态固定列</p>
|
||||
</div>
|
||||
),
|
||||
default: () => (
|
||||
<RayTable
|
||||
title="带有拓展功能的表格"
|
||||
data={this.tableData}
|
||||
v-model:columns={this.actionColumns}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
</NCard>
|
||||
<NCard title="右键菜单" style={['margin-top: 18px']}>
|
||||
<RayTable
|
||||
title="右键菜单表格"
|
||||
action={false}
|
||||
data={this.tableData}
|
||||
columns={this.baseColumns}
|
||||
rightClickMenu={this.tableMenuOptions}
|
||||
onMenuSelect={this.handleMenuSelect.bind(this)}
|
||||
/>
|
||||
</NCard>
|
||||
</NLayout>
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user