mirror of https://gitee.com/xiaonuobase/snowy
【升级】前端表格组件优化、新图标选择器优化并加入readme说明
parent
9650871e90
commit
f88765a34c
|
@ -24,6 +24,8 @@
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import Draggable from 'vuedraggable-es'
|
import Draggable from 'vuedraggable-es'
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
const emit = defineEmits(['columnChange'])
|
const emit = defineEmits(['columnChange'])
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
columns: {
|
columns: {
|
||||||
|
@ -35,25 +37,47 @@
|
||||||
const indeterminate = ref(false)
|
const indeterminate = ref(false)
|
||||||
const checkAll = ref(true)
|
const checkAll = ref(true)
|
||||||
const columnsSetting = ref([])
|
const columnsSetting = ref([])
|
||||||
const originColumns = ref()
|
const originColumns = ref([])
|
||||||
|
|
||||||
onMounted(() => {
|
const emitColumnChange = () => {
|
||||||
columnsSetting.value = props.columns.map((value) => {
|
// 确保发送的数据包含所有必要的列信息,并保持响应性
|
||||||
if (value.checked === undefined) {
|
const updatedColumns = columnsSetting.value.map((col) => ({
|
||||||
return {
|
...col,
|
||||||
...value,
|
hidden: !col.checked,
|
||||||
checked: true
|
checked: col.checked,
|
||||||
|
show: col.checked // 添加show属性以确保列的显示状态正确同步
|
||||||
|
}))
|
||||||
|
// 触发列变化事件,确保父组件能够接收到完整的列信息
|
||||||
|
emit('columnChange', updatedColumns)
|
||||||
}
|
}
|
||||||
} else return value
|
|
||||||
})
|
|
||||||
|
|
||||||
// 这里要用深的拷贝,否则,勾选了字段时会修改了originColumns里的内容
|
// 初始化列设置
|
||||||
|
const initializeColumns = (columns) => {
|
||||||
|
columnsSetting.value = columns.map((value) => ({
|
||||||
|
...value,
|
||||||
|
checked: value.checked !== undefined ? value.checked : !value.hidden
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 深拷贝保存原始列设置
|
||||||
originColumns.value = columnsSetting.value.map((value) => ({ ...value }))
|
originColumns.value = columnsSetting.value.map((value) => ({ ...value }))
|
||||||
|
|
||||||
// 处理全选组件
|
// 处理全选状态
|
||||||
const notCheckedList = columnsSetting.value.filter((value) => !value.checked)
|
const notCheckedList = columnsSetting.value.filter((value) => !value.checked)
|
||||||
if (notCheckedList.length) checkAll.value = false
|
checkAll.value = !notCheckedList.length
|
||||||
})
|
indeterminate.value = notCheckedList.length > 0 && notCheckedList.length < columnsSetting.value.length
|
||||||
|
|
||||||
|
// 触发列变化事件
|
||||||
|
emitColumnChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听props.columns的变化
|
||||||
|
watch(
|
||||||
|
() => props.columns,
|
||||||
|
(newColumns) => {
|
||||||
|
initializeColumns(newColumns)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
columnsSetting.value = originColumns.value.map((value) => ({ ...value }))
|
columnsSetting.value = originColumns.value.map((value) => ({ ...value }))
|
||||||
|
@ -83,11 +107,6 @@
|
||||||
}))
|
}))
|
||||||
emitColumnChange()
|
emitColumnChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
const emitColumnChange = () => {
|
|
||||||
// eslint-disable-next-line vue/require-explicit-emits
|
|
||||||
emit('columnChange', columnsSetting.value)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.s-tool-column-item {
|
.s-tool-column-item {
|
||||||
|
|
|
@ -52,7 +52,6 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 统计列数据 -->
|
<!-- 统计列数据 -->
|
||||||
<a-alert showIcon class="s-table-alert mb-4" v-if="props.alert">
|
<a-alert showIcon class="s-table-alert mb-4" v-if="props.alert">
|
||||||
<template #message>
|
<template #message>
|
||||||
|
@ -65,7 +64,7 @@
|
||||||
}}
|
}}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<span className="mr-3" v-for="item in data.needTotalList">
|
<span className="mr-3" v-for="item in data.needTotalList" :key="item">
|
||||||
{{ item.title }} 总计{{ ' ' }}
|
{{ item.title }} 总计{{ ' ' }}
|
||||||
<a className="font-6">{{ !item.customRender ? item.total : item.customRender(item.total) }}</a>
|
<a className="font-6">{{ !item.customRender ? item.total : item.customRender(item.total) }}</a>
|
||||||
</span>
|
</span>
|
||||||
|
@ -75,13 +74,11 @@
|
||||||
"
|
"
|
||||||
className="ml-6"
|
className="ml-6"
|
||||||
@click="
|
@click="
|
||||||
rowClear(
|
|
||||||
typeof props.alert === 'boolean' && props.alert
|
typeof props.alert === 'boolean' && props.alert
|
||||||
? clearSelected()
|
? clearSelected()
|
||||||
: props.alert.clear && typeof props.alert.clear === 'function'
|
: props.alert.clear && typeof props.alert.clear === 'function'
|
||||||
? props.alert.clear()
|
? props.alert.clear()
|
||||||
: null
|
: null
|
||||||
)
|
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
|
@ -121,10 +118,11 @@
|
||||||
import columnSetting from './columnSetting.vue'
|
import columnSetting from './columnSetting.vue'
|
||||||
import { useSlots } from 'vue'
|
import { useSlots } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { get } from 'lodash-es'
|
import { cloneDeep, get } from 'lodash-es'
|
||||||
|
|
||||||
const slots = useSlots()
|
const slots = useSlots()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const emit = defineEmits(['onExpand'])
|
const emit = defineEmits(['onExpand', 'onSelectionChange'])
|
||||||
const renderSlots = Object.keys(slots)
|
const renderSlots = Object.keys(slots)
|
||||||
|
|
||||||
const props = defineProps(
|
const props = defineProps(
|
||||||
|
@ -207,7 +205,17 @@
|
||||||
localSettings: {
|
localSettings: {
|
||||||
rowClassName: props.rowClassName,
|
rowClassName: props.rowClassName,
|
||||||
rowClassNameSwitch: Boolean(props.rowClassName)
|
rowClassNameSwitch: Boolean(props.rowClassName)
|
||||||
}
|
},
|
||||||
|
renderTableProps: {
|
||||||
|
...props,
|
||||||
|
columns: [],
|
||||||
|
dataSource: [],
|
||||||
|
pagination: {},
|
||||||
|
loading: false,
|
||||||
|
size: props.compSize
|
||||||
|
},
|
||||||
|
selectedRows: [],
|
||||||
|
selectedRowKeys: []
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -234,15 +242,127 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
// 监听showPagination的变化
|
||||||
|
watch(
|
||||||
|
() => props.rowSelection,
|
||||||
|
(newVal) => {
|
||||||
|
if (!newVal) {
|
||||||
|
// 如果rowSelection被设置为null,清空选中状态
|
||||||
|
data.selectedRows = []
|
||||||
|
data.selectedRowKeys = []
|
||||||
|
}
|
||||||
|
// 更新表格属性
|
||||||
|
getTableProps()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
watch(
|
||||||
|
() => props.showPagination,
|
||||||
|
(newVal) => {
|
||||||
|
// 更新分页状态
|
||||||
|
data.localPagination = newVal === false ? false : Object.assign({}, data.localPagination)
|
||||||
|
// 重新加载数据和更新表格属性
|
||||||
|
loadData()
|
||||||
|
getTableProps()
|
||||||
|
}
|
||||||
|
)
|
||||||
watch(
|
watch(
|
||||||
() => props.columns,
|
() => props.columns,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
data.columnsSetting = newVal
|
data.columnsSetting = newVal.map((col) => ({
|
||||||
}
|
...col,
|
||||||
|
checked: col.checked === undefined ? true : col.checked
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
// 表格props
|
// 表格props
|
||||||
const renderTableProps = ref([])
|
const renderTableProps = computed(() => {
|
||||||
|
const tableProps = {
|
||||||
|
...props,
|
||||||
|
columns: data.localColumns || props.columns,
|
||||||
|
dataSource: data.localDataSource,
|
||||||
|
pagination: data.localPagination,
|
||||||
|
loading: data.localLoading,
|
||||||
|
size: data.customSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.rowSelection) {
|
||||||
|
tableProps.rowSelection = {
|
||||||
|
...props.rowSelection,
|
||||||
|
selectedRowKeys: data.selectedRowKeys,
|
||||||
|
selectedRows: data.selectedRows,
|
||||||
|
onChange: (selectedRowKeys, selectedRows) => {
|
||||||
|
updateSelect(selectedRowKeys, selectedRows)
|
||||||
|
props.rowSelection.onChange?.(selectedRowKeys, selectedRows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.lineSelection && props.rowSelection) {
|
||||||
|
tableProps.customRow = (record, index) => {
|
||||||
|
const customRowProps = typeof props.customRow === 'function' ? props.customRow(record, index) : {}
|
||||||
|
return {
|
||||||
|
...customRowProps,
|
||||||
|
onClick: (event) => {
|
||||||
|
// 执行原有的onClick事件
|
||||||
|
if (customRowProps && typeof customRowProps.onClick === 'function') {
|
||||||
|
customRowProps.onClick(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查行是否禁用
|
||||||
|
const rowDisabled =
|
||||||
|
typeof props.rowSelection.getCheckboxProps === 'function' &&
|
||||||
|
props.rowSelection.getCheckboxProps(record).disabled
|
||||||
|
if (rowDisabled) return
|
||||||
|
|
||||||
|
// 过滤掉按钮等可交互元素的点击
|
||||||
|
if (
|
||||||
|
event.target?.tagName.toLowerCase() === 'button' ||
|
||||||
|
event.target?.tagName.toLowerCase() === 'a' ||
|
||||||
|
event.target?.closest('button') ||
|
||||||
|
event.target?.closest('a') ||
|
||||||
|
event.target?.closest('.ant-checkbox-wrapper') ||
|
||||||
|
event.target?.closest('.ant-radio-wrapper')
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取行的key
|
||||||
|
const key = (typeof props.rowKey === 'function' && props.rowKey(record)) || record[props.rowKey] || index
|
||||||
|
|
||||||
|
// 处理选中状态
|
||||||
|
let selectedRowKeys = [...data.selectedRowKeys]
|
||||||
|
let selectedRows = [...data.selectedRows]
|
||||||
|
const rowType = props.rowSelection?.type || 'checkbox'
|
||||||
|
|
||||||
|
if (rowType === 'radio') {
|
||||||
|
// 单选模式下,直接替换选中项
|
||||||
|
selectedRowKeys = [key]
|
||||||
|
selectedRows = [record]
|
||||||
|
} else {
|
||||||
|
// 多选模式下,切换选中状态
|
||||||
|
const existingIndex = selectedRowKeys.indexOf(key)
|
||||||
|
if (existingIndex === -1) {
|
||||||
|
selectedRowKeys.push(key)
|
||||||
|
selectedRows.push(record)
|
||||||
|
} else {
|
||||||
|
selectedRowKeys.splice(existingIndex, 1)
|
||||||
|
selectedRows.splice(existingIndex, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新选中状态并触发事件
|
||||||
|
updateSelect(selectedRowKeys, selectedRows)
|
||||||
|
props.rowSelection.onChange?.(selectedRowKeys, selectedRows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tableProps
|
||||||
|
})
|
||||||
|
|
||||||
// 右上角工具数组
|
// 右上角工具数组
|
||||||
const tool = [
|
const tool = [
|
||||||
{
|
{
|
||||||
|
@ -291,13 +411,9 @@
|
||||||
// 列设置
|
// 列设置
|
||||||
const columnChange = (v) => {
|
const columnChange = (v) => {
|
||||||
data.columnsSetting = v
|
data.columnsSetting = v
|
||||||
getTableProps()
|
data.localColumns = v.filter((value) => value.checked === undefined || value.checked)
|
||||||
|
getTableProps() // 调用getTableProps以确保表格重新渲染
|
||||||
}
|
}
|
||||||
// 列清空
|
|
||||||
const rowClear = (callback) => {
|
|
||||||
clearSelected()
|
|
||||||
}
|
|
||||||
// 初始化
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
const { current } = route.params
|
const { current } = route.params
|
||||||
const localPageNum = (current && parseInt(current)) || props.pageNum
|
const localPageNum = (current && parseInt(current)) || props.pageNum
|
||||||
|
@ -305,7 +421,7 @@
|
||||||
(['auto', true].includes(props.showPagination) &&
|
(['auto', true].includes(props.showPagination) &&
|
||||||
Object.assign({}, data.localPagination, {
|
Object.assign({}, data.localPagination, {
|
||||||
current: localPageNum,
|
current: localPageNum,
|
||||||
pageSize: props.size, //props.compSize, size// 改动
|
pageSize: props.size,
|
||||||
showSizeChanger: props.showSizeChanger,
|
showSizeChanger: props.showSizeChanger,
|
||||||
defaultPageSize: props.defaultPageSize,
|
defaultPageSize: props.defaultPageSize,
|
||||||
pageSizeOptions: props.pageSizeOptions,
|
pageSizeOptions: props.pageSizeOptions,
|
||||||
|
@ -316,6 +432,16 @@
|
||||||
false
|
false
|
||||||
data.needTotalList = initTotalList(props.columns)
|
data.needTotalList = initTotalList(props.columns)
|
||||||
data.columnsSetting = props.columns
|
data.columnsSetting = props.columns
|
||||||
|
|
||||||
|
// 初始化时同步外部的选中状态
|
||||||
|
if (props.rowSelection && props.rowSelection.selectedRowKeys) {
|
||||||
|
data.selectedRowKeys = [...props.rowSelection.selectedRowKeys]
|
||||||
|
// 如果有selectedRows,也同步
|
||||||
|
if (props.rowSelection.selectedRows) {
|
||||||
|
data.selectedRows = cloneDeep(props.rowSelection.selectedRows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,83 +579,44 @@
|
||||||
: null
|
: null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置行属性, 点击行时高亮
|
|
||||||
if (k === 'customRow') {
|
|
||||||
if (props.lineSelection && props.rowSelection) {
|
|
||||||
// 如果需要 整行选择,则重新绑定 customRow 事件
|
|
||||||
renderProps[k] = (record, index) => {
|
|
||||||
return {
|
|
||||||
...(typeof props.customRow !== 'undefined' && props.customRow(record, index)),
|
|
||||||
onClick: (event) => {
|
|
||||||
// 若存在原onClick则执行
|
|
||||||
typeof data[k] !== 'undefined' &&
|
|
||||||
typeof data[k](record, index).onClick !== 'undefined' &&
|
|
||||||
data[k](record, index).onClick(event)
|
|
||||||
// 记录为disabled则直接返回,默认为不可选
|
|
||||||
const rowDisabled =
|
|
||||||
typeof props.rowSelection.getCheckboxProps !== 'undefined' &&
|
|
||||||
props.rowSelection.getCheckboxProps(record).disabled
|
|
||||||
if (rowDisabled) return
|
|
||||||
// 过滤自定义按钮的非空白区域
|
|
||||||
const classList = event.target?.classList
|
|
||||||
if (!classList.contains('ant-table-cell')) return
|
|
||||||
const key = (typeof props.rowKey === 'function' && props.rowKey(record)) || props.rowKey || index
|
|
||||||
let selectedRows = props.rowSelection.selectedRows
|
|
||||||
let selectedRowKeys = props.rowSelection.selectedRowKeys
|
|
||||||
const rowType = props.rowSelection?.type || 'checkbox'
|
|
||||||
|
|
||||||
if (rowType === 'radio' || props.rowSelection.selectedRowKeys === undefined) {
|
|
||||||
selectedRowKeys = [key]
|
|
||||||
selectedRows = [record]
|
|
||||||
} else if (!props.rowSelection.selectedRowKeys?.includes(key)) {
|
|
||||||
selectedRowKeys.push(key)
|
|
||||||
selectedRows.push(record)
|
|
||||||
} else {
|
|
||||||
const index = props.rowSelection.selectedRowKeys?.findIndex((itemKey) => itemKey === key)
|
|
||||||
selectedRows.splice(index, 1)
|
|
||||||
selectedRowKeys.splice(index, 1)
|
|
||||||
}
|
|
||||||
updateSelect(selectedRowKeys, selectedRows)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return renderProps[k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
renderProps[k] = props[k]
|
renderProps[k] = props[k]
|
||||||
})
|
})
|
||||||
renderProps = {
|
renderProps = {
|
||||||
...renderProps,
|
...renderProps,
|
||||||
size: data.customSize, // 注意这个size是a-table组件需要的,这里不能跟别的地方成为compSize
|
size: data.customSize,
|
||||||
columns: data.columnsSetting.filter((value) => value.checked === undefined || value.checked),
|
columns: data.columnsSetting.filter((value) => value.checked === undefined || value.checked),
|
||||||
...data.localSettings
|
...data.localSettings
|
||||||
}
|
}
|
||||||
// 将值为 undefined 或者 null 的 table里props属性进行一个过滤
|
// 将值为 undefined 或者 null 的 table里props属性进行一个过滤
|
||||||
renderTableProps.value = Object.entries(renderProps).reduce((x, [y, z]) => (z == null ? x : ((x[y] = z), x)), {})
|
data.renderTableProps = Object.entries(renderProps).reduce((x, [y, z]) => (z == null ? x : ((x[y] = z), x)), {})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用于更新已选中的列表数据 total 统计
|
// 用于更新已选中的列表数据 total 统计
|
||||||
const updateSelect = (selectedRowKeys, selectedRows) => {
|
const updateSelect = (selectedRowKeys, selectedRows) => {
|
||||||
if (props.rowSelection) {
|
if (props.rowSelection) {
|
||||||
|
// 更新本地响应式数据
|
||||||
|
data.selectedRows = cloneDeep(selectedRows)
|
||||||
|
data.selectedRowKeys = cloneDeep(selectedRowKeys)
|
||||||
|
// 同步更新rowSelection的选中状态
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
props.rowSelection.selectedRows = selectedRows
|
props.rowSelection.selectedRows = cloneDeep(selectedRows)
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
props.rowSelection.selectedRowKeys = selectedRowKeys
|
props.rowSelection.selectedRowKeys = cloneDeep(selectedRowKeys)
|
||||||
props.rowSelection.onChange(selectedRowKeys, selectedRows)
|
// 通知父组件更新
|
||||||
|
emit('onSelectionChange', selectedRowKeys, selectedRows)
|
||||||
|
// 更新表格属性
|
||||||
getTableProps()
|
getTableProps()
|
||||||
}
|
// 更新统计数据
|
||||||
const list = data.needTotalList
|
data.needTotalList = initTotalList(props.columns)
|
||||||
data.needTotalList = list.map((item) => {
|
data.needTotalList.forEach((item) => {
|
||||||
return {
|
item.total = selectedRows.reduce((sum, val) => {
|
||||||
...item,
|
const value = get(val, item.dataIndex)
|
||||||
total: selectedRows.reduce((sum, val) => {
|
return addNumbers(sum, value)
|
||||||
return addNumbers(sum, get(val, item.dataIndex))
|
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 如果开启了needTotal计算总和,支持小数点
|
}
|
||||||
|
// 数值相加辅助函数
|
||||||
const addNumbers = (num1, num2) => {
|
const addNumbers = (num1, num2) => {
|
||||||
// 将参数转换为数字
|
// 将参数转换为数字
|
||||||
let num1Value = Number(num1)
|
let num1Value = Number(num1)
|
||||||
|
@ -554,10 +641,19 @@
|
||||||
// 清空 table 已选中项
|
// 清空 table 已选中项
|
||||||
const clearSelected = () => {
|
const clearSelected = () => {
|
||||||
if (props.rowSelection) {
|
if (props.rowSelection) {
|
||||||
|
// 清空选中状态
|
||||||
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
|
props.rowSelection.selectedRowKeys = []
|
||||||
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
|
props.rowSelection.selectedRows = []
|
||||||
|
// 触发onChange事件
|
||||||
props.rowSelection.onChange([], [])
|
props.rowSelection.onChange([], [])
|
||||||
updateSelect([], [])
|
// 更新表格属性
|
||||||
getTableProps()
|
getTableProps()
|
||||||
|
// 取消选中的
|
||||||
|
updateSelect([], [])
|
||||||
}
|
}
|
||||||
|
data.needTotalList = initTotalList(props.columns)
|
||||||
}
|
}
|
||||||
// 刷新并清空已选
|
// 刷新并清空已选
|
||||||
const clearRefreshSelected = (bool = false) => {
|
const clearRefreshSelected = (bool = false) => {
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
## 小诺图标选择器
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
|
||||||
|
改组件为小诺人员选择器,可返回id用逗号隔离的字符串或id数组类型的数据格式
|
||||||
|
|
||||||
|
@author yubaoshan
|
||||||
|
|
||||||
|
@data 2024年4月13日23:59:23
|
||||||
|
|
||||||
|
### props定义
|
||||||
|
|
||||||
|
| 序号 | 编码 | 类型 | 说明 | 默认 |
|
||||||
|
|-----|-------------------|---------------|---------------------|-------|
|
||||||
|
| 1 | value | String | 值,跟v-model绑定 | '' |
|
||||||
|
| 2 | formRef | - | 表单dom,为了取消绑定的验证 | - |
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||||
|
<a-form-item label="图标选择:" name="icon">
|
||||||
|
<xn-icon-selector
|
||||||
|
v-model:value="formData.icon"
|
||||||
|
v-model:formRef="formRef"
|
||||||
|
placeholder="请选择图标"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { required } from '@/utils/formRules'
|
||||||
|
// 定义表单的dom
|
||||||
|
const formRef = ref()
|
||||||
|
// 表单数据
|
||||||
|
const formData = ref({
|
||||||
|
icon: ''
|
||||||
|
})
|
||||||
|
// 默认要校验的
|
||||||
|
const formRules = {
|
||||||
|
icon: [required('请选择图标')]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
|
@ -5,20 +5,21 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
placement="bottomLeft"
|
placement="bottomLeft"
|
||||||
:overlayStyle="{ width: '500px' }"
|
:overlayStyle="{ width: '500px' }"
|
||||||
@visibleChange="handleVisibleChange"
|
@openChange="handleOpenChange"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="icon-selector-content">
|
<div class="icon-selector-content">
|
||||||
<a-tabs v-model:activeKey="activeKey" tab-position="left" size="small" @change="handleTabChange">
|
<a-tabs v-model:activeKey="activeKey" tab-position="left" size="small" @change="handleTabChange">
|
||||||
<a-tab-pane v-for="group in iconData" :key="group.key" :tab="group.name">
|
<a-tab-pane v-for="group in iconData" :key="group.key" :tab="group.name">
|
||||||
<div v-if="group.iconItem.length > 1" class="icon-category">
|
<div v-if="group.iconItem.length > 1" class="icon-category">
|
||||||
|
<a-form-item-rest>
|
||||||
<a-radio-group v-model:value="currentCategory" @change="handleCategoryChange" size="small">
|
<a-radio-group v-model:value="currentCategory" @change="handleCategoryChange" size="small">
|
||||||
<a-radio-button v-for="item in group.iconItem" :key="item.key" :value="item.key">
|
<a-radio-button v-for="item in group.iconItem" :key="item.key" :value="item.key">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</a-radio-button>
|
</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
|
</a-form-item-rest>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="icon-grid">
|
<div class="icon-grid">
|
||||||
<template v-for="iconGroup in group.iconItem" :key="iconGroup.key">
|
<template v-for="iconGroup in group.iconItem" :key="iconGroup.key">
|
||||||
<template v-if="iconGroup.key === currentCategory">
|
<template v-if="iconGroup.key === currentCategory">
|
||||||
|
@ -59,7 +60,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue'
|
import { ref } from 'vue'
|
||||||
import config from '@/config/iconSelect'
|
import config from '@/config/iconSelect'
|
||||||
import { SearchOutlined, CloseCircleOutlined } from '@ant-design/icons-vue'
|
import { SearchOutlined, CloseCircleOutlined } from '@ant-design/icons-vue'
|
||||||
|
|
||||||
|
@ -85,10 +86,10 @@
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const formRef = defineModel('formRef')
|
||||||
const emit = defineEmits(['update:value', 'change'])
|
const emit = defineEmits(['update:value', 'change'])
|
||||||
|
|
||||||
const selectedIcon = ref(props.value)
|
const selectedIcon = ref()
|
||||||
const iconData = ref(config.icons)
|
const iconData = ref(config.icons)
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const activeKey = ref(iconData.value[0]?.key || '')
|
const activeKey = ref(iconData.value[0]?.key || '')
|
||||||
|
@ -101,9 +102,9 @@
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleVisibleChange = (isVisible) => {
|
const handleOpenChange = (isOpen) => {
|
||||||
if (!props.disabled) {
|
if (!props.disabled) {
|
||||||
visible.value = isVisible
|
visible.value = isOpen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,16 +113,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleIconSelect = (icon) => {
|
const handleIconSelect = (icon) => {
|
||||||
selectedIcon.value = icon
|
|
||||||
emit('update:value', icon)
|
emit('update:value', icon)
|
||||||
emit('change', icon)
|
formRef.value?.validateFields('icon')
|
||||||
visible.value = false
|
visible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
selectedIcon.value = ''
|
|
||||||
emit('update:value', '')
|
emit('update:value', '')
|
||||||
emit('change', '')
|
selectedIcon.value = ''
|
||||||
|
formRef.value?.validateFields('icon')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTabChange = (key) => {
|
const handleTabChange = (key) => {
|
||||||
|
@ -144,6 +144,7 @@
|
||||||
color: rgba(0, 0, 0, 0.25);
|
color: rgba(0, 0, 0, 0.25);
|
||||||
transition: color 0.3s;
|
transition: color 0.3s;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: rgba(0, 0, 0, 0.45);
|
color: rgba(0, 0, 0, 0.45);
|
||||||
}
|
}
|
||||||
|
@ -210,6 +211,7 @@
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
@ -240,12 +242,15 @@
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-tabs-tab) {
|
:deep(.ant-tabs-tab) {
|
||||||
padding: 8px !important;
|
padding: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-tabs-nav) {
|
:deep(.ant-tabs-nav) {
|
||||||
width: 60px !important;
|
width: 60px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-tabs-tabpane) {
|
:deep(.ant-tabs-tabpane) {
|
||||||
padding-left: 0 !important;
|
padding-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue