Browse Source
* 完善树表格拖拽,支持拖拽过程中停留在目标节点1.5秒将其展开;支持将数据拖入至无孩子节点的目标节点中。 * 新增树表格懒加载模式。 * 新增数据分组功能,支持根据分组字段提取独立行或合并分组。 * 新增高级查询功能,支持用户自定义组装查询条件。 * 新增自定义追加行功能。 * 内置按钮新增`fullScreen`,该按钮为全屏与退出动态变化。 * 用户配置中增加分组、分割线配置功能。 * 优化Form表单验证错误,先看是否有errorMessageI18nKey,有的情况下优先使用。 * 修复全屏时紧凑失效与底部边框不显示问题。 * 修复内置编辑窗口中的按钮提交报错后关闭窗口重新打开仍然处于loading状态的问题。 * beforeRemove与beforeRequestData事件增加同步操作,事件方法处理完成后再执行内部处理。main
123 changed files with 8744 additions and 5610 deletions
@ -0,0 +1,32 @@ |
|||||
|
<template> |
||||
|
<!-- 单独分组行 --> |
||||
|
<GroupTr v-if="useGroupTrComputed" :scope="props.scope"></GroupTr> |
||||
|
|
||||
|
<!-- 普通行 --> |
||||
|
<Tr v-else :scope="props.scope"></Tr> |
||||
|
|
||||
|
<!-- 追加行 --> |
||||
|
<GridAppendRow :scope="props.scope"></GridAppendRow> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject, computed } from 'vue'; |
||||
|
import { Constant, GridTools } from './ts/index'; |
||||
|
import Tr from './Tr.vue'; |
||||
|
import GroupTr from './extra/group/GroupTr.vue'; |
||||
|
import GridAppendRow from './extra/append/AppendRow.vue'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const props = defineProps({ |
||||
|
scope: { |
||||
|
// 顶部插槽属性 |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const useGroupTrComputed = computed(() => { |
||||
|
return tools.props.groupMode === Constant.GROUP_MODE.ALONE && !tools.props.tree && tools.table.configStore.aloneGroupByField; |
||||
|
}); |
||||
|
</script> |
@ -1,134 +0,0 @@ |
|||||
<template> |
|
||||
<w-dialog ref="dialogRef" :title="dialog.title" v-bind="props.grid.props.editor?.cellEditor" :buttons="dialog.buttons"> |
|
||||
<w-form ref="dialogFormRef" :cols-num="1" :fields="fieldsComputed" class="pt-1.5 px-1.5"></w-form> |
|
||||
</w-dialog> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, reactive, inject, computed, toRaw } from 'vue'; |
|
||||
import { t, Tools, noErrorAxios, NotifyManager, ServerExceptionHandler } from '@/platform'; |
|
||||
|
|
||||
const dialogRef = ref(); |
|
||||
const dialogFormRef = ref(); |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
// 表格实例 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
url: { |
|
||||
// 表格所有的url |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
getRow: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
componentInfo: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
rowKeyName: { |
|
||||
type: String, |
|
||||
default: '', |
|
||||
}, |
|
||||
}); |
|
||||
const table = inject('table'); |
|
||||
|
|
||||
const dialog = { |
|
||||
title: t('action.edit'), |
|
||||
buttons: [ |
|
||||
{ |
|
||||
icon: 'beenhere', |
|
||||
labelI18nKey: props.grid.props.localMode ? 'confirm' : 'action.submit', |
|
||||
label: t(props.grid.props.localMode ? 'confirm' : 'action.submit'), |
|
||||
loading: false, |
|
||||
click: async () => { |
|
||||
dialog.buttons[0].loading = true; |
|
||||
const result = await dialogFormRef.value.validate(); |
|
||||
if (result) { |
|
||||
const cellSelected = table['cellSelected']; |
|
||||
const data = dialogFormRef.value.getData(); |
|
||||
const row = props.getRow(table.rows, cellSelected['row'][props.rowKeyName], false); |
|
||||
if (row) { |
|
||||
row[cellSelected['colName']] = data[cellSelected['colName']]; |
|
||||
// 判定是否需要往后端发送修改操作 |
|
||||
if (!props.grid.props.localMode) { |
|
||||
const requestParams = { |
|
||||
method: 'PUT', |
|
||||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|
||||
data: toRaw(row), |
|
||||
url: getUrl(row[props.grid.props.primaryKey]), |
|
||||
}; |
|
||||
noErrorAxios(requestParams) |
|
||||
.then((resp) => { |
|
||||
dialog.buttons[0].loading = false; |
|
||||
props.grid.emit('afterEditorDataSubmit', { grid: props.grid, data: resp.data }); |
|
||||
NotifyManager.info(t('tip.operationSuccess')); |
|
||||
dialogRef.value.hide(); |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
if (error?.code === 1001) { |
|
||||
// 验证错误 |
|
||||
dialogFormRef.value.setValidationErrors(error.data); |
|
||||
} else { |
|
||||
ServerExceptionHandler.handle(error); |
|
||||
} |
|
||||
dialog.buttons[0].loading = false; |
|
||||
}); |
|
||||
} else { |
|
||||
dialogRef.value.hide(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
dialog.buttons[0].loading = false; |
|
||||
}, |
|
||||
}, |
|
||||
], |
|
||||
}; |
|
||||
|
|
||||
const fieldsComputed = computed(() => { |
|
||||
const fields = <any>[]; |
|
||||
const cellSelected = table['cellSelected']; |
|
||||
if (Object.keys(cellSelected).length > 0) { |
|
||||
const column = table['columns'].find((item) => item['name'] === cellSelected['colName']); |
|
||||
if (column) { |
|
||||
fields.push({ |
|
||||
name: column['name'], |
|
||||
type: column['type'], |
|
||||
label: column['label'], |
|
||||
defaultValue: cellSelected['value'], |
|
||||
...column['attrs'], |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
return fields; |
|
||||
}); |
|
||||
|
|
||||
const getUrl = (primaryKey) => { |
|
||||
if (!Tools.isEmpty(props.url.editDataUrl)) { |
|
||||
return props.url.editDataUrl + '/' + primaryKey; |
|
||||
} else { |
|
||||
return props.url.dataUrl + '/' + primaryKey; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const getDialog = () => { |
|
||||
return dialogRef.value; |
|
||||
}; |
|
||||
const getForm = () => { |
|
||||
return dialogFormRef.value; |
|
||||
}; |
|
||||
|
|
||||
defineExpose({ |
|
||||
getDialog, |
|
||||
getForm, |
|
||||
}); |
|
||||
</script> |
|
@ -1,27 +0,0 @@ |
|||||
<template> |
|
||||
<q-tr v-for="(appendRow, rowIndex) in props.grid.props.appendRows" :key="rowIndex" :no-hover="true" :class="''"> |
|
||||
<q-td v-for="(col, colIndex) in appendRow" :key="colIndex" :rowspan="col['rowSpan'] ? col['rowSpan'] : 1" :colspan="col['colSpan'] ? col['colSpan'] : 1"> |
|
||||
<template v-if="typeof col['value'] === 'function'"> |
|
||||
<GridAppendContent :value="col.value({ grid: props.grid, rows: props.grid.getRows() })"></GridAppendContent> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<span v-dompurify-html="!Tools.isEmpty(col['value']) ? col['value'] : ''"></span> |
|
||||
</template> |
|
||||
</q-td> |
|
||||
</q-tr> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
import { Tools } from '@/platform'; |
|
||||
import GridAppendContent from './GridAppendContent.vue'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
// 表格实例 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
</script> |
|
@ -1,376 +0,0 @@ |
|||||
<template> |
|
||||
<template v-if="props.grid.props.tree"> |
|
||||
<TreeGridRow |
|
||||
:ref="(el) => setTreeRowComponentRef(el, scope.row)" |
|
||||
:grid="props.grid" |
|
||||
:columns-map="props.tableColumnsMap" |
|
||||
:row="props.scope.row" |
|
||||
:cols="props.scope.cols" |
|
||||
:row-key="props.rowKeyName" |
|
||||
:grid-row-click="props.rowClick" |
|
||||
:grid-row-db-click="props.rowDbClick" |
|
||||
:after-drag-and-drop="props.afterDragAndDrop" |
|
||||
:get-row="props.getRow" |
|
||||
:url="props.url" |
|
||||
:get-row-component-refs="getRowComponentRefs" |
|
||||
:set-old-value="setOldValue" |
|
||||
:no-data-tr-colspan="noDataTrColspan" |
|
||||
:updates="updates" |
|
||||
:row-index="scope.rowIndex" |
|
||||
></TreeGridRow> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<q-tr |
|
||||
ref="trRef" |
|
||||
:no-hover="props.grid.props.selectMode === selectMode.row ? false : true" |
|
||||
:class="props.scope.row[table.selectedField] && props.grid.props.selectMode === selectMode.row ? 'selected' : ''" |
|
||||
:props="props.scope" |
|
||||
:draggable="draggableComputed" |
|
||||
@click.stop="props.rowClick($event, scope.row, scope.rowIndex)" |
|
||||
@dblclick.stop="props.rowDbClick($event, scope.row, scope.rowIndex)" |
|
||||
@dragleave="draggableComputed ? onDragLeave($event) : () => {}" |
|
||||
@dragover="draggableComputed ? onDragOver($event, scope) : () => {}" |
|
||||
@drop="draggableComputed ? onDrop($event, scope) : () => {}" |
|
||||
@dragstart="draggableComputed ? onDragStart($event, scope) : () => {}" |
|
||||
> |
|
||||
<q-td v-if="table.checkboxSelection && props.grid.props.selectMode !== selectMode.none" class="text-center" style="padding: 0; width: 50px"> |
|
||||
<q-checkbox |
|
||||
v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)[table.tickedField]" |
|
||||
flat |
|
||||
:dense="props.denseBody" |
|
||||
@update:model-value="updateTicked($event, scope.row)" |
|
||||
/> |
|
||||
</q-td> |
|
||||
<template v-for="(col, index) in scope.cols" :key="index"> |
|
||||
<GridTd |
|
||||
:ref="(el) => setComponentRef(el, scope.row, col)" |
|
||||
:grid="props.grid" |
|
||||
:get-row="props.getRow" |
|
||||
:row-key-name="props.rowKeyName" |
|
||||
:scope="scope" |
|
||||
:col="col" |
|
||||
:value="col.value" |
|
||||
:is-selected-row="isSelectedRowComputed" |
|
||||
></GridTd> |
|
||||
</template> |
|
||||
</q-tr> |
|
||||
<GridEditToolbar |
|
||||
:grid="props.grid" |
|
||||
:url="props.url" |
|
||||
:row="props.scope.row" |
|
||||
:row-key-name="props.rowKeyName" |
|
||||
:get-row="props.getRow" |
|
||||
:get-row-component-refs="getRowComponentRefs" |
|
||||
:set-old-value="setOldValue" |
|
||||
:no-data-tr-colspan="noDataTrColspan" |
|
||||
></GridEditToolbar> |
|
||||
<GridAppendRow v-if="showAppendRowsComputed" :grid="props.grid"></GridAppendRow> |
|
||||
</template> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { computed, inject, ref, toRaw } from 'vue'; |
|
||||
import { Tools, noErrorAxios, NotifyManager, t, ServerExceptionHandler } from '@/platform'; |
|
||||
import { dndMode, dndImage, selectMode, editStatus } from './ts/grid'; |
|
||||
import TreeGridRow from './TreeGridRow.vue'; |
|
||||
import GridTd from './GridTd.vue'; |
|
||||
import GridEditToolbar from './GridEditToolbar.vue'; |
|
||||
import GridAppendRow from './GridAppendRow.vue'; |
|
||||
|
|
||||
const trRef = ref(); |
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
// 表格实例 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
url: { |
|
||||
// 表格所有的url |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
scope: { |
|
||||
// 顶部插槽属性 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
tableColumnsMap: { |
|
||||
type: Map, |
|
||||
default: () => { |
|
||||
return new Map(); |
|
||||
}, |
|
||||
}, |
|
||||
rowKeyName: { |
|
||||
type: String, |
|
||||
default: '', |
|
||||
}, |
|
||||
rowClick: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
rowDbClick: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
afterDragAndDrop: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
denseBody: { |
|
||||
type: Boolean, |
|
||||
default: () => { |
|
||||
return false; |
|
||||
}, |
|
||||
}, |
|
||||
getRow: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
setOldValue: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
noDataTrColspan: { |
|
||||
type: Number, |
|
||||
default: () => { |
|
||||
return 0; |
|
||||
}, |
|
||||
}, |
|
||||
allTickedStatus: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
}); |
|
||||
const table = inject('table'); |
|
||||
|
|
||||
const componentRef = ref({}); |
|
||||
const getRowComponentRefs = (rowKey: string | Array<string>) => { |
|
||||
const refs = <any>[]; |
|
||||
if (!Tools.isEmpty(componentRef.value)) { |
|
||||
const filterResult = Object.keys(componentRef.value).filter((item) => { |
|
||||
if (typeof rowKey === 'string') { |
|
||||
return item.startsWith(rowKey + '_'); |
|
||||
} else { |
|
||||
return true; |
|
||||
} |
|
||||
}); |
|
||||
if (filterResult.length > 0) { |
|
||||
filterResult.forEach((key) => { |
|
||||
refs.push(componentRef.value[key]); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
return refs; |
|
||||
}; |
|
||||
|
|
||||
const setComponentRef = (el, row, col) => { |
|
||||
if (el && !Tools.isEmpty(col.type)) { |
|
||||
componentRef.value[row[props.rowKeyName] + '_' + col.name] = el; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const setTreeRowComponentRef = (el, row) => { |
|
||||
if (el && !Tools.isEmpty(row)) { |
|
||||
componentRef.value[row[props.rowKeyName] + '_'] = el; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const checkLastRow = (row) => { |
|
||||
if (props.grid.props.tree && row['expand'] && !Tools.isEmpty(row.children) && row.children.length > 0) { |
|
||||
const childrenLastRow = row.children[row.children.length - 1]; |
|
||||
if (childrenLastRow['expand'] && !Tools.isEmpty(childrenLastRow.children) && childrenLastRow.children.length > 0) { |
|
||||
return checkLastRow(childrenLastRow); |
|
||||
} else { |
|
||||
return childrenLastRow[props.rowKeyName] === props.scope.row[props.rowKeyName]; |
|
||||
} |
|
||||
} else { |
|
||||
return row[props.rowKeyName] === props.scope.row[props.rowKeyName]; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const showAppendRowsComputed = computed(() => { |
|
||||
if (Array.isArray(props.grid.props.appendRows) && props.grid.props.appendRows.length > 0) { |
|
||||
const lastRow = table.rows[table.rows.length - 1]; |
|
||||
return checkLastRow(lastRow); |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
|
|
||||
const draggableComputed = computed(() => { |
|
||||
if ( |
|
||||
props.grid.props.dndMode && |
|
||||
typeof props.grid.props.dndMode === 'string' && |
|
||||
!Tools.isEmpty(dndMode[props.grid.props.dndMode]) && |
|
||||
table.bodyEditStatus === editStatus.none |
|
||||
) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
|
|
||||
const isSelectedRowComputed = computed(() => { |
|
||||
const selected = props.grid.getSelectedRow(); |
|
||||
if (!Tools.isEmpty(selected)) { |
|
||||
return props.scope.row[props.rowKeyName] === props.grid.getSelectedRow()[props.rowKeyName]; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
|
|
||||
// 得到表格数据行的中间高度 |
|
||||
const gridTrMiddleHeightComputed = computed(() => { |
|
||||
if (trRef?.value) { |
|
||||
return trRef.value.$el.offsetHeight / 2; |
|
||||
} else { |
|
||||
return (table.dense || table.denseBody ? 24 : 48) / 2; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 批量更新数据 |
|
||||
const updates = (data, callback) => { |
|
||||
const requestParams = { |
|
||||
method: 'PUT', |
|
||||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|
||||
data: data, |
|
||||
url: props.url.dataUrl + '/updates', |
|
||||
}; |
|
||||
noErrorAxios(requestParams) |
|
||||
.then((resp) => { |
|
||||
if (!Tools.isEmpty(callback)) { |
|
||||
callback(resp?.data); |
|
||||
} |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
if (error.code === 1001) { |
|
||||
NotifyManager.error('服务器验证未通过'); |
|
||||
} else { |
|
||||
ServerExceptionHandler.handle(error); |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
// 拖拽开始 |
|
||||
const onDragStart = (e, scope) => { |
|
||||
const img = new Image(); |
|
||||
img.src = dndImage; |
|
||||
e.dataTransfer.setDragImage(img, 0, 0); |
|
||||
const currPageIndex = table.rows.findIndex((item) => { |
|
||||
return item[props.rowKeyName] === scope.row[props.rowKeyName]; |
|
||||
}); |
|
||||
const currPageStartIndex = scope.rowIndex - currPageIndex; |
|
||||
if (props.grid.props.pageable) { |
|
||||
table.dragRow = { row: { ...scope.row }, rowIndex: scope.rowIndex, currPageIndex: currPageIndex, currPageStartIndex }; |
|
||||
} else { |
|
||||
table.dragRow = { row: { ...scope.row }, rowIndex: scope.rowIndex, currPageIndex: scope.rowIndex, currPageStartIndex }; |
|
||||
} |
|
||||
e.dataTransfer.dropEffect = 'move'; |
|
||||
}; |
|
||||
const addDragTopStyle = (e) => { |
|
||||
if (e.target?.parentNode?.children) { |
|
||||
for (let i = 0; i < e.target.parentNode.children.length; i++) { |
|
||||
e.target.parentNode.children[i].style.borderTopWidth = '2px'; |
|
||||
e.target.parentNode.children[i].style.borderTopStyle = 'dashed'; |
|
||||
e.target.parentNode.children[i].style.borderTopColor = 'orange'; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const removeDragTopStyle = (e) => { |
|
||||
if (e.target?.parentNode?.children) { |
|
||||
for (let i = 0; i < e.target.parentNode.children.length; i++) { |
|
||||
e.target.parentNode.children[i].style.borderTopWidth = ''; |
|
||||
e.target.parentNode.children[i].style.borderTopStyle = ''; |
|
||||
e.target.parentNode.children[i].style.borderTopColor = ''; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
const addDragBottomStyle = (e) => { |
|
||||
if (e.target?.parentNode?.children) { |
|
||||
for (let i = 0; i < e.target.parentNode.children.length; i++) { |
|
||||
e.target.parentNode.children[i].style.borderBottomWidth = '2px'; |
|
||||
e.target.parentNode.children[i].style.borderBottomStyle = 'dashed'; |
|
||||
e.target.parentNode.children[i].style.borderBottomColor = 'orange'; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
const removeDragBottomStyle = (e) => { |
|
||||
if (e.target?.parentNode?.children) { |
|
||||
for (let i = 0; i < e.target.parentNode.children.length; i++) { |
|
||||
e.target.parentNode.children[i].style.borderBottomWidth = ''; |
|
||||
e.target.parentNode.children[i].style.borderBottomStyle = ''; |
|
||||
e.target.parentNode.children[i].style.borderBottomColor = ''; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
// 拖拽至不可放置区域触发 |
|
||||
const onDragLeave = (e) => { |
|
||||
removeDragTopStyle(e); |
|
||||
removeDragBottomStyle(e); |
|
||||
}; |
|
||||
// 拖拽过程触发 |
|
||||
const onDragOver = (e, scope) => { |
|
||||
e.preventDefault(); |
|
||||
if (e.target.nodeName === 'TD' && e.offsetY <= gridTrMiddleHeightComputed.value) { |
|
||||
removeDragBottomStyle(e); |
|
||||
addDragTopStyle(e); |
|
||||
} else if (e.target.nodeName === 'TD' && e.offsetY > gridTrMiddleHeightComputed.value) { |
|
||||
removeDragTopStyle(e); |
|
||||
addDragBottomStyle(e); |
|
||||
} |
|
||||
}; |
|
||||
// 拖拽放置时触发 |
|
||||
const onDrop = (e, scope) => { |
|
||||
e.preventDefault(); |
|
||||
removeDragTopStyle(e); |
|
||||
removeDragBottomStyle(e); |
|
||||
if (table.dragRow.rowIndex === scope.rowIndex) { |
|
||||
return; |
|
||||
} |
|
||||
const dragRow = table.dragRow.row; |
|
||||
const currPageStartIndex = table.dragRow.currPageStartIndex; |
|
||||
table.rows.splice(table.dragRow.currPageIndex, 1); |
|
||||
if (e.offsetY <= gridTrMiddleHeightComputed.value && table.dragRow.rowIndex < scope.rowIndex) { |
|
||||
table.rows.splice(scope.rowIndex - currPageStartIndex - 1, 0, dragRow); |
|
||||
} else if (e.offsetY > gridTrMiddleHeightComputed.value && table.dragRow.rowIndex > scope.rowIndex) { |
|
||||
table.rows.splice(scope.rowIndex - currPageStartIndex + 1, 0, dragRow); |
|
||||
} else { |
|
||||
table.rows.splice(scope.rowIndex - currPageStartIndex, 0, dragRow); |
|
||||
} |
|
||||
|
|
||||
const updateData = <any>[]; |
|
||||
table.rows.forEach((item, index) => { |
|
||||
if (!Tools.isEmpty(item)) { |
|
||||
item[props.grid.props.dndOrderBy] = currPageStartIndex + index + 1; |
|
||||
updateData.push(toRaw(item)); |
|
||||
} |
|
||||
}); |
|
||||
if (props.grid.props.dndMode === dndMode.server && updateData.length > 0) { |
|
||||
// 访问后端更新排序 |
|
||||
updates(updateData, () => {}); |
|
||||
} |
|
||||
|
|
||||
props.afterDragAndDrop(updateData); |
|
||||
}; |
|
||||
|
|
||||
const updateTicked = (evt: Event, row: any) => { |
|
||||
if (table.bodyEditStatus === 'none') { |
|
||||
props.getRow(table.rows, row[props.rowKeyName], false)[table.selectedField] = row[table.tickedField]; |
|
||||
props.allTickedStatus(); |
|
||||
if (!row[table.tickedField]) { |
|
||||
// 取消选中时将选中的单元格也清空 |
|
||||
table.cellSelected = {}; |
|
||||
} |
|
||||
if (props.grid.props.onUpdateTicked) { |
|
||||
props.grid.emit('updateTicked', { grid: props.grid, evt, row }); |
|
||||
} |
|
||||
} else { |
|
||||
props.getRow(table.rows, row[props.rowKeyName], false)[table.tickedField] = !props.getRow(table.rows, row[props.rowKeyName], false)[table.tickedField]; |
|
||||
} |
|
||||
}; |
|
||||
</script> |
|
||||
|
|
||||
<style lang="css"></style> |
|
@ -1,363 +0,0 @@ |
|||||
<template> |
|
||||
<q-list padding style="min-width: 250px"> |
|
||||
<q-item :clickable="false"> |
|
||||
<q-item-section> |
|
||||
<q-item-label>全屏</q-item-label> |
|
||||
</q-item-section> |
|
||||
<q-item-section side> |
|
||||
<q-btn-group outline flat dense unelevated spread> |
|
||||
<q-btn |
|
||||
:color="!scope.inFullscreen ? 'primary' : ''" |
|
||||
:outline="scope.inFullscreen ? true : false" |
|
||||
dense |
|
||||
label="正常" |
|
||||
unelevated |
|
||||
@click=" |
|
||||
() => { |
|
||||
if (scope.inFullscreen) { |
|
||||
scope.toggleFullscreen(); |
|
||||
table.gridConfig = false; |
|
||||
} |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
<q-btn |
|
||||
:color="scope.inFullscreen ? 'primary' : ''" |
|
||||
:outline="!scope.inFullscreen ? true : false" |
|
||||
dense |
|
||||
label="全屏" |
|
||||
unelevated |
|
||||
@click=" |
|
||||
() => { |
|
||||
if (!scope.inFullscreen) { |
|
||||
scope.toggleFullscreen(); |
|
||||
table.gridConfig = false; |
|
||||
} |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
</q-btn-group> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<q-separator /> |
|
||||
<q-item :clickable="false"> |
|
||||
<q-item-section> |
|
||||
<q-item-label>复选框</q-item-label> |
|
||||
</q-item-section> |
|
||||
<q-item-section side> |
|
||||
<q-btn-group outline flat dense unelevated spread> |
|
||||
<q-btn |
|
||||
:color="table.checkboxSelection ? 'primary' : ''" |
|
||||
:outline="table.checkboxSelection ? false : true" |
|
||||
dense |
|
||||
label="显示" |
|
||||
unelevated |
|
||||
@click=" |
|
||||
() => { |
|
||||
table.checkboxSelection = true; |
|
||||
grid.refreshStyle(100); |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
<q-btn |
|
||||
:color="table.checkboxSelection ? '' : 'primary'" |
|
||||
:outline="table.checkboxSelection ? true : false" |
|
||||
dense |
|
||||
label="隐藏" |
|
||||
unelevated |
|
||||
@click=" |
|
||||
() => { |
|
||||
table.checkboxSelection = false; |
|
||||
grid.refreshStyle(100); |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
</q-btn-group> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<template v-if="!grid.props.tree"> |
|
||||
<q-separator /> |
|
||||
<q-item :clickable="false"> |
|
||||
<q-item-section> |
|
||||
<q-item-label>序号</q-item-label> |
|
||||
</q-item-section> |
|
||||
<q-item-section side> |
|
||||
<q-btn-group outline flat dense unelevated spread> |
|
||||
<q-btn |
|
||||
:color="table.sortNo ? 'primary' : ''" |
|
||||
:outline="table.sortNo ? false : true" |
|
||||
dense |
|
||||
label="显示" |
|
||||
unelevated |
|
||||
@click=" |
|
||||
() => { |
|
||||
table.columns[0].showIf = true; |
|
||||
table.sortNo = true; |
|
||||
grid.refreshStyle(100); |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
<q-btn |
|
||||
:color="table.sortNo ? '' : 'primary'" |
|
||||
:outline="table.sortNo ? true : false" |
|
||||
dense |
|
||||
label="隐藏" |
|
||||
unelevated |
|
||||
@click=" |
|
||||
() => { |
|
||||
table.columns[0].showIf = false; |
|
||||
table.sortNo = false; |
|
||||
grid.refreshStyle(100); |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
</q-btn-group> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</template> |
|
||||
|
|
||||
<q-separator /> |
|
||||
<q-expansion-item label="分割线"> |
|
||||
<q-card> |
|
||||
<q-card-section> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="separatorHorizontal" dense @update:model-value="separatorChange('horizontal', separatorHorizontal)" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>水平</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="separatorVertical" dense @update:model-value="separatorChange('vertical', separatorVertical)" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>垂直</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="separatorCell" dense @update:model-value="separatorChange('cell', separatorCell)" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>单元格</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="separatorNone" dense @update:model-value="separatorChange('none', separatorNone)" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>无</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-card-section> |
|
||||
</q-card> |
|
||||
</q-expansion-item> |
|
||||
|
|
||||
<q-separator /> |
|
||||
<q-expansion-item label="紧凑模式"> |
|
||||
<q-card> |
|
||||
<q-card-section> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="table.dense" dense @update:model-value="denseChange()" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>紧凑</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="table.denseToolbar" dense @update:model-value="denseChange" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>按钮栏紧凑</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="table.denseHeader" dense @update:model-value="denseChange" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>列头紧凑</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="table.denseBody" dense @update:model-value="denseChange" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>内容紧凑</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox v-model="table.denseBottom" dense @update:model-value="denseChange" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>分页栏紧凑</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-card-section> |
|
||||
</q-card> |
|
||||
</q-expansion-item> |
|
||||
|
|
||||
<q-separator /> |
|
||||
<q-item :clickable="false"> |
|
||||
<q-item-section> |
|
||||
<q-item-label class="nowrap text-nowrap">固定列</q-item-label> |
|
||||
</q-item-section> |
|
||||
<q-item-section side> |
|
||||
<q-select |
|
||||
v-model="table.stickyNum" |
|
||||
emit-value |
|
||||
map-options |
|
||||
:hide-bottom-space="true" |
|
||||
:hide-hint="true" |
|
||||
:outlined="true" |
|
||||
:dense="true" |
|
||||
:options="stickyOptions" |
|
||||
@update:model-value=" |
|
||||
() => { |
|
||||
grid.refreshStyle(500); |
|
||||
} |
|
||||
" |
|
||||
> |
|
||||
</q-select> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<q-separator /> |
|
||||
<q-item-label header>显示列</q-item-label> |
|
||||
|
|
||||
<template v-for="col in table.columns" :key="col.name"> |
|
||||
<q-item v-if="showColumn(col.name)" v-ripple tag="label" dense> |
|
||||
<q-item-section side> |
|
||||
<q-checkbox |
|
||||
v-model="col.showIf" |
|
||||
dense |
|
||||
@update:model-value=" |
|
||||
() => { |
|
||||
grid.refreshStyle(100); |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ col.label || col.name }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</template> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, ref, watch } from 'vue'; |
|
||||
const props = defineProps({ |
|
||||
scope: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
moreColumnTitleArray: { |
|
||||
// 多表头数组 |
|
||||
type: Array, |
|
||||
default: () => { |
|
||||
return []; |
|
||||
}, |
|
||||
}, |
|
||||
grid: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
const table = inject('table'); |
|
||||
const stickyOptions = [ |
|
||||
{ label: '不固定', value: 0 }, |
|
||||
{ label: '固定1列', value: 1 }, |
|
||||
{ label: '固定2列', value: 2 }, |
|
||||
{ label: '固定3列', value: 3 }, |
|
||||
{ label: '固定4列', value: 4 }, |
|
||||
{ label: '固定5列', value: 5 }, |
|
||||
{ label: '固定6列', value: 6 }, |
|
||||
{ label: '固定7列', value: 7 }, |
|
||||
{ label: '固定8列', value: 8 }, |
|
||||
{ label: '固定9列', value: 9 }, |
|
||||
{ label: '固定10列', value: 10 }, |
|
||||
]; |
|
||||
const separatorHorizontal = ref(false); |
|
||||
const separatorVertical = ref(false); |
|
||||
const separatorCell = ref(false); |
|
||||
const separatorNone = ref(false); |
|
||||
|
|
||||
const setSeparatorValue = (value) => { |
|
||||
if (value === 'horizontal') { |
|
||||
separatorHorizontal.value = true; |
|
||||
separatorVertical.value = false; |
|
||||
separatorCell.value = false; |
|
||||
separatorNone.value = false; |
|
||||
} else if (value === 'vertical') { |
|
||||
separatorHorizontal.value = false; |
|
||||
separatorVertical.value = true; |
|
||||
separatorCell.value = false; |
|
||||
separatorNone.value = false; |
|
||||
} else if (value === 'cell') { |
|
||||
separatorHorizontal.value = false; |
|
||||
separatorVertical.value = false; |
|
||||
separatorCell.value = true; |
|
||||
separatorNone.value = false; |
|
||||
} else if (value === 'none') { |
|
||||
separatorHorizontal.value = false; |
|
||||
separatorVertical.value = false; |
|
||||
separatorCell.value = false; |
|
||||
separatorNone.value = true; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
if (table.separator) { |
|
||||
setSeparatorValue(table.separator); |
|
||||
} |
|
||||
|
|
||||
const separatorChange = (separator, value) => { |
|
||||
if (!value) { |
|
||||
setSeparatorValue(table.separator); |
|
||||
} else { |
|
||||
table.separator = separator; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const showColumn = (name) => { |
|
||||
if (props.moreColumnTitleArray.length > 0) { |
|
||||
if ( |
|
||||
props.moreColumnTitleArray[0].findIndex((item) => { |
|
||||
return item.name === name; |
|
||||
}) > -1 |
|
||||
) { |
|
||||
return true; |
|
||||
} else { |
|
||||
return false; |
|
||||
} |
|
||||
} else { |
|
||||
return true; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
watch( |
|
||||
() => table.separator, |
|
||||
(newVal, oldVal) => { |
|
||||
if (newVal) { |
|
||||
setSeparatorValue(newVal); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
|
|
||||
const denseChange = () => { |
|
||||
props.grid.refreshStyle(0); |
|
||||
}; |
|
||||
</script> |
|
@ -1,359 +0,0 @@ |
|||||
<template> |
|
||||
<q-tr v-if="showRowEditButtonComputed"> |
|
||||
<q-td :colspan="props.noDataTrColspan"> |
|
||||
<div class="editButton text-center"> |
|
||||
<w-toolbar |
|
||||
:dense="true" |
|
||||
align="center" |
|
||||
:grid="props.grid" |
|
||||
:buttons="[ |
|
||||
{ |
|
||||
label: '保存', |
|
||||
name: 'save', |
|
||||
icon: 'save', |
|
||||
color: 'primary', |
|
||||
outline: false, |
|
||||
click: async (args) => { |
|
||||
save(args); |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
label: '取消', |
|
||||
name: 'cancel', |
|
||||
icon: 'close', |
|
||||
color: 'blue-grey', |
|
||||
outline: false, |
|
||||
click: (args) => { |
|
||||
if (table.bodyEditStatus === 'rowEdit' || table.bodyEditStatus === 'cellEdit') { |
|
||||
props.setOldValue(args.selected); |
|
||||
} else if (table.bodyEditStatus === 'rowsEdit') { |
|
||||
props.grid.getRows().forEach((item) => { |
|
||||
props.setOldValue(item); |
|
||||
}); |
|
||||
} |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
}, |
|
||||
}, |
|
||||
]" |
|
||||
></w-toolbar> |
|
||||
</div> |
|
||||
</q-td> |
|
||||
</q-tr> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { computed, inject, ref, toRaw } from 'vue'; |
|
||||
import { Tools, NotifyManager, noErrorAxios, t, ServerExceptionHandler } from '@/platform'; |
|
||||
import { editStatus } from './ts/grid'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
// 表格实例 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
url: { |
|
||||
// 表格所有的url |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
rowKeyName: { |
|
||||
type: String, |
|
||||
default: '', |
|
||||
}, |
|
||||
row: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
getRow: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
getRowComponentRefs: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
setOldValue: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
noDataTrColspan: { |
|
||||
type: Number, |
|
||||
default: () => { |
|
||||
return 0; |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
const table = inject('table'); |
|
||||
|
|
||||
const validate = async (refs) => { |
|
||||
let result = true; |
|
||||
for (let i = 0; i < refs.length; i++) { |
|
||||
const component = refs[i].getComponentRef(); |
|
||||
if (component) { |
|
||||
if (!Tools.isEmpty(refs[i].getComponentOneRef)) { |
|
||||
const componentOne = refs[i].getComponentOneRef(); |
|
||||
if (!Tools.isEmpty(componentOne) && !Tools.isEmpty(componentOne.validate)) { |
|
||||
const componentOneValidateResult = await componentOne.validate(); |
|
||||
if (!componentOneValidateResult) { |
|
||||
result = false; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
if (props.grid.props.tree && !Tools.isEmpty(component)) { |
|
||||
const keys = Object.keys(component); |
|
||||
for (let k = 0; k < keys.length; k++) { |
|
||||
if (!Tools.isEmpty(component[keys[k]]?.validate)) { |
|
||||
const treeComponentValidateResult = await component[keys[k]].validate(); |
|
||||
if (!treeComponentValidateResult) { |
|
||||
result = false; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} else if (!Tools.isEmpty(component.validate)) { |
|
||||
const componentValidateResult = await component.validate(); |
|
||||
if (!componentValidateResult) { |
|
||||
result = false; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return result; |
|
||||
}; |
|
||||
|
|
||||
const rowSave = (args) => { |
|
||||
let data = args.selected['_rowOldValue']; |
|
||||
let submitFlag = true; |
|
||||
let localeUpdateFlag = false; |
|
||||
let url = ''; |
|
||||
// 执行保存 |
|
||||
props.grid.emit('beforeEditorDataSubmit', { |
|
||||
grid: props.grid, |
|
||||
data: data, |
|
||||
callback: (handlerRequestParams: any | boolean, localeUpdate: boolean = false) => { |
|
||||
if (typeof handlerRequestParams === 'boolean' && handlerRequestParams === false) { |
|
||||
submitFlag = false; |
|
||||
} else { |
|
||||
data = handlerRequestParams; |
|
||||
} |
|
||||
localeUpdateFlag = localeUpdate; |
|
||||
}, |
|
||||
}); |
|
||||
if (localeUpdateFlag && submitFlag) { |
|
||||
// 只进行本地修改,不访问服务器 |
|
||||
props.grid.updateLocalData(data); |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
} else { |
|
||||
if (submitFlag) { |
|
||||
data = { ...args.selected, ...data }; |
|
||||
if (!Tools.isEmpty(props.url.editDataUrl)) { |
|
||||
url = props.url.editDataUrl + '/' + args.selected[props.grid.props.primaryKey]; |
|
||||
} else { |
|
||||
url = props.url.dataUrl + '/' + args.selected[props.grid.props.primaryKey]; |
|
||||
} |
|
||||
const requestParams = { |
|
||||
method: 'PUT', |
|
||||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|
||||
data: data, |
|
||||
url: url, |
|
||||
}; |
|
||||
noErrorAxios(requestParams) |
|
||||
.then((resp) => { |
|
||||
props.grid.emit('afterEditorDataSubmit', { grid: props.grid, data: resp.data }); |
|
||||
NotifyManager.info(t('tip.operationSuccess')); |
|
||||
if (props.grid.props.refreshData || !props.grid.props.tree) { |
|
||||
props.grid.refresh(); |
|
||||
} else if (resp.data) { |
|
||||
props.grid.updateLocalData(data); |
|
||||
} |
|
||||
// 保存成功后退出编辑状态 |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
if (error.code === 1001) { |
|
||||
NotifyManager.error('服务器验证未通过'); |
|
||||
} else { |
|
||||
ServerExceptionHandler.handle(error); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 检查数据是否修改过 |
|
||||
const checkDataModified = (row) => { |
|
||||
const keys = Object.keys(row['_rowOldValue']); |
|
||||
return keys.some((key) => row[key] !== row['_rowOldValue'][key]); |
|
||||
}; |
|
||||
|
|
||||
const treeDataPush = (arr) => { |
|
||||
const data = <any>[]; |
|
||||
if (arr && arr.length > 0) { |
|
||||
arr.forEach((item) => { |
|
||||
if (checkDataModified(item)) { |
|
||||
data.push(item['_rowOldValue']); |
|
||||
} |
|
||||
const childrenData = treeDataPush(item.children); |
|
||||
if (childrenData.length > 0) { |
|
||||
data.push(...childrenData); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
return data; |
|
||||
}; |
|
||||
|
|
||||
const rowsSave = (args) => { |
|
||||
let data = <any>[]; |
|
||||
const rows = args.grid.getRows(); |
|
||||
const isTree = props.grid.props.tree; |
|
||||
rows.forEach((item) => { |
|
||||
if (checkDataModified(item)) { |
|
||||
data.push(item['_rowOldValue']); |
|
||||
} |
|
||||
if (isTree && item.children) { |
|
||||
const childrenData = treeDataPush(item.children); |
|
||||
if (childrenData.length > 0) { |
|
||||
data.push(...childrenData); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
let submitFlag = true; |
|
||||
let localeUpdateFlag = false; |
|
||||
// 执行保存 |
|
||||
props.grid.emit('beforeEditorDataSubmit', { |
|
||||
grid: props.grid, |
|
||||
data: data, |
|
||||
callback: (handlerRequestParams: any | boolean, localeUpdate: boolean = false) => { |
|
||||
if (typeof handlerRequestParams === 'boolean' && handlerRequestParams === false) { |
|
||||
submitFlag = false; |
|
||||
} else { |
|
||||
data = handlerRequestParams; |
|
||||
} |
|
||||
localeUpdateFlag = localeUpdate; |
|
||||
}, |
|
||||
}); |
|
||||
if ((localeUpdateFlag && submitFlag) || props.grid.props.localMode) { |
|
||||
// 只进行本地修改,不访问服务器 |
|
||||
data.forEach((item) => { |
|
||||
props.grid.updateLocalData(item); |
|
||||
}); |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
} else { |
|
||||
if (submitFlag) { |
|
||||
updates(data, (callbackData) => { |
|
||||
NotifyManager.info(t('tip.operationSuccess')); |
|
||||
if (props.grid.props.refreshData || !props.grid.props.tree) { |
|
||||
props.grid.refresh(); |
|
||||
} else if (!Tools.isEmpty(callbackData)) { |
|
||||
callbackData.forEach((item) => { |
|
||||
props.grid.updateLocalData(item); |
|
||||
}); |
|
||||
} |
|
||||
// 保存成功后退出编辑状态 |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const save = async (args) => { |
|
||||
const refs = props.getRowComponentRefs(table.bodyEditStatus === 'rowEdit' || table.bodyEditStatus === 'cellEdit' ? args.selected[props.rowKeyName] : []); |
|
||||
const result = await validate(refs); |
|
||||
if (!result) { |
|
||||
NotifyManager.error('验证未通过'); |
|
||||
} else { |
|
||||
if (table.bodyEditStatus === 'rowEdit' || table.bodyEditStatus === 'cellEdit') { |
|
||||
rowSave(args); |
|
||||
} else if (table.bodyEditStatus === 'rowsEdit') { |
|
||||
rowsSave(args); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 批量更新数据 |
|
||||
const updates = (data, callback) => { |
|
||||
const requestParams = { |
|
||||
method: 'PUT', |
|
||||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|
||||
data: data, |
|
||||
url: props.url.dataUrl + '/updates', |
|
||||
}; |
|
||||
noErrorAxios(requestParams) |
|
||||
.then((resp) => { |
|
||||
if (!Tools.isEmpty(callback)) { |
|
||||
callback(resp?.data); |
|
||||
} |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
if (error.code === 1001) { |
|
||||
NotifyManager.error('服务器验证未通过'); |
|
||||
} else { |
|
||||
ServerExceptionHandler.handle(error); |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const showRowEditButtonComputed = computed(() => { |
|
||||
if (props.grid.props.localMode && table.bodyEditStatus !== editStatus.rows) { |
|
||||
return false; |
|
||||
} |
|
||||
if (table.bodyEditStatus === 'cellEdit') { |
|
||||
const selected = props.grid.getSelectedCell(); |
|
||||
if (selected?.colName && selected.colName === table.cellSelected['colName'] && selected.row[props.rowKeyName] === props.row[props.rowKeyName]) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} else if (table.bodyEditStatus === 'rowEdit' && isSelectedRowComputed.value) { |
|
||||
return true; |
|
||||
} else if (table.bodyEditStatus === 'rowsEdit' && table.rows.length > 0 && isLastRowComputed.value) { |
|
||||
return true; |
|
||||
} else { |
|
||||
return false; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
const checkLastRow = (row) => { |
|
||||
if (props.grid.props.tree && row['expand'] && !Tools.isEmpty(row.children) && row.children.length > 0) { |
|
||||
const childrenLastRow = row.children[row.children.length - 1]; |
|
||||
if (childrenLastRow['expand'] && !Tools.isEmpty(childrenLastRow.children) && childrenLastRow.children.length > 0) { |
|
||||
return checkLastRow(childrenLastRow); |
|
||||
} else { |
|
||||
return childrenLastRow[props.rowKeyName] === props.row[props.rowKeyName]; |
|
||||
} |
|
||||
} else { |
|
||||
return row[props.rowKeyName] === props.row[props.rowKeyName]; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const isLastRowComputed = computed(() => { |
|
||||
const lastRow = table.rows[table.rows.length - 1]; |
|
||||
return checkLastRow(lastRow); |
|
||||
}); |
|
||||
|
|
||||
const isSelectedRowComputed = computed(() => { |
|
||||
const selected = props.grid.getSelectedRow(); |
|
||||
if (!Tools.isEmpty(selected)) { |
|
||||
return props.row[props.rowKeyName] === props.grid.getSelectedRow()[props.rowKeyName]; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
|
|
||||
defineExpose({}); |
|
||||
</script> |
|
||||
|
|
||||
<style lang="css"> |
|
||||
.editButton { |
|
||||
position: sticky; |
|
||||
background-color: white; |
|
||||
left: 45%; |
|
||||
width: 150px; |
|
||||
} |
|
||||
</style> |
|
@ -1,232 +0,0 @@ |
|||||
<template> |
|
||||
<w-dialog ref="dialogRef" v-bind="props.grid.props.editor.dialog" :title="dialog.dialogTitle" :buttons="dialogButtonsComputed"> |
|
||||
<w-form ref="dialogFormRef" v-bind="props.grid.props.editor.form" class="pt-1.5 px-1.5"></w-form> |
|
||||
</w-dialog> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, reactive, inject, computed } from 'vue'; |
|
||||
import { t, Tools, noErrorAxios, NotifyManager, ServerExceptionHandler } from '@/platform'; |
|
||||
|
|
||||
const dialogRef = ref(); |
|
||||
const dialogFormRef = ref(); |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
// 表格实例 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
url: { |
|
||||
// 表格所有的url |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
request: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
setRowDataExtraProperty: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
getRow: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
}); |
|
||||
const table = inject('table'); |
|
||||
|
|
||||
const dialogButtonsComputed = computed(() => { |
|
||||
if (props.grid.props.editor?.dialog?.buttons) { |
|
||||
return [...props.grid.props.editor.dialog.buttons, ...dialog.dialogButtons]; |
|
||||
} |
|
||||
return dialog.dialogButtons; |
|
||||
}); |
|
||||
|
|
||||
const save = async () => { |
|
||||
dialog.dialogButtons[0].loading = true; |
|
||||
const formStatus = dialogFormRef.value.getStatus(); |
|
||||
const validate = await dialogFormRef.value.validate(); |
|
||||
if (validate) { |
|
||||
let dialogFormData = dialogFormRef.value.getData(); |
|
||||
const selected = props.grid.getSelectedRow(); |
|
||||
if (formStatus === 'edit' && selected[props.grid.props.primaryKey] && Tools.isEmpty(dialogFormData[props.grid.props.primaryKey])) { |
|
||||
dialogFormData[props.grid.props.primaryKey] = selected[props.grid.props.primaryKey]; |
|
||||
} |
|
||||
let submitFlag = true; |
|
||||
let closeDialog = true; |
|
||||
props.grid.emit('beforeEditorDataSubmit', { |
|
||||
grid: props.grid, |
|
||||
data: dialogFormData, |
|
||||
callback: (handlerRequestParams: any | boolean, closeFlag: boolean = true) => { |
|
||||
if (typeof handlerRequestParams === 'boolean' && handlerRequestParams === false) { |
|
||||
submitFlag = false; |
|
||||
} else { |
|
||||
dialogFormData = handlerRequestParams; |
|
||||
} |
|
||||
closeDialog = closeFlag; |
|
||||
}, |
|
||||
}); |
|
||||
if (submitFlag) { |
|
||||
if (formStatus === 'addTop') { |
|
||||
dialogFormData[props.grid.props.foreignKey] = null; |
|
||||
} else if (formStatus === 'addChild') { |
|
||||
dialogFormData[props.grid.props.foreignKey] = selected[props.grid.props.primaryKey]; |
|
||||
} else if ((formStatus === 'edit' || formStatus === 'clone') && selected[props.grid.props.foreignKey]) { |
|
||||
dialogFormData[props.grid.props.foreignKey] = selected[props.grid.props.foreignKey]; |
|
||||
} |
|
||||
if (formStatus === 'edit') { |
|
||||
// 将行数据默认添加到传递给后端的数据中 |
|
||||
dialogFormData = { ...selected, ...dialogFormData }; |
|
||||
} |
|
||||
if (props.grid.props.localMode) { |
|
||||
if (formStatus === 'add' || formStatus === 'clone' || formStatus === 'addTop' || formStatus === 'addChild') { |
|
||||
addData(dialogFormData); |
|
||||
} else { |
|
||||
updateData(dialogFormData); |
|
||||
} |
|
||||
dialog.dialogButtons[0].loading = false; |
|
||||
if (closeDialog) { |
|
||||
dialogRef.value.hide(); |
|
||||
} |
|
||||
} else { |
|
||||
let requestParams = { |
|
||||
method: getMethod(formStatus), |
|
||||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|
||||
data: dialogFormData, |
|
||||
url: getUrl(formStatus, dialogFormData), |
|
||||
}; |
|
||||
dialog.dialogButtons[0].loading = false; |
|
||||
noErrorAxios(requestParams) |
|
||||
.then((resp) => { |
|
||||
dialog.dialogButtons[0].loading = false; |
|
||||
props.grid.emit('afterEditorDataSubmit', { grid: props.grid, data: resp.data }); |
|
||||
NotifyManager.info(t('tip.operationSuccess')); |
|
||||
if (closeDialog) { |
|
||||
dialogRef.value.hide(); |
|
||||
} |
|
||||
if (props.grid.props.refreshData || !props.grid.props.tree) { |
|
||||
props.grid.refresh(); |
|
||||
} else if (resp.data && (formStatus === 'add' || formStatus === 'clone' || formStatus === 'addTop' || formStatus === 'addChild')) { |
|
||||
addData(resp.data); |
|
||||
} else if (resp.data) { |
|
||||
updateData(resp.data); |
|
||||
} |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
if (error?.code === 1001) { |
|
||||
// 验证错误 |
|
||||
dialogFormRef.value.setValidationErrors(error.data); |
|
||||
} else { |
|
||||
ServerExceptionHandler.handle(error); |
|
||||
} |
|
||||
dialog.dialogButtons[0].loading = false; |
|
||||
}); |
|
||||
} |
|
||||
} else { |
|
||||
dialog.dialogButtons[0].loading = false; |
|
||||
if (closeDialog) { |
|
||||
dialogRef.value.hide(); |
|
||||
} |
|
||||
} |
|
||||
} else { |
|
||||
dialog.dialogButtons[0].loading = false; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const dialog = reactive({ |
|
||||
dialogTitle: t('action.addNew'), |
|
||||
dialogButtons: [ |
|
||||
{ |
|
||||
icon: 'beenhere', |
|
||||
labelI18nKey: props.grid.props.localMode ? 'confirm' : 'action.submit', |
|
||||
label: t(props.grid.props.localMode ? 'confirm' : 'action.submit'), |
|
||||
loading: false, |
|
||||
click: () => { |
|
||||
save(); |
|
||||
}, |
|
||||
}, |
|
||||
], |
|
||||
}); |
|
||||
|
|
||||
// 新增树表格中的数据 |
|
||||
const addTreeRow = (row) => { |
|
||||
if (Tools.isEmpty(row[props.grid.props.foreignKey])) { |
|
||||
table.rows.push(row); |
|
||||
} else { |
|
||||
const parent = props.getRow(table.rows, row[props.grid.props.foreignKey], true); |
|
||||
if (parent) { |
|
||||
if (parent['children'] && Array.isArray(parent['children'])) { |
|
||||
parent['children'].push(row); |
|
||||
} else { |
|
||||
parent['children'] = [row]; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const addData = (rowData) => { |
|
||||
if (props.grid.props.tree) { |
|
||||
addTreeRow(rowData); |
|
||||
props.setRowDataExtraProperty(table.rows); |
|
||||
} else { |
|
||||
props.grid.addLocalData(rowData, false); |
|
||||
} |
|
||||
}; |
|
||||
const updateData = (rowData) => { |
|
||||
const selected = props.grid.getSelectedRow(); |
|
||||
if (Tools.isEmpty(rowData[props.grid.props.primaryKey])) { |
|
||||
rowData[props.grid.props.primaryKey] = selected[props.grid.props.primaryKey]; |
|
||||
} |
|
||||
rowData[props.grid.props.selectedField] = true; |
|
||||
if (selected['children']) { |
|
||||
rowData['children'] = selected['children']; |
|
||||
} |
|
||||
props.grid.updateLocalData(rowData); |
|
||||
}; |
|
||||
|
|
||||
const getMethod = (formStatus: string) => { |
|
||||
if (formStatus === 'add' || formStatus === 'clone' || formStatus === 'addTop' || formStatus === 'addChild') { |
|
||||
return 'POST'; |
|
||||
} else { |
|
||||
return 'PUT'; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const getUrl = (formStatus: string, formData: any) => { |
|
||||
if (formStatus === 'add' || formStatus === 'clone' || formStatus === 'addTop' || formStatus === 'addChild') { |
|
||||
if (!Tools.isEmpty(props.url.addDataUrl)) { |
|
||||
return props.url.addDataUrl; |
|
||||
} else { |
|
||||
return props.url.dataUrl; |
|
||||
} |
|
||||
} else { |
|
||||
if (!Tools.isEmpty(props.url.editDataUrl)) { |
|
||||
return props.url.editDataUrl + '/' + formData[props.grid.props.primaryKey]; |
|
||||
} else { |
|
||||
return props.url.dataUrl + '/' + formData[props.grid.props.primaryKey]; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const resetButtonLabel = () => { |
|
||||
dialog.dialogButtons[0].label = t(dialog.dialogButtons[0].labelI18nKey); |
|
||||
}; |
|
||||
const getDialog = () => { |
|
||||
return dialogRef.value; |
|
||||
}; |
|
||||
const getForm = () => { |
|
||||
return dialogFormRef.value; |
|
||||
}; |
|
||||
|
|
||||
defineExpose({ |
|
||||
resetButtonLabel, |
|
||||
getDialog, |
|
||||
getForm, |
|
||||
}); |
|
||||
</script> |
|
||||
<style lang="css"></style> |
|
@ -1,73 +0,0 @@ |
|||||
<template> |
|
||||
<template v-if="props.grid.props.pageable && !props.grid.props.tree"> |
|
||||
<template v-if="props.state.refHeightWidth.middleWidth > 600"> |
|
||||
<q-pagination |
|
||||
v-model="page" |
|
||||
:boundary-links="props.state.pagination.config.boundaryLinks" |
|
||||
:boundary-numbers="props.state.pagination.config.boundaryNumbers" |
|
||||
:direction-links="props.state.pagination.config.directionLinks" |
|
||||
:ellipses="props.state.pagination.config.ellipses" |
|
||||
:max-pages="props.state.pagination.config.maxPages" |
|
||||
:min="1" |
|
||||
:max="props.scope.pagesNumber" |
|
||||
:size="props.denseBottom ? '10px' : ''" |
|
||||
@update:model-value="pageChange" |
|
||||
/> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<q-pagination |
|
||||
v-model="page" |
|
||||
:boundary-links="props.state.pagination.config.boundaryLinks" |
|
||||
:boundary-numbers="props.state.pagination.config.boundaryNumbers" |
|
||||
:direction-links="props.state.pagination.config.directionLinks" |
|
||||
:ellipses="props.state.pagination.config.ellipses" |
|
||||
:max-pages="3" |
|
||||
:min="1" |
|
||||
:max="props.scope.pagesNumber" |
|
||||
:size="props.denseBottom ? '10px' : ''" |
|
||||
@update:model-value="pageChange" |
|
||||
/> |
|
||||
</template> |
|
||||
<span>{{ $t('tip.pagenation.totalRecord', { count: props.state.pagination.rowsNumber }) }}</span> |
|
||||
</template> |
|
||||
<template v-else> {{ $t('tip.pagenation.totalRecord', { count: props.state.pagination.rowsNumber }) }} </template> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
const page = defineModel({ type: Number, default: 1 }); |
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
// 表格实例 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
scope: { |
|
||||
// 顶部插槽属性 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
state: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
denseBottom: { |
|
||||
type: Boolean, |
|
||||
default: false, |
|
||||
}, |
|
||||
request: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
const pageChange = (value) => { |
|
||||
page.value = value; |
|
||||
props.request(props.state); |
|
||||
}; |
|
||||
</script> |
|
||||
<style lang="css"></style> |
|
@ -1,210 +0,0 @@ |
|||||
<template> |
|
||||
<template v-if="rowSpanIsFirstComputed"> |
|
||||
<q-td |
|
||||
ref="tdRef" |
|
||||
:key="col.name" |
|
||||
:props="scope" |
|
||||
:title="titleComputed" |
|
||||
:class="props.grid.props.selectMode === selectMode.cell && (table.bodyEditStatus === 'none' || props.grid.props.localMode) ? tdClassComputed : ''" |
|
||||
:rowspan="rowSpanComputed" |
|
||||
:style="col.type && table.bodyEditStatus !== 'none' ? 'width: ' + (tdWidth - 24) + 'px;' : ''" |
|
||||
@click=" |
|
||||
() => { |
|
||||
// 判定是否要退出编辑模式 |
|
||||
if (props.grid.props.localMode && table.bodyEditStatus === editStatus.cell && props.grid.props.selectMode === selectMode.cell) { |
|
||||
if (table['cellSelected']['colName'] !== col['name'] || table['cellSelected']['row'][props.rowKeyName] !== props.scope.row[props.rowKeyName]) { |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
} |
|
||||
} else if (props.grid.props.localMode && table.bodyEditStatus === editStatus.row && props.grid.props.selectMode === selectMode.cell) { |
|
||||
if (table['cellSelected']['row'][props.rowKeyName] !== props.scope.row[props.rowKeyName]) { |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
} |
|
||||
} |
|
||||
if (table.bodyEditStatus === 'none' && props.grid.props.selectMode === selectMode.cell) { |
|
||||
table['cellSelected'] = { |
|
||||
row: toRaw(scope.row), |
|
||||
rowKey: scope.row[props.rowKeyName], |
|
||||
primaryKey: scope.row[props.grid.props.primaryKey], |
|
||||
colName: col['name'], |
|
||||
value: scope.row[col['name']], |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
" |
|
||||
> |
|
||||
<template v-if="col.name === '_sortNo_'"> |
|
||||
{{ scope.rowIndex + 1 }} |
|
||||
</template> |
|
||||
<template |
|
||||
v-if=" |
|
||||
!Tools.isEmpty(col.type) && |
|
||||
((props.isSelectedRow && table.bodyEditStatus === 'rowEdit') || |
|
||||
table.bodyEditStatus === 'rowsEdit' || |
|
||||
(isSelectedCellComputed && table.bodyEditStatus === 'cellEdit')) |
|
||||
" |
|
||||
> |
|
||||
<template v-if="props.grid.props.localMode && table.bodyEditStatus !== editStatus.rows"> |
|
||||
<component |
|
||||
:is="col.type" |
|
||||
ref="componentRef" |
|
||||
v-bind="componentAttrs(col)" |
|
||||
v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)[col.name]" |
|
||||
bg-color="light-green-1" |
|
||||
></component> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<component |
|
||||
:is="col.type" |
|
||||
ref="componentRef" |
|
||||
v-bind="componentAttrs(col)" |
|
||||
v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)['_rowOldValue'][col.name]" |
|
||||
bg-color="light-green-1" |
|
||||
></component> |
|
||||
</template> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<template v-if="!Tools.isEmpty(value) && typeof value === 'object' && value.componentType && value.bindModelValue"> |
|
||||
<component |
|
||||
:is="value.componentType" |
|
||||
v-bind="value.attrs" |
|
||||
v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)[col.name]" |
|
||||
></component> |
|
||||
</template> |
|
||||
<template v-else-if="!Tools.isEmpty(value) && typeof value === 'object' && value.componentType"> |
|
||||
<component :is="value.componentType" v-bind="value.attrs"></component> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<span v-dompurify-html="Tools.isUndefinedOrNull(value) ? '' : value"></span> |
|
||||
</template> |
|
||||
</template> |
|
||||
</q-td> |
|
||||
</template> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, computed, ref, toRaw, onMounted } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
import { selectMode, editStatus } from './ts/grid.ts'; |
|
||||
|
|
||||
const tdRef = ref(); |
|
||||
const componentRef = ref(); |
|
||||
const tdWidth = ref(0); |
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
type: Object, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
getRow: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
rowKeyName: { |
|
||||
type: String, |
|
||||
default: '', |
|
||||
}, |
|
||||
scope: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
col: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
value: { |
|
||||
type: [Object, String, Number, Boolean], |
|
||||
default: '', |
|
||||
}, |
|
||||
isSelectedRow: { |
|
||||
type: Boolean, |
|
||||
default: false, |
|
||||
}, |
|
||||
}); |
|
||||
const table = inject('table'); |
|
||||
|
|
||||
const getComponentRef = () => { |
|
||||
return componentRef.value; |
|
||||
}; |
|
||||
|
|
||||
const isSelectedCellComputed = computed(() => { |
|
||||
const selected = props.grid.getSelectedCell(); |
|
||||
if (selected?.colName && props.scope.row[props.rowKeyName] === selected['row'][props.rowKeyName] && props.col['name'] === selected.colName) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
|
|
||||
const titleComputed = computed(() => { |
|
||||
if (table['columns'] && table['columns'].length > 0) { |
|
||||
const column = table['columns'].find((item) => item['name'] === props.col.name); |
|
||||
if (column && column['title'] && typeof column['title'] === 'function') { |
|
||||
return column.title({ grid: props.grid, row: toRaw(props.scope.row), value: props.scope.row[props.col.name] }); |
|
||||
} else if (column && column['title']) { |
|
||||
return column['title']; |
|
||||
} |
|
||||
} |
|
||||
if (props.col.classes?.indexOf('truncate') > -1 && !Tools.isEmpty(props.value) && typeof props.value !== 'object') { |
|
||||
return props.value; |
|
||||
} |
|
||||
return ''; |
|
||||
}); |
|
||||
|
|
||||
const rowSpanComputed = computed(() => { |
|
||||
if (Object.keys(table.mergeRecords).length > 0) { |
|
||||
const column = table.mergeRecords[props.col['name']]; |
|
||||
if (column && column[props.scope['row'][props.col['name']]].length > 1) { |
|
||||
return column[props.scope['row'][props.col['name']]].length; |
|
||||
} |
|
||||
} |
|
||||
return 1; |
|
||||
}); |
|
||||
|
|
||||
const rowSpanIsFirstComputed = computed(() => { |
|
||||
if (Object.keys(table.mergeRecords).length > 0) { |
|
||||
const column = table.mergeRecords[props.col['name']]; |
|
||||
if (props.col['name'] === '_sortNo_') { |
|
||||
return true; |
|
||||
} else if (column && column[props.scope['row'][props.col['name']]].length > 1) { |
|
||||
return props.scope['row'][props.rowKeyName] === column[props.scope['row'][props.col['name']]][0]; |
|
||||
} |
|
||||
} |
|
||||
return true; |
|
||||
}); |
|
||||
|
|
||||
const tdClassComputed = computed(() => { |
|
||||
const tdClass = <any>[]; |
|
||||
if (props.grid.props.selectMode === selectMode.cell) { |
|
||||
tdClass.push('cellHover'); |
|
||||
} |
|
||||
if (table && table['cellSelected'] && Tools.hasOwnProperty(table['cellSelected'], 'colName')) { |
|
||||
if (table['cellSelected']['colName'] === props.col['name'] && table['cellSelected']['rowKey'] === props.scope.row[props.rowKeyName]) |
|
||||
tdClass.push('cellSelected'); |
|
||||
} |
|
||||
return tdClass; |
|
||||
}); |
|
||||
|
|
||||
const componentAttrs = (col) => { |
|
||||
if (col.attrs) { |
|
||||
return col.attrs; |
|
||||
} else if (props.grid.props.editor?.form?.fields) { |
|
||||
const field = props.grid.props.editor.form.fields.find((item) => item['name'] === col.name); |
|
||||
if (field) { |
|
||||
return { ...field, label: undefined }; |
|
||||
} |
|
||||
} |
|
||||
return undefined; |
|
||||
}; |
|
||||
|
|
||||
onMounted(() => { |
|
||||
if (tdRef.value?.$el) { |
|
||||
tdWidth.value = tdRef.value.$el.clientWidth; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
defineExpose({ |
|
||||
getComponentRef, |
|
||||
}); |
|
||||
</script> |
|
||||
<style lang="css"></style> |
|
@ -1,786 +0,0 @@ |
|||||
<template> |
|
||||
<div class="col"> |
|
||||
<w-form ref="formRef" v-bind="props.grid.props.queryFormAttrs" :fields="queryFormFieldsComputed" :cols-num="props.grid.props.queryFormColsNum"></w-form> |
|
||||
<div |
|
||||
v-if="props.grid.props.title || buttons_.length > 0 || props.grid.props.configButton || fields.length > 0" |
|
||||
class="flex flex-nowrap items-end" |
|
||||
:class="fields.length > 0 ? 'pt-2.5' : ''" |
|
||||
> |
|
||||
<div class="flex-none">{{ $t(props.grid.props.title ? props.grid.props.title : '') }}</div> |
|
||||
<div class="flex-1"> |
|
||||
<w-toolbar |
|
||||
ref="toolbarRef" |
|
||||
:dense="denseToolbarComputed" |
|
||||
v-bind="props.grid.props.toolbarConfigure" |
|
||||
:buttons="toolbarButtonsComputed" |
|
||||
:grid="props.grid" |
|
||||
></w-toolbar> |
|
||||
</div> |
|
||||
<div v-if="props.grid.props.configButton" class="flex-none pl-1"> |
|
||||
<q-btn round dense :size="denseToolbarComputed ? '13px' : undefined" icon="manage_accounts" unelevated outline> |
|
||||
<q-popup-proxy v-model="table.gridConfig"> |
|
||||
<GridConfig :scope="props.scope" :more-column-title-array="props.moreColumnTitleArray" :grid="props.grid"></GridConfig> |
|
||||
</q-popup-proxy> |
|
||||
</q-btn> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { computed, inject, ref, reactive, nextTick, onBeforeMount, toRaw } from 'vue'; |
|
||||
import { useQuasar, exportFile } from 'quasar'; |
|
||||
import { axios, Tools, t, NotifyManager } from '@/platform'; |
|
||||
import { selectMode, formStatus, editStatus } from './ts/grid'; |
|
||||
import GridConfig from './GridConfig.vue'; |
|
||||
|
|
||||
const $q = useQuasar(); |
|
||||
|
|
||||
const formRef = ref(); |
|
||||
const toolbarRef = ref(); |
|
||||
const localeFlag = ref(false); |
|
||||
const fields = ref(<any>[]); |
|
||||
const buttons_ = ref(<any>[]); |
|
||||
const moreQueryStatus = ref(false); // 当前更多查询状态是否激活 |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
// 表格实例 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
scope: { |
|
||||
// 顶部插槽属性 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
moreColumnTitleArray: { |
|
||||
// 多表头数组 |
|
||||
type: Array, |
|
||||
default: () => { |
|
||||
return []; |
|
||||
}, |
|
||||
}, |
|
||||
buildQueryCriterias: { |
|
||||
// 构建查询 |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
columns: { |
|
||||
// 表格列集合 |
|
||||
type: Array, |
|
||||
default: () => { |
|
||||
return []; |
|
||||
}, |
|
||||
}, |
|
||||
url: { |
|
||||
// 表格所有的url |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
const table = inject('table'); |
|
||||
|
|
||||
const queryFormFieldsComputed = computed(() => { |
|
||||
localeFlag.value; |
|
||||
return fields.value; |
|
||||
}); |
|
||||
const toolbarButtonsComputed = computed(() => { |
|
||||
localeFlag.value; |
|
||||
return buttons_.value; |
|
||||
}); |
|
||||
const denseToolbarComputed = computed(() => { |
|
||||
if (table.denseToolbar) { |
|
||||
return true; |
|
||||
} else if (table.dense !== false) { |
|
||||
return true; |
|
||||
} else { |
|
||||
return false; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
const screenCols = { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 }; |
|
||||
const queryFormColsNumComputed = computed(() => { |
|
||||
if (typeof props.grid.props.queryFormColsNum === 'number' && props.grid.props.queryFormColsNum > 0) { |
|
||||
return props.grid.props.queryFormColsNum; |
|
||||
} else if (typeof props.grid.props.queryFormColsNum === 'object') { |
|
||||
const screen = { ...screenCols, ...props.grid.props.queryFormColsNum }; |
|
||||
return screen[$q.screen.name]; |
|
||||
} |
|
||||
return screenCols[$q.screen.name]; |
|
||||
}); |
|
||||
|
|
||||
// 处理查询form显示的字段 |
|
||||
const handlerQueryFormShowField = () => { |
|
||||
fields.value = []; |
|
||||
if (moreQueryStatus.value) { |
|
||||
props.grid.props.queryFormFields.forEach((item: any) => { |
|
||||
fields.value.push(item); |
|
||||
item.showIf = () => { |
|
||||
return true; |
|
||||
}; |
|
||||
}); |
|
||||
} else { |
|
||||
// 一行应该显示的字段个数 |
|
||||
const rowColsNum = queryFormColsNumComputed.value * (props.grid.props.queryFormRowNum || 1); |
|
||||
let currRowColsNum = 0; |
|
||||
props.grid.props.queryFormFields.forEach((item: any) => { |
|
||||
if (Tools.hasOwnProperty(item, 'colSpan')) { |
|
||||
currRowColsNum += item.colSpan; |
|
||||
} else { |
|
||||
currRowColsNum += 1; |
|
||||
} |
|
||||
if (currRowColsNum <= rowColsNum) { |
|
||||
fields.value.push(item); |
|
||||
item.showIf = (form) => { |
|
||||
return true; |
|
||||
}; |
|
||||
} else { |
|
||||
item.showIf = (form) => { |
|
||||
return false; |
|
||||
}; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const edit = (selected) => { |
|
||||
if (!selected) { |
|
||||
NotifyManager.warn(t('action.edit.tip')); |
|
||||
} else { |
|
||||
openEditor(t('action.edit'), formStatus.edit, selected); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const clone = (selected) => { |
|
||||
if (!selected) { |
|
||||
NotifyManager.warn(t('action.copy.tip')); |
|
||||
} else { |
|
||||
selected[props.grid.props.primaryKey] = undefined; |
|
||||
openEditor(t('action.copy'), formStatus.clone, selected); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const remove = () => { |
|
||||
const ids = <any>[]; |
|
||||
const tickedRows = props.grid.getTickedRows(); |
|
||||
const selectedRows = props.grid.getSelectedRows(); |
|
||||
if (tickedRows?.length > 0) { |
|
||||
tickedRows.forEach((item) => { |
|
||||
ids.push(props.grid.props.localMode ? item['_rowKey_'] : item[props.grid.props.primaryKey]); |
|
||||
}); |
|
||||
} else if (selectedRows?.length > 0) { |
|
||||
selectedRows.forEach((item) => { |
|
||||
ids.push(props.grid.props.localMode ? item['_rowKey_'] : item[props.grid.props.primaryKey]); |
|
||||
}); |
|
||||
} |
|
||||
let flag = true; |
|
||||
props.grid.emit('beforeRemove', { |
|
||||
grid: props.grid, |
|
||||
ids: ids, |
|
||||
callback: (param: any) => { |
|
||||
if (param && Array.isArray(param)) { |
|
||||
ids.splice(0, ids.length - 1); |
|
||||
ids.push(...param); |
|
||||
} else { |
|
||||
flag = false; |
|
||||
} |
|
||||
}, |
|
||||
}); |
|
||||
if (flag) { |
|
||||
if (props.grid.props.localMode) { |
|
||||
props.grid.removeLocalData(ids); |
|
||||
} else { |
|
||||
let requestParams: any = { |
|
||||
method: 'DELETE', |
|
||||
url: props.url.removeDataUrl || props.url.dataUrl, |
|
||||
data: ids, |
|
||||
}; |
|
||||
axios(requestParams) |
|
||||
.then((resp) => { |
|
||||
props.grid.emit('afterRemove', { grid: props.grid, ids: resp?.data }); |
|
||||
NotifyManager.info(t('tip.operationSuccess')); |
|
||||
if (props.grid.props.refreshData || !props.grid.props.tree) { |
|
||||
props.grid.refresh(); |
|
||||
} else { |
|
||||
props.grid.removeLocalData(resp?.data); |
|
||||
} |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
console.error('[w-grid]Remove error:', error); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const getExportData = async () => { |
|
||||
let resultData = <any>[]; |
|
||||
const reqParams: any = { pageable: false }; |
|
||||
let urlSearchParams = props.buildQueryCriterias(reqParams); |
|
||||
const resp = await axios.get(props.url.fetchDataUrl || props.url.dataUrl, { params: urlSearchParams }); |
|
||||
if (resp && resp.data) { |
|
||||
const responseData = resp.data; |
|
||||
if (Array.isArray(responseData)) { |
|
||||
resultData = responseData; |
|
||||
} else if (typeof responseData === 'object' && responseData.content) { |
|
||||
resultData = responseData.content; |
|
||||
} |
|
||||
} |
|
||||
return resultData; |
|
||||
}; |
|
||||
|
|
||||
const wrapCsvValue = (val, formatFn, row) => { |
|
||||
let formatted = formatFn !== void 0 ? formatFn(val, row) : val; |
|
||||
formatted = formatted === void 0 || formatted === null ? '' : String(formatted); |
|
||||
formatted = formatted.split('"').join('""'); |
|
||||
/** |
|
||||
* Excel accepts \n and \r in strings, but some other CSV parsers do not |
|
||||
* Uncomment the next two lines to escape new lines |
|
||||
*/ |
|
||||
// .split('\n').join('\\n') |
|
||||
// .split('\r').join('\\r') |
|
||||
return `"${formatted}"`; |
|
||||
}; |
|
||||
|
|
||||
const resetDefaultValues = () => { |
|
||||
let requestParams: any = { |
|
||||
method: 'POST', |
|
||||
url: props.url.dataUrl + '/resetDefaultValues', |
|
||||
}; |
|
||||
axios(requestParams) |
|
||||
.then((resp) => { |
|
||||
NotifyManager.info(t('tip.operationSuccess')); |
|
||||
props.grid.refresh(); |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
console.error('[w-grid]ResetDefaultValues error:', error); |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const showLoading = (msg: string = '正在处理,请稍等...') => { |
|
||||
$q.loading.show({ |
|
||||
message: msg, |
|
||||
boxClass: 'bg-grey-2 text-grey-9', |
|
||||
spinnerColor: 'primary', |
|
||||
}); |
|
||||
}; |
|
||||
const hideLoading = () => { |
|
||||
$q.loading.hide(); |
|
||||
}; |
|
||||
|
|
||||
const openEditor = (title, status, data) => { |
|
||||
props.grid.getEditorDialog().show(); |
|
||||
nextTick(() => { |
|
||||
props.grid.getEditorDialog().setTitle(title); |
|
||||
props.grid.getEditorForm().setStatus(status); |
|
||||
if (!Tools.isEmpty(data)) { |
|
||||
props.grid.getEditorForm().setData(data); |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const buttonObj = reactive({ |
|
||||
separator: 'separator', |
|
||||
query: { |
|
||||
name: 'query', |
|
||||
icon: 'search', |
|
||||
labelI18nKey: 'action.query', |
|
||||
label: t('action.query'), |
|
||||
click: () => { |
|
||||
props.grid.refresh(); |
|
||||
}, |
|
||||
}, |
|
||||
moreQuery: { |
|
||||
name: 'moreQuery', |
|
||||
icon: 'zoom_in', |
|
||||
labelI18nKey: 'action.moreQueryConditions', |
|
||||
label: t('action.moreQueryConditions'), |
|
||||
enableIf: () => { |
|
||||
if (props.grid.props.queryFormFields.length <= fields.value.length && !moreQueryStatus.value) { |
|
||||
return false; |
|
||||
} else { |
|
||||
return true; |
|
||||
} |
|
||||
}, |
|
||||
click: () => { |
|
||||
moreQueryStatus.value = !moreQueryStatus.value; |
|
||||
handlerQueryFormShowField(); |
|
||||
}, |
|
||||
}, |
|
||||
reset: { |
|
||||
name: 'reset', |
|
||||
icon: 'restart_alt', |
|
||||
labelI18nKey: 'action.reset', |
|
||||
label: t('action.reset'), |
|
||||
click: () => { |
|
||||
formRef.value.reset(); |
|
||||
}, |
|
||||
}, |
|
||||
refresh: { |
|
||||
name: 'refresh', |
|
||||
icon: 'loop', |
|
||||
labelI18nKey: 'action.refresh', |
|
||||
label: t('action.refresh'), |
|
||||
click: () => { |
|
||||
props.grid.refresh(); |
|
||||
}, |
|
||||
}, |
|
||||
add: { |
|
||||
name: 'add', |
|
||||
icon: 'add', |
|
||||
labelI18nKey: 'action.addNew', |
|
||||
label: t('action.addNew'), |
|
||||
click: () => { |
|
||||
openEditor(t('action.addNew'), formStatus.add, undefined); |
|
||||
}, |
|
||||
afterEditorOpen: () => { |
|
||||
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined }); |
|
||||
}, |
|
||||
}, |
|
||||
edit: { |
|
||||
name: 'edit', |
|
||||
icon: 'edit', |
|
||||
labelI18nKey: 'action.edit', |
|
||||
label: t('action.edit'), |
|
||||
enableIf: (args) => { |
|
||||
if (args.selected) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: (args) => { |
|
||||
edit(args.selected); |
|
||||
}, |
|
||||
afterEditorOpen: (args) => { |
|
||||
props.grid.emit('afterEditorOpen', { grid: props.grid, data: args.selected }); |
|
||||
}, |
|
||||
}, |
|
||||
inlineCellEdit: { |
|
||||
name: 'inlineCellEdit', |
|
||||
icon: 'border_color', |
|
||||
labelI18nKey: 'action.edit', |
|
||||
label: t('action.edit'), |
|
||||
enableIf: (args) => { |
|
||||
if (Object.keys(table.cellSelected).length > 0 && table.cellSelected['colName']) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: () => { |
|
||||
if (props.grid.props.selectMode !== selectMode.cell) { |
|
||||
console.warn('[w-grid]`selectMode` property is not `cell`, Cannot use cell editing function.'); |
|
||||
return false; |
|
||||
} else if (Object.keys(table.cellSelected).length === 0 || !table.cellSelected['colName']) { |
|
||||
NotifyManager.info('请选择要编辑的单元格'); |
|
||||
return false; |
|
||||
} else if (table.columns.findIndex((item) => item['name'] === table.cellSelected['colName'] && item['type']) < 0) { |
|
||||
console.warn('[w-grid]The column selected is not configured with a component type for editing.'); |
|
||||
return false; |
|
||||
} |
|
||||
table.bodyEditStatus = editStatus.cell; |
|
||||
}, |
|
||||
}, |
|
||||
cellEdit: { |
|
||||
name: 'cellEdit', |
|
||||
icon: 'border_color', |
|
||||
labelI18nKey: 'action.edit', |
|
||||
label: t('action.edit'), |
|
||||
enableIf: (args) => { |
|
||||
if (Object.keys(table.cellSelected).length > 0 && table.cellSelected['colName']) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: () => { |
|
||||
if (props.grid.props.selectMode !== selectMode.cell) { |
|
||||
console.warn('[w-grid]`selectMode` property is not `cell`, Cannot use cell editing function.'); |
|
||||
return false; |
|
||||
} else if (Object.keys(table.cellSelected).length === 0 || !table.cellSelected['colName']) { |
|
||||
NotifyManager.info('请选择要编辑的单元格'); |
|
||||
return false; |
|
||||
} else if (table.columns.findIndex((item) => item['name'] === table.cellSelected['colName'] && item['type']) < 0) { |
|
||||
console.warn('[w-grid]The column selected is not configured with a component type for editing.'); |
|
||||
return false; |
|
||||
} |
|
||||
// 弹出模态框编辑,但是只编辑该列的值 |
|
||||
props.grid.getCellEditorDialog().show(); |
|
||||
}, |
|
||||
}, |
|
||||
inlineRowEdit: { |
|
||||
name: 'inlineRowEdit', |
|
||||
icon: 'edit_note', |
|
||||
labelI18nKey: 'action.edit', |
|
||||
label: t('action.edit'), |
|
||||
enableIf: (args) => { |
|
||||
if (args.selected) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: () => { |
|
||||
if (table.columns.findIndex((item) => item['type']) < 0) { |
|
||||
console.warn('[w-grid]Not configured with a component type for editing.'); |
|
||||
return false; |
|
||||
} |
|
||||
table.bodyEditStatus = editStatus.row; |
|
||||
}, |
|
||||
}, |
|
||||
inlineRowsEdit: { |
|
||||
name: 'inlineRowsEdit', |
|
||||
icon: 'app_registration', |
|
||||
labelI18nKey: 'action.edit', |
|
||||
label: t('action.edit'), |
|
||||
click: () => { |
|
||||
if (table.columns.findIndex((item) => item['type']) < 0) { |
|
||||
console.warn('[w-grid]Not configured with a component type for editing.'); |
|
||||
return false; |
|
||||
} |
|
||||
table.bodyEditStatus = editStatus.rows; |
|
||||
// 清空勾选与选中 |
|
||||
props.grid.clearSelected(); |
|
||||
props.grid.clearTicked(); |
|
||||
table.allTicked = false; |
|
||||
}, |
|
||||
}, |
|
||||
clone: { |
|
||||
name: 'clone', |
|
||||
icon: 'content_copy', |
|
||||
labelI18nKey: 'action.copy', |
|
||||
label: t('action.copy'), |
|
||||
enableIf: (args) => { |
|
||||
if (args.selected) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: () => { |
|
||||
clone(props.grid.getSelectedRow()); |
|
||||
}, |
|
||||
afterEditorOpen: (args) => { |
|
||||
props.grid.emit('afterEditorOpen', { grid: props.grid, data: args.selected }); |
|
||||
}, |
|
||||
}, |
|
||||
remove: { |
|
||||
name: 'remove', |
|
||||
icon: 'delete', |
|
||||
labelI18nKey: 'action.remove', |
|
||||
label: t('action.remove'), |
|
||||
enableIf: (args) => { |
|
||||
if (args.ticked) { |
|
||||
return true; |
|
||||
} else if (args.selected) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: (tips: boolean = true) => { |
|
||||
if (!tips) { |
|
||||
remove(); |
|
||||
} else { |
|
||||
$q.dialog({ |
|
||||
title: t('confirm'), |
|
||||
message: t('action.remove.tip'), |
|
||||
cancel: { noCaps: true }, |
|
||||
ok: { noCaps: true }, |
|
||||
persistent: true, |
|
||||
}).onOk(() => { |
|
||||
remove(); |
|
||||
}); |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
view: { |
|
||||
name: 'view', |
|
||||
icon: 'visibility', |
|
||||
labelI18nKey: 'action.view', |
|
||||
label: t('action.view'), |
|
||||
enableIf: (args) => { |
|
||||
if (args.selected) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: () => { |
|
||||
props.grid.view(); |
|
||||
}, |
|
||||
}, |
|
||||
export: { |
|
||||
name: 'export', |
|
||||
icon: 'file_download', |
|
||||
labelI18nKey: 'action.export', |
|
||||
label: t('action.export'), |
|
||||
click: async () => { |
|
||||
showLoading(); |
|
||||
let exportData = props.grid.getRows(); |
|
||||
// 判断是否配置了 url, 以不分页形式请求后端获取全部数据一把导出。 |
|
||||
if (!Tools.isEmpty(props.grid.props.fetchDataUrl) || !Tools.isEmpty(props.grid.props.dataUrl)) { |
|
||||
const fetchResult = await getExportData(); |
|
||||
if (fetchResult && fetchResult.length > 0) { |
|
||||
exportData = fetchResult; |
|
||||
} |
|
||||
} |
|
||||
const content = [props.columns.map((col) => wrapCsvValue(col.label))] |
|
||||
.concat( |
|
||||
exportData.map((row) => |
|
||||
props.columns |
|
||||
.map((col) => wrapCsvValue(typeof col.field === 'function' ? col.field(row) : row[col.field === void 0 ? col.name : col.field], col.format, row)) |
|
||||
.join(','), |
|
||||
), |
|
||||
) |
|
||||
.join('\r\n'); |
|
||||
|
|
||||
const status = exportFile('table-export.csv', content, { |
|
||||
encoding: 'utf-8', |
|
||||
mimeType: 'text/csv', |
|
||||
byteOrderMark: '\uFEFF', // 解决乱码问题 |
|
||||
}); |
|
||||
|
|
||||
if (status !== true) { |
|
||||
NotifyManager.error(t('action.export.failed')); |
|
||||
} |
|
||||
hideLoading(); |
|
||||
}, |
|
||||
}, |
|
||||
addTop: { |
|
||||
name: 'addTop', |
|
||||
icon: 'add', |
|
||||
labelI18nKey: 'action.addTop', |
|
||||
label: t('action.addTop'), |
|
||||
click: () => { |
|
||||
openEditor(t('action.addTop'), formStatus.addTop, undefined); |
|
||||
}, |
|
||||
afterEditorOpen: () => { |
|
||||
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined }); |
|
||||
}, |
|
||||
}, |
|
||||
addChild: { |
|
||||
name: 'addChild', |
|
||||
icon: 'playlist_add', |
|
||||
labelI18nKey: 'action.addChild', |
|
||||
label: t('action.addChild'), |
|
||||
enableIf: (args) => { |
|
||||
if (args.selected) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: () => { |
|
||||
openEditor(t('action.addChild'), formStatus.addChild, undefined); |
|
||||
}, |
|
||||
afterEditorOpen: () => { |
|
||||
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined }); |
|
||||
}, |
|
||||
}, |
|
||||
expand: { |
|
||||
name: 'expand', |
|
||||
icon: () => { |
|
||||
return table.treeExpand ? 'expand_less' : 'expand_more'; |
|
||||
}, |
|
||||
label: () => { |
|
||||
return table.treeExpand ? t('action.collapseAll') : t('action.expandAll'); |
|
||||
}, |
|
||||
click: () => { |
|
||||
if (table.treeExpand) { |
|
||||
props.grid.collapse(); |
|
||||
} else { |
|
||||
props.grid.expand(); |
|
||||
} |
|
||||
table.treeExpand = !table.treeExpand; |
|
||||
}, |
|
||||
}, |
|
||||
resetDefaultValues: { |
|
||||
name: 'resetDefaultValues', |
|
||||
icon: 'bi-copy', |
|
||||
labelI18nKey: 'action.resetDefaultValues', |
|
||||
label: t('action.resetDefaultValues'), |
|
||||
click: (tips: boolean = true) => { |
|
||||
if (!tips) { |
|
||||
resetDefaultValues(); |
|
||||
} else { |
|
||||
$q.dialog({ |
|
||||
title: t('confirm'), |
|
||||
message: t('action.resetDefaultValues.tip'), |
|
||||
cancel: { noCaps: true }, |
|
||||
ok: { noCaps: true }, |
|
||||
persistent: true, |
|
||||
}).onOk(() => { |
|
||||
resetDefaultValues(); |
|
||||
}); |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
// 处理toobar |
|
||||
const handleChildrenBtn = (arr, moreQueryShow) => { |
|
||||
const tempArr = <any>[]; |
|
||||
for (let i = 0; i < arr.length; i++) { |
|
||||
const btn = arr[i]; |
|
||||
if (typeof btn === 'string' && !buttonObj[btn]) { |
|
||||
throw new Error('[w-grid]`' + btn + '`' + ' toolbar action NOT exist.'); |
|
||||
} else if (Array.isArray(btn) && btn.length > 0) { |
|
||||
const handleResult = handleChildrenBtn(btn, moreQueryShow); |
|
||||
if (handleResult && handleResult.length > 0) { |
|
||||
tempArr.push(handleResult); |
|
||||
} |
|
||||
} else if (typeof btn === 'string' && buttonObj[btn]) { |
|
||||
if (btn === buttonObj.query.name && i === 0) { |
|
||||
// 查询按钮为第一个时,追加更多查询时直接追加到查询按钮当前所在下标的后面一个,不能追加为数组,否则按钮组取第一个为 QBtnDropdown 左边按钮时取到的是一个数组,会报错。 |
|
||||
// ['query', 'reset'] |
|
||||
tempArr.push(buttonObj[btn]); |
|
||||
tempArr.push(buttonObj[buttonObj.moreQuery.name]); |
|
||||
} else if (btn === buttonObj.query.name) { |
|
||||
tempArr.push([buttonObj[btn], buttonObj[buttonObj.moreQuery.name]]); |
|
||||
} else { |
|
||||
tempArr.push(buttonObj[btn]); |
|
||||
} |
|
||||
} else if (typeof btn === 'object' && btn.extend && buttonObj[btn.extend]) { |
|
||||
let overrideClick = false; |
|
||||
if (btn.click) { |
|
||||
overrideClick = true; |
|
||||
} |
|
||||
tempArr.push({ ...buttonObj[btn.extend], ...btn, _click: buttonObj[btn.extend].click, overrideClick }); |
|
||||
} else { |
|
||||
tempArr.push(btn); |
|
||||
} |
|
||||
} |
|
||||
return tempArr; |
|
||||
}; |
|
||||
const handleToolbarActions = () => { |
|
||||
buttons_.value.splice(0, buttons_.value.length); |
|
||||
// 处理查询按钮,符合条件时给其追加更多查询按钮 |
|
||||
let moreQueryShow = false; |
|
||||
const rowColsNum = queryFormColsNumComputed.value * (props.grid.props.queryFormRowNum || 1); |
|
||||
let currRowColsNum = 0; |
|
||||
let showQueryFormFieldNum = 0; |
|
||||
props.grid.props.queryFormFields.forEach((item: any) => { |
|
||||
if (Tools.hasOwnProperty(item, 'colSpan')) { |
|
||||
currRowColsNum += item.colSpan; |
|
||||
} else { |
|
||||
currRowColsNum += 1; |
|
||||
} |
|
||||
if (currRowColsNum <= rowColsNum) { |
|
||||
showQueryFormFieldNum += 1; |
|
||||
} |
|
||||
}); |
|
||||
if (showQueryFormFieldNum < props.grid.props.queryFormFields.length) { |
|
||||
moreQueryShow = true; |
|
||||
} |
|
||||
props.grid.props.toolbarActions.forEach((btn: any, index) => { |
|
||||
if (typeof btn === 'string' && !buttonObj[btn]) { |
|
||||
throw new Error('[w-grid]`' + btn + '`' + ' toolbar action NOT exist.'); |
|
||||
} else if (typeof btn === 'string' && buttonObj[btn]) { |
|
||||
if (btn === buttonObj.query.name && moreQueryShow) { |
|
||||
buttons_.value.push([buttonObj[btn], buttonObj[buttonObj.moreQuery.name]]); |
|
||||
} else { |
|
||||
buttons_.value.push(buttonObj[btn]); |
|
||||
} |
|
||||
} else if (Array.isArray(btn) && btn.length > 0) { |
|
||||
buttons_.value.push(handleChildrenBtn(btn, moreQueryShow)); |
|
||||
} else if (typeof btn === 'object' && btn.extend && buttonObj[btn.extend]) { |
|
||||
// 继承内置按钮 |
|
||||
let overrideClick = false; |
|
||||
if (btn.click) { |
|
||||
overrideClick = true; |
|
||||
} |
|
||||
buttons_.value.push({ ...buttonObj[btn.extend], ...btn, _click: buttonObj[btn.extend].click, overrideClick: overrideClick }); |
|
||||
} else { |
|
||||
buttons_.value.push(btn); |
|
||||
} |
|
||||
}); |
|
||||
if (buttons_.value.length > 0 && buttons_.value[buttons_.value.length - 1] !== 'separator' && props.grid.props.configButton) { |
|
||||
buttons_.value.push(buttonObj.separator); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const setLocaleFlag = () => { |
|
||||
localeFlag.value = !localeFlag.value; |
|
||||
}; |
|
||||
const resetLabel = () => { |
|
||||
Object.keys(buttonObj).forEach((btn) => { |
|
||||
if (typeof buttonObj[btn] === 'object' && buttonObj[btn].labelI18nKey) { |
|
||||
buttonObj[btn].label = t(buttonObj[btn].labelI18nKey); |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const buttonClick = (arr) => { |
|
||||
arr.forEach((button) => { |
|
||||
if (typeof button === 'object' && !Tools.isEmpty(button.click) && props.grid.props.dbClickOperation === button.name) { |
|
||||
toolbarRef.value.buttonClick(button); |
|
||||
} else if (Array.isArray(button) && button.length > 0) { |
|
||||
buttonClick(button); |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
const dbClickOperation = (row) => { |
|
||||
if (!Tools.isEmpty(row) && props.grid.props.dbClickOperation === buttonObj.expand.name) { |
|
||||
row['expand'] = Tools.isEmpty(row['expand']) ? true : !row['expand']; |
|
||||
} else { |
|
||||
if (!buttonObj[props.grid.props.dbClickOperation] && checkConfigNotContains(buttons_.value)) { |
|
||||
throw new Error('[w-grid]`' + props.grid.props.dbClickOperation + '`' + ' toolbar action NOT exist.'); |
|
||||
} else if ( |
|
||||
props.grid.props.dbClickOperation !== buttonObj.separator && |
|
||||
buttonObj[props.grid.props.dbClickOperation] && |
|
||||
checkConfigNotContains(buttons_.value) |
|
||||
) { |
|
||||
// 双击时配置的dbClickOperation不在用户配置的按钮集中,直接调用内置按钮 |
|
||||
toolbarRef.value.buttonClick(buttonObj[props.grid.props.dbClickOperation]); |
|
||||
} else { |
|
||||
buttonClick(buttons_.value); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
// 检查用户配置的按钮中是否不包含固定名字的按钮 |
|
||||
const checkConfigNotContains = (arr) => { |
|
||||
let flag = true; |
|
||||
for (let i = 0; i < arr.length; i++) { |
|
||||
if (typeof arr[i] === 'object' && props.grid.props.dbClickOperation === arr[i]['name']) { |
|
||||
flag = false; |
|
||||
break; |
|
||||
} else if (Array.isArray(arr[i]) && arr[i].length > 0) { |
|
||||
const tempFlag = checkConfigNotContains(arr[i]); |
|
||||
flag = tempFlag; |
|
||||
} |
|
||||
} |
|
||||
return flag; |
|
||||
}; |
|
||||
const getQueryForm = () => { |
|
||||
return formRef.value; |
|
||||
}; |
|
||||
|
|
||||
onBeforeMount(() => { |
|
||||
handleToolbarActions(); |
|
||||
}); |
|
||||
|
|
||||
defineExpose({ |
|
||||
setLocaleFlag, |
|
||||
handlerQueryFormShowField, |
|
||||
handleToolbarActions, |
|
||||
resetLabel, |
|
||||
dbClickOperation, |
|
||||
getQueryForm, |
|
||||
reset: buttonObj.reset.click, |
|
||||
add: buttonObj.add.click, |
|
||||
edit, |
|
||||
cellEdit: buttonObj.cellEdit.click, |
|
||||
inlineCellEdit: buttonObj.inlineCellEdit.click, |
|
||||
inlineRowEdit: buttonObj.inlineRowEdit.click, |
|
||||
inlineRowsEdit: buttonObj.inlineRowsEdit.click, |
|
||||
clone: buttonObj.clone.click, |
|
||||
remove: buttonObj.remove.click, |
|
||||
view: buttonObj.view.click, |
|
||||
export: buttonObj.export.click, |
|
||||
addTop: buttonObj.addTop.click, |
|
||||
addChild: buttonObj.addChild.click, |
|
||||
expand: buttonObj.expand.click, |
|
||||
resetDefaultValues: buttonObj.resetDefaultValues.click, |
|
||||
}); |
|
||||
</script> |
|
||||
|
|
||||
<style lang="css"></style> |
|
@ -1,100 +0,0 @@ |
|||||
<template> |
|
||||
<w-drawer ref="drawerRef" :title="$t('action.view')" v-bind="props.grid.props.viewer.drawer"> |
|
||||
<div class="p-2.5"> |
|
||||
<w-info-panel ref="infoRef" v-bind="props.grid.props.viewer.panel" :info="infoArray"></w-info-panel> |
|
||||
</div> |
|
||||
</w-drawer> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { t, Tools, NotifyManager } from '@/platform'; |
|
||||
|
|
||||
const drawerRef = ref(); |
|
||||
const infoRef = ref(); |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
grid: { |
|
||||
// 表格实例 |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
tableColumnsMap: { |
|
||||
type: Map, |
|
||||
default: () => { |
|
||||
return new Map(); |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
const infoArray = ref(<any>[]); |
|
||||
|
|
||||
const view = () => { |
|
||||
const selected = props.grid.getSelectedRow(); |
|
||||
if (!selected) { |
|
||||
NotifyManager.warn(t('action.view.tip')); |
|
||||
} else { |
|
||||
infoArray.value = []; |
|
||||
if (props.grid.props.viewer.panel.fields && props.grid.props.viewer.panel.fields.length > 0) { |
|
||||
for (let item of props.grid.props.viewer.panel.fields) { |
|
||||
if (item.format) { |
|
||||
let value = selected[item.name]; |
|
||||
try { |
|
||||
value = item.format(selected[item.name], selected[0]); |
|
||||
} catch (error) { |
|
||||
console.error('format error!'); |
|
||||
} |
|
||||
infoArray.value.push({ label: item.label, value: value, originalValue: selected[item.name] }); |
|
||||
} else { |
|
||||
let value = null; |
|
||||
if (props.tableColumnsMap.get(item.name) && !Tools.isEmpty(props.tableColumnsMap.get(item.name).format)) { |
|
||||
value = selected[item.name]; |
|
||||
try { |
|
||||
value = props.tableColumnsMap.get(item.name).format(selected[item.name], selected); |
|
||||
} catch (error) { |
|
||||
console.error('format error!'); |
|
||||
} |
|
||||
} else { |
|
||||
value = selected[item.name]; |
|
||||
} |
|
||||
infoArray.value.push({ label: item.label, value: value, originalValue: selected[item.name] }); |
|
||||
} |
|
||||
} |
|
||||
} else { |
|
||||
for (let item of props.tableColumnsMap) { |
|
||||
if (item[1].format) { |
|
||||
let value = selected[item[0]]; |
|
||||
try { |
|
||||
value = item[1].format(selected[item[0]], selected); |
|
||||
} catch (error) { |
|
||||
console.error('format error!'); |
|
||||
} |
|
||||
infoArray.value.push({ label: item[1].label, value: value, originalValue: selected[item.name] }); |
|
||||
} else { |
|
||||
infoArray.value.push({ |
|
||||
label: item[1].label, |
|
||||
value: selected[item[0]], |
|
||||
originalValue: selected[item.name], |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
drawerRef.value.show(); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const getViewerDrawer = () => { |
|
||||
return drawerRef.value; |
|
||||
}; |
|
||||
const getInfoPanel = () => { |
|
||||
return infoRef.value; |
|
||||
}; |
|
||||
|
|
||||
defineExpose({ |
|
||||
getViewerDrawer, |
|
||||
getInfoPanel, |
|
||||
view, |
|
||||
}); |
|
||||
</script> |
|
||||
<style lang="css"></style> |
|
@ -0,0 +1,41 @@ |
|||||
|
<template> |
||||
|
<template v-if="tools.props.pageable && !tools.props.tree"> |
||||
|
<q-pagination |
||||
|
v-model="page" |
||||
|
:boundary-links="tools.table.store.pagination.config.boundaryLinks" |
||||
|
:boundary-numbers="tools.table.store.pagination.config.boundaryNumbers" |
||||
|
:direction-links="tools.table.store.pagination.config.directionLinks" |
||||
|
:ellipses="tools.table.store.pagination.config.ellipses" |
||||
|
:max-pages="tools.table.store.location.middleWidth > 600 ? tools.table.store.pagination.config.maxPages : 3" |
||||
|
:min="1" |
||||
|
:max="props.scope.pagesNumber" |
||||
|
:size="tools.cm.denseBottom.value ? '10px' : ''" |
||||
|
@update:model-value="pageChange" |
||||
|
/> |
||||
|
<span>{{ $t('tip.pagenation.totalRecord', { count: tools.table.store.pagination.rowsNumber }) }}</span> |
||||
|
</template> |
||||
|
<template v-else> {{ $t('tip.pagenation.totalRecord', { count: tools.table.store.pagination.rowsNumber }) }} </template> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { $t } from '@/platform'; |
||||
|
import type { GridTools } from './ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const page = defineModel({ type: Number, default: 1 }); |
||||
|
const props = defineProps({ |
||||
|
scope: { |
||||
|
// 顶部插槽属性 |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const pageChange = (value) => { |
||||
|
tools.reqApiFM.fetchData({ pagination: tools.table.store.pagination }); |
||||
|
page.value = value; |
||||
|
}; |
||||
|
</script> |
||||
|
<style lang="css"></style> |
@ -0,0 +1,200 @@ |
|||||
|
<template> |
||||
|
<template v-if="generateTdIfComputed"> |
||||
|
<q-td |
||||
|
ref="tdRef" |
||||
|
:key="props.col.name" |
||||
|
:props="props.scope" |
||||
|
:class="tdClassComputed" |
||||
|
:style="tdStyleComputed" |
||||
|
:title="titleComputed" |
||||
|
:rowspan="rowSpanComputed" |
||||
|
@click="tdClick" |
||||
|
> |
||||
|
<!-- 前端排序号 --> |
||||
|
<template v-if="props.col.name === Constant.FIELD_NAMES.SORT_NO"> |
||||
|
{{ props.scope.rowIndex + 1 }} |
||||
|
</template> |
||||
|
|
||||
|
<!-- 树表格第一列 --> |
||||
|
<template v-else-if="isTreeGridAndFirstTdComputed"> |
||||
|
<TreeGridFirstTdContent |
||||
|
ref="treeGridFirstTdRef" |
||||
|
:value="props.col.value" |
||||
|
:row="props.scope.row" |
||||
|
:col="props.col" |
||||
|
:level="props.level" |
||||
|
></TreeGridFirstTdContent> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 内联编辑模式组件 --> |
||||
|
<template v-else-if="showInlineEditComponentComputed"> |
||||
|
<InlineEditComponent :col="props.col" :row="props.scope.row"></InlineEditComponent> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 普通单元格内容 --> |
||||
|
<TdContent v-else :value="props.col.value"></TdContent> |
||||
|
</q-td> |
||||
|
</template> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { computed, inject, onMounted, ref, toRaw } from 'vue'; |
||||
|
import { Tools } from '@/platform'; |
||||
|
import TdContent from './TdContent.vue'; |
||||
|
import TreeGridFirstTdContent from './TreeGridFirstTdContent.vue'; |
||||
|
import InlineEditComponent from './extra/inline-edit/InlineEditComponent.vue'; |
||||
|
import { Constant, GridTools } from './ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const tdRef = ref(); |
||||
|
const treeGridFirstTdRef = ref(); |
||||
|
const tdWidth = ref(0); |
||||
|
const props = defineProps({ |
||||
|
level: { type: Number, default: 0 }, |
||||
|
col: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
scope: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
const rowKey = Constant.FIELD_NAMES.ROW_KEY; |
||||
|
|
||||
|
const titleComputed = computed(() => { |
||||
|
if (tools.table.columns.length > 0) { |
||||
|
const column = tools.table.columns.find((item) => item['name'] === props.col.name); |
||||
|
if (column && column['title'] && typeof column['title'] === 'function') { |
||||
|
return column.title({ grid: tools.instance, row: toRaw(props.scope.row), value: props.scope.row[props.col.name] }); |
||||
|
} else if (column && column['title']) { |
||||
|
return column['title']; |
||||
|
} |
||||
|
} |
||||
|
if (props.col.classes?.indexOf('truncate') > -1 && !Tools.isEmpty(props.col.value) && typeof props.col.value !== 'object') { |
||||
|
return props.col.value; |
||||
|
} |
||||
|
return ''; |
||||
|
}); |
||||
|
const tdClassComputed = computed(() => { |
||||
|
const tdClass = <any>[]; |
||||
|
if (props.col.name === Constant.FIELD_NAMES.SORT_NO) { |
||||
|
tdClass.push('sortNo_td'); |
||||
|
} |
||||
|
if (tools.props.selectMode === Constant.SELECT_MODE.CELL) { |
||||
|
tdClass.push('cellHover'); |
||||
|
} |
||||
|
if ( |
||||
|
tools.table.store.cellSelected && |
||||
|
tools.table.store.cellSelected.colName === props.col['name'] && |
||||
|
tools.table.store.cellSelected.rowKey === props.scope.row[rowKey] |
||||
|
) { |
||||
|
tdClass.push('cellSelected'); |
||||
|
} |
||||
|
return tdClass; |
||||
|
}); |
||||
|
const tdStyleComputed = computed(() => { |
||||
|
let style = {}; |
||||
|
if (tools.editFM.getEditorFieldByName(props.col['name']) && tools.table.store.inlineEditStatus !== Constant.EDIT_STATUS.NONE) { |
||||
|
style['width'] = tdWidth.value - 10 + 'px'; |
||||
|
} |
||||
|
return style; |
||||
|
}); |
||||
|
const isTreeGridAndFirstTdComputed = computed(() => { |
||||
|
return tools.props.tree && props.col.name === props.scope.cols[0].name; |
||||
|
}); |
||||
|
const showInlineEditComponentComputed = computed(() => { |
||||
|
return tools.table.store.inlineEditStatus !== Constant.EDIT_STATUS.NONE && tools.editFM.isShowInlineEditor(props.col, props.scope.row[rowKey]); |
||||
|
}); |
||||
|
const rowSpanComputed = computed(() => { |
||||
|
if (Object.keys(tools.table.store.mergeGroupRecords).length > 0) { |
||||
|
const column = tools.table.store.mergeGroupRecords[props.col['name']]; |
||||
|
if (column && column[props.scope['row'][props.col['name']]].length > 1) { |
||||
|
return column[props.scope['row'][props.col['name']]].length; |
||||
|
} |
||||
|
} |
||||
|
return 1; |
||||
|
}); |
||||
|
const generateTdIfComputed = computed(() => { |
||||
|
if (Object.keys(tools.table.store.mergeGroupRecords).length > 0) { |
||||
|
const colName = props.col['name']; |
||||
|
const column = tools.table.store.mergeGroupRecords[colName]; |
||||
|
if (colName === Constant.FIELD_NAMES.SORT_NO) { |
||||
|
return true; |
||||
|
} else if (column && column[props.scope['row'][colName]].length > 1) { |
||||
|
return props.scope['row'][rowKey] === column[props.scope['row'][colName]][0]; |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
}); |
||||
|
|
||||
|
const mergeGroupByFieldContainsIf = (fieldName: string) => { |
||||
|
const mergeGroupByField = tools.cm.mergeGroupByField.value; |
||||
|
if (Array.isArray(mergeGroupByField) && mergeGroupByField.length > 0) { |
||||
|
const index = mergeGroupByField.findIndex((item) => item === fieldName); |
||||
|
if (index > -1) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
}; |
||||
|
const sortByContainsIf = (fieldName: string) => { |
||||
|
if (tools.props.sortBy && tools.props.sortBy.length > 0) { |
||||
|
const findResult = tools.props.sortBy.findIndex((item) => { |
||||
|
if (item.startsWith('-') && item.substring(1) === fieldName) { |
||||
|
return true; |
||||
|
} |
||||
|
return item === fieldName; |
||||
|
}); |
||||
|
return findResult > -1; |
||||
|
} |
||||
|
return false; |
||||
|
}; |
||||
|
|
||||
|
const tdClick = () => { |
||||
|
// 判定是否要退出编辑模式 |
||||
|
const cellSelected = tools.table.store.cellSelected; |
||||
|
if (cellSelected) { |
||||
|
if (tools.props.localMode && tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.CELL && tools.props.selectMode === Constant.SELECT_MODE.CELL) { |
||||
|
if (cellSelected.colName !== props.col['name'] || cellSelected.row[rowKey] !== props.scope.row[rowKey]) { |
||||
|
tools.editFM.exitInlineEdit(); |
||||
|
} |
||||
|
} else if ( |
||||
|
tools.props.localMode && |
||||
|
tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROW && |
||||
|
tools.props.selectMode === Constant.SELECT_MODE.CELL |
||||
|
) { |
||||
|
if (cellSelected.row[rowKey] !== props.scope.row[rowKey]) { |
||||
|
tools.editFM.exitInlineEdit(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.NONE && tools.props.selectMode === Constant.SELECT_MODE.CELL) { |
||||
|
tools.table.store.cellSelected = { |
||||
|
row: toRaw(props.scope.row), |
||||
|
rowKey: props.scope.row[rowKey], |
||||
|
primaryKey: props.scope.row[tools.props.primaryKey], |
||||
|
colName: props.col['name'], |
||||
|
value: props.scope.row[props.col['name']], |
||||
|
}; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
onMounted(() => { |
||||
|
if (tdRef.value?.$el) { |
||||
|
tdWidth.value = tdRef.value.$el.clientWidth; |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="css"> |
||||
|
.sortNo_td { |
||||
|
padding: 0; |
||||
|
width: 50px; |
||||
|
min-width: 50px; |
||||
|
max-width: 50px; |
||||
|
} |
||||
|
</style> |
@ -1,5 +1,5 @@ |
|||||
<template> |
<template> |
||||
<template v-if="typeof value === 'object' && value.componentType"> |
<template v-if="value && typeof value === 'object' && value.componentType"> |
||||
<component :is="value.componentType" v-bind="value.attrs"></component> |
<component :is="value.componentType" v-bind="value.attrs"></component> |
||||
</template> |
</template> |
||||
<template v-else> |
<template v-else> |
@ -0,0 +1,287 @@ |
|||||
|
<template> |
||||
|
<div class="col"> |
||||
|
<template v-if="tools.table.advancedQueryStatus"> |
||||
|
<!-- 高级查询 --> |
||||
|
<AdvancedQuery ref="formRef"></AdvancedQuery> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<!-- 普通查询form --> |
||||
|
<w-form ref="formRef" v-bind="tools.props.queryFormAttrs" :fields="queryFormFieldsComputed" :cols-num="tools.props.queryFormColsNum"></w-form> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 标题与按钮 --> |
||||
|
<div |
||||
|
v-if="tools.props.title || buttons_.length > 0 || tools.props.configButton || tools.table.queryFormDisplayFields.length > 0" |
||||
|
class="flex flex-nowrap items-end" |
||||
|
:class="tools.table.queryFormDisplayFields.length > 0 ? 'pt-2.5' : ''" |
||||
|
> |
||||
|
<!-- 标题 --> |
||||
|
<div class="flex-none">{{ $t(tools.props.title ? tools.props.title : '') }}</div> |
||||
|
|
||||
|
<!-- 按钮 --> |
||||
|
<div class="flex-1"> |
||||
|
<w-toolbar ref="toolbarRef" v-bind="tools.props.toolbarConfigure" v-model="buttons_" :dense="denseToolbarComputed" :grid="tools.instance"></w-toolbar> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 最右侧用户配置按钮 --> |
||||
|
<div v-if="tools.props.configButton" class="flex-none pl-1"> |
||||
|
<q-btn round dense :size="denseToolbarComputed ? '13px' : undefined" icon="manage_accounts" unelevated outline> |
||||
|
<q-popup-proxy v-model="tools.table.configStore.showConfigPanel"> |
||||
|
<ConfigPanel :scope="props.scope" :grid="tools.instance"></ConfigPanel> |
||||
|
</q-popup-proxy> |
||||
|
</q-btn> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { computed, inject, ref, onBeforeMount } from 'vue'; |
||||
|
import { useQuasar } from 'quasar'; |
||||
|
import { Tools, $t } from '@/platform'; |
||||
|
import ConfigPanel from './extra/config/ConfigPanel.vue'; |
||||
|
import AdvancedQuery from './extra/advanced-query/AdvancedQuery.vue'; |
||||
|
import { Constant, GridTools } from './ts/index'; |
||||
|
|
||||
|
const $q = useQuasar(); |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const formRef = ref(); |
||||
|
const toolbarRef = ref(); |
||||
|
const localeFlag = ref(false); |
||||
|
const buttons_ = ref(<any>[]); |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
scope: { |
||||
|
// 顶部插槽属性 |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
const buttons = tools.bm.getButtonConfig(); |
||||
|
|
||||
|
const queryFormFieldsComputed = computed(() => { |
||||
|
if (localeFlag.value) { |
||||
|
return tools.table.queryFormDisplayFields; |
||||
|
} |
||||
|
return tools.table.queryFormDisplayFields; |
||||
|
}); |
||||
|
const denseToolbarComputed = computed(() => { |
||||
|
if (tools.table.configStore.denseToolbar) { |
||||
|
return true; |
||||
|
} else if (tools.table.configStore.dense !== false) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const screenCols = { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 }; |
||||
|
const queryFormColsNumComputed = computed(() => { |
||||
|
if (typeof tools.props.queryFormColsNum === 'number' && tools.props.queryFormColsNum > 0) { |
||||
|
return tools.props.queryFormColsNum; |
||||
|
} else if (typeof tools.props.queryFormColsNum === 'object') { |
||||
|
const screen = { ...screenCols, ...tools.props.queryFormColsNum }; |
||||
|
return screen[$q.screen.name]; |
||||
|
} |
||||
|
return screenCols[$q.screen.name]; |
||||
|
}); |
||||
|
|
||||
|
// 处理查询form显示的字段 |
||||
|
const handleQueryFormShowField = () => { |
||||
|
tools.table.queryFormDisplayFields = []; |
||||
|
if (tools.table.moreQueryStatus) { |
||||
|
tools.props.queryFormFields.forEach((item: any) => { |
||||
|
tools.table.queryFormDisplayFields.push(item); |
||||
|
item.showIf = () => { |
||||
|
return true; |
||||
|
}; |
||||
|
}); |
||||
|
} else { |
||||
|
// 一行应该显示的字段个数 |
||||
|
const rowColsNum = queryFormColsNumComputed.value * (tools.props.queryFormRowNum || 1); |
||||
|
let currRowColsNum = 0; |
||||
|
tools.props.queryFormFields.forEach((item: any) => { |
||||
|
if (Tools.hasOwnProperty(item, 'colSpan')) { |
||||
|
currRowColsNum += item.colSpan; |
||||
|
} else { |
||||
|
currRowColsNum += 1; |
||||
|
} |
||||
|
if (currRowColsNum <= rowColsNum) { |
||||
|
tools.table.queryFormDisplayFields.push(item); |
||||
|
item.showIf = (form) => { |
||||
|
return true; |
||||
|
}; |
||||
|
} else { |
||||
|
item.showIf = (form) => { |
||||
|
return false; |
||||
|
}; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 处理toobar |
||||
|
const handleChildrenBtn = (arr, moreQueryShow) => { |
||||
|
const tempArr = <any>[]; |
||||
|
for (let i = 0; i < arr.length; i++) { |
||||
|
const btn = arr[i]; |
||||
|
if (typeof btn === 'string' && !buttons[btn]) { |
||||
|
throw new Error('[w-grid]`' + btn + '`' + ' toolbar action NOT exist.'); |
||||
|
} else if (Array.isArray(btn) && btn.length > 0) { |
||||
|
const handleResult = handleChildrenBtn(btn, moreQueryShow); |
||||
|
if (handleResult && handleResult.length > 0) { |
||||
|
tempArr.push(handleResult); |
||||
|
} |
||||
|
} else if (typeof btn === 'string' && buttons[btn]) { |
||||
|
if (btn === tools.bm.query.name && i === 0) { |
||||
|
// 查询按钮为第一个时,追加更多查询时直接追加到查询按钮当前所在下标的后面一个,不能追加为数组,否则按钮组取第一个为 QBtnDropdown 左边按钮时取到的是一个数组,会报错。 |
||||
|
// ['query', 'reset'] |
||||
|
tempArr.push(buttons[btn]); |
||||
|
tempArr.push(buttons[tools.bm.moreQuery.name]); |
||||
|
tempArr.push(buttons[tools.bm.advancedQuery.name]); |
||||
|
} else if (btn === tools.bm.query.name) { |
||||
|
tempArr.push([buttons[btn], buttons[tools.bm.moreQuery.name]]); |
||||
|
} else { |
||||
|
tempArr.push(buttons[btn]); |
||||
|
} |
||||
|
} else if (typeof btn === 'object' && btn.extend && buttons[btn.extend]) { |
||||
|
// 继承内置按钮 |
||||
|
let overrideClick = false; |
||||
|
if (btn.click) { |
||||
|
overrideClick = true; |
||||
|
} |
||||
|
tempArr.push({ ...buttons[btn.extend], ...btn, _click: buttons[btn.extend].click, overrideClick }); |
||||
|
} else { |
||||
|
tempArr.push(btn); |
||||
|
} |
||||
|
} |
||||
|
return tempArr; |
||||
|
}; |
||||
|
const handleToolbarActions = () => { |
||||
|
buttons_.value = []; |
||||
|
// 处理查询按钮,符合条件时给其追加更多查询按钮 |
||||
|
let moreQueryShow = false; |
||||
|
const rowColsNum = queryFormColsNumComputed.value * (tools.props.queryFormRowNum || 1); |
||||
|
let currRowColsNum = 0; |
||||
|
let showQueryFormFieldNum = 0; |
||||
|
tools.props.queryFormFields.forEach((item: any) => { |
||||
|
if (Tools.hasOwnProperty(item, 'colSpan')) { |
||||
|
currRowColsNum += item.colSpan; |
||||
|
} else { |
||||
|
currRowColsNum += 1; |
||||
|
} |
||||
|
if (currRowColsNum <= rowColsNum) { |
||||
|
showQueryFormFieldNum += 1; |
||||
|
} |
||||
|
}); |
||||
|
if (showQueryFormFieldNum < tools.props.queryFormFields.length) { |
||||
|
moreQueryShow = true; |
||||
|
} |
||||
|
tools.props.toolbarActions.forEach((btn: any, index) => { |
||||
|
if (typeof btn === 'string' && !buttons[btn]) { |
||||
|
throw new Error('[w-grid]`' + btn + '`' + ' toolbar action NOT exist.'); |
||||
|
} else if (typeof btn === 'string' && buttons[btn]) { |
||||
|
if (btn === tools.bm.query.name) { |
||||
|
const tmpBtns = [buttons[btn]]; |
||||
|
if (moreQueryShow) { |
||||
|
tmpBtns.push(buttons[tools.bm.moreQuery.name]); |
||||
|
} |
||||
|
if (tools.props.advancedQuery) { |
||||
|
tmpBtns.push(buttons[tools.bm.advancedQuery.name]); |
||||
|
} |
||||
|
if (tmpBtns.length === 1) { |
||||
|
buttons_.value.push(...tmpBtns); |
||||
|
} else { |
||||
|
buttons_.value.push(tmpBtns); |
||||
|
} |
||||
|
} else { |
||||
|
buttons_.value.push(buttons[btn]); |
||||
|
} |
||||
|
} else if (Array.isArray(btn) && btn.length > 0) { |
||||
|
buttons_.value.push(handleChildrenBtn(btn, moreQueryShow)); |
||||
|
} else if (typeof btn === 'object' && btn.extend && buttons[btn.extend]) { |
||||
|
// 继承内置按钮 |
||||
|
let overrideClick = false; |
||||
|
if (btn.click) { |
||||
|
overrideClick = true; |
||||
|
} |
||||
|
buttons_.value.push({ ...buttons[btn.extend], ...btn, _click: buttons[btn.extend].click, overrideClick }); |
||||
|
} else { |
||||
|
buttons_.value.push(btn); |
||||
|
} |
||||
|
}); |
||||
|
if (buttons_.value.length > 0 && buttons_.value[buttons_.value.length - 1] !== tools.bm.separator.name && tools.props.configButton) { |
||||
|
buttons_.value.push(buttons[tools.bm.separator.name]); |
||||
|
} |
||||
|
setLocaleFlag(); |
||||
|
}; |
||||
|
|
||||
|
const setLocaleFlag = () => { |
||||
|
localeFlag.value = !localeFlag.value; |
||||
|
}; |
||||
|
const resetLabel = () => { |
||||
|
Object.keys(buttons).forEach((btn) => { |
||||
|
if (typeof buttons[btn] === 'object' && buttons[btn].labelI18nKey) { |
||||
|
buttons[btn].label = $t(buttons[btn].labelI18nKey); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const buttonClick = (arr) => { |
||||
|
arr.forEach((button) => { |
||||
|
if (typeof button === 'object' && !Tools.isEmpty(button.click) && tools.props.dbClickOperation === button.name) { |
||||
|
toolbarRef.value.buttonClick(button); |
||||
|
} else if (Array.isArray(button) && button.length > 0) { |
||||
|
buttonClick(button); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
const dbClickOperation = (row) => { |
||||
|
if (!Tools.isEmpty(row) && tools.props.dbClickOperation === tools.bm.expand.name) { |
||||
|
row[Constant.FIELD_NAMES.EXPAND] = Tools.isEmpty(row[Constant.FIELD_NAMES.EXPAND]) ? true : !row[Constant.FIELD_NAMES.EXPAND]; |
||||
|
} else { |
||||
|
if (!buttons[tools.props.dbClickOperation] && checkConfigNotContains(buttons_.value)) { |
||||
|
throw new Error('[w-grid]`' + tools.props.dbClickOperation + '`' + ' toolbar action NOT exist.'); |
||||
|
} else if (tools.props.dbClickOperation !== tools.bm.separator.name && buttons[tools.props.dbClickOperation] && checkConfigNotContains(buttons_.value)) { |
||||
|
// 双击时配置的dbClickOperation不在用户配置的按钮集中,直接调用内置按钮 |
||||
|
toolbarRef.value.buttonClick(buttons[tools.props.dbClickOperation]); |
||||
|
} else { |
||||
|
buttonClick(buttons_.value); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
// 检查用户配置的按钮中是否不包含固定名字的按钮 |
||||
|
const checkConfigNotContains = (arr) => { |
||||
|
let flag = true; |
||||
|
for (let i = 0; i < arr.length; i++) { |
||||
|
if (typeof arr[i] === 'object' && tools.props.dbClickOperation === arr[i]['name']) { |
||||
|
flag = false; |
||||
|
break; |
||||
|
} else if (Array.isArray(arr[i]) && arr[i].length > 0) { |
||||
|
const tempFlag = checkConfigNotContains(arr[i]); |
||||
|
flag = tempFlag; |
||||
|
} |
||||
|
} |
||||
|
return flag; |
||||
|
}; |
||||
|
const getQueryForm = () => { |
||||
|
return formRef.value; |
||||
|
}; |
||||
|
|
||||
|
onBeforeMount(() => { |
||||
|
handleToolbarActions(); |
||||
|
}); |
||||
|
|
||||
|
defineExpose({ |
||||
|
setLocaleFlag, |
||||
|
handleQueryFormShowField, |
||||
|
handleToolbarActions, |
||||
|
resetLabel, |
||||
|
dbClickOperation, |
||||
|
getQueryForm, |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="css"></style> |
@ -0,0 +1,117 @@ |
|||||
|
<template> |
||||
|
<q-tr |
||||
|
ref="trRef" |
||||
|
:props="props.scope" |
||||
|
:no-hover="trNoHoverComputed" |
||||
|
:class="{ selected: trSelectedClassComputed }" |
||||
|
:draggable="draggableComputed" |
||||
|
@click.stop="tools.em.rowClick($event, props.scope.row, props.scope.rowIndex)" |
||||
|
@dblclick.stop="tools.em.rowDbClick($event, scope.row, scope.rowIndex)" |
||||
|
@dragstart="draggableComputed ? tools.dndFM.onDragStart($event, scope) : () => {}" |
||||
|
@dragleave="draggableComputed ? tools.dndFM.onDragLeave($event) : () => {}" |
||||
|
@dragover="draggableComputed ? tools.dndFM.onDragOver($event, scope, trMiddleHeightComputed) : () => {}" |
||||
|
@drop="draggableComputed ? tools.dndFM.onDrop($event, scope, trMiddleHeightComputed) : () => {}" |
||||
|
@dragend="draggableComputed ? tools.dndFM.onDragEnd($event, scope) : () => {}" |
||||
|
> |
||||
|
<q-td v-if="showCheckboxComputed" class="text-center checkbox_td"> |
||||
|
<!-- checkbo选项 --> |
||||
|
<q-checkbox |
||||
|
v-model="rowDataComputed[tickedField]" |
||||
|
flat |
||||
|
:dense="tools.table.configStore.dense || tools.table.configStore.denseBody || false" |
||||
|
@update:model-value="tools.em.updateTicked($event, scope.row)" |
||||
|
/> |
||||
|
</q-td> |
||||
|
<template v-for="(col, index) in props.scope.cols" :key="col.name"> |
||||
|
<template v-if="index === 0"> |
||||
|
<Td :scope="props.scope" :col="col" :level="props.level"></Td> |
||||
|
</template> |
||||
|
<Td v-else :scope="props.scope" :col="col" :level="props.level"></Td> |
||||
|
</template> |
||||
|
</q-tr> |
||||
|
<template v-if="showChildrenTrComputed"> |
||||
|
<!-- 处理树表格的孩子行 --> |
||||
|
<template v-for="(child, childIndex) in props.scope.row.children" :key="child[Constant.FIELD_NAMES.ROW_KEY]"> |
||||
|
<Tr |
||||
|
v-if="props.scope.row[Constant.FIELD_NAMES.EXPAND]" |
||||
|
:scope="GridTools.buildChildrenScope(props.scope, child, props.scope.pageIndex + childIndex, selectedField, tickedField)" |
||||
|
:level="props.level + 1" |
||||
|
></Tr> |
||||
|
</template> |
||||
|
</template> |
||||
|
<InlineEditToolbar :row="props.scope.row"></InlineEditToolbar> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { computed, inject, ref } from 'vue'; |
||||
|
import { Tools } from '@/platform'; |
||||
|
import { Constant, GridTools } from './ts/index'; |
||||
|
import Td from './Td.vue'; |
||||
|
import InlineEditToolbar from './extra/inline-edit/InlineEditToolbar.vue'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const trRef = ref(); |
||||
|
const props = defineProps({ |
||||
|
level: { type: Number, default: 0 }, |
||||
|
scope: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
const selectedField = tools.props.selectedField; |
||||
|
const tickedField = tools.props.tickedField; |
||||
|
const selectMode = tools.props.selectMode; |
||||
|
const dndMode = tools.props.dndMode; |
||||
|
|
||||
|
// 是否显示 checkbox 勾选框 |
||||
|
const showCheckboxComputed = computed(() => { |
||||
|
return tools.table.configStore.useCheckboxSelection && tools.props.selectMode !== Constant.SELECT_MODE.NONE && !tools.props.tree; |
||||
|
}); |
||||
|
|
||||
|
// 当前行数据 |
||||
|
const rowDataComputed = computed(() => { |
||||
|
return tools.dataFM.getRow(tools.table.rows, props.scope.row[Constant.FIELD_NAMES.ROW_KEY], false); |
||||
|
}); |
||||
|
|
||||
|
// 是否显示孩子行 |
||||
|
const showChildrenTrComputed = computed(() => { |
||||
|
return tools.props.tree && props.scope.row?.children; |
||||
|
}); |
||||
|
|
||||
|
// tr 是否禁用悬停效果 |
||||
|
const trNoHoverComputed = computed(() => { |
||||
|
if (selectMode === Constant.SELECT_MODE.ROW) { |
||||
|
return false; |
||||
|
} |
||||
|
return true; |
||||
|
}); |
||||
|
// tr 是否应用选中样式 |
||||
|
const trSelectedClassComputed = computed(() => { |
||||
|
return props.scope.row[selectedField] && selectMode === Constant.SELECT_MODE.ROW; |
||||
|
}); |
||||
|
|
||||
|
// 行拖拽是否启用 |
||||
|
const draggableComputed = computed(() => { |
||||
|
return dndMode && !Tools.isEmpty(Constant.DND_MODE.getAll()[dndMode]) && tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.NONE; |
||||
|
}); |
||||
|
|
||||
|
// 得到表格数据行的中间高度 |
||||
|
const trMiddleHeightComputed = computed(() => { |
||||
|
if (trRef?.value) { |
||||
|
return trRef.value.$el.offsetHeight / 2; |
||||
|
} else { |
||||
|
return (tools.table.configStore.dense || tools.table.configStore.denseBody ? 24 : 48) / 2; |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="css"> |
||||
|
.checkbox_td { |
||||
|
padding: 0 !important; |
||||
|
width: 50px; |
||||
|
min-width: 50px; |
||||
|
max-width: 50px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,269 @@ |
|||||
|
<template> |
||||
|
<div class="flex flex-nowrap items-center h-full w-full treeGridFirstTd" style="flex-wrap: nowrap"> |
||||
|
<!--层级占位符--> |
||||
|
<span :style="`width:${24 * props.level}px;`"></span> |
||||
|
<!--展开按钮--> |
||||
|
<q-btn |
||||
|
v-if="expandableComputed" |
||||
|
flat |
||||
|
dense |
||||
|
padding="0px 0px" |
||||
|
:icon="props.row.expand ? 'bi-dash' : 'bi-plus'" |
||||
|
:loading="lazyloading" |
||||
|
@click.stop="expandFun" |
||||
|
@dblclick.stop="() => {}" |
||||
|
> |
||||
|
<template #loading> |
||||
|
<q-spinner-gears /> |
||||
|
</template> |
||||
|
</q-btn> |
||||
|
<!--展开按钮占位符--> |
||||
|
<span v-else style="width: 24px"></span> |
||||
|
<!--选择框--> |
||||
|
<q-checkbox |
||||
|
v-if="tools.table.configStore.useCheckboxSelection" |
||||
|
v-model="rowDataComputed[tickedField]" |
||||
|
flat |
||||
|
dense |
||||
|
@dblclick.stop="() => {}" |
||||
|
@update:model-value="selectedFun(rowDataComputed[tickedField], $event)" |
||||
|
/> |
||||
|
<!-- 图标与显示内容区域 --> |
||||
|
<div class="treeGridIconAndName" @dragover="draggableComputed ? tools.dndFM.iconAndNameDragOver($event, props.row) : () => {}"> |
||||
|
<q-icon v-if="typeof iconComputed === 'string'" :name="iconComputed" size="20px" class="px-1 treeGridIconAndName"></q-icon> |
||||
|
<q-icon v-else-if="typeof iconComputed === 'object'" size="20px" v-bind="iconComputed" class="px-1 treeGridIconAndName"></q-icon> |
||||
|
<span v-else class="px-1 treeGridIconAndName"></span> |
||||
|
<template v-if="tools.table.store.inlineEditStatus !== Constant.EDIT_STATUS.NONE && showInlineEditComponentComputed"> |
||||
|
<InlineEditComponent :col="props.col" :row="props.row"></InlineEditComponent> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<TdContent :value="props.value" class="treeGridIconAndName"></TdContent> |
||||
|
</template> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { inject, computed, ref, watch } from 'vue'; |
||||
|
import { Tools, NotifyManager } from '@/platform'; |
||||
|
import InlineEditComponent from './extra/inline-edit/InlineEditComponent.vue'; |
||||
|
import TdContent from './TdContent.vue'; |
||||
|
import { DragAndDrop } from './ts/function/DragAndDrop'; |
||||
|
import { Constant, GridTools } from './ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const lazyloading = ref(false); |
||||
|
const props = defineProps({ |
||||
|
level: { type: Number, default: 0 }, |
||||
|
row: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
col: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
value: { |
||||
|
type: [Object, String, Number], |
||||
|
default: () => { |
||||
|
return ''; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
const selectedField = tools.props.selectedField; |
||||
|
const tickedField = tools.props.tickedField; |
||||
|
const rowKeyField = Constant.FIELD_NAMES.ROW_KEY; |
||||
|
const expandField = Constant.FIELD_NAMES.EXPAND; |
||||
|
const lazyloadNoChildrenField = Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN; |
||||
|
|
||||
|
const style = { |
||||
|
icon: { |
||||
|
defaultTop: { |
||||
|
// 默认顶级节点图标 |
||||
|
name: 'bi-folder-fill', |
||||
|
color: 'amber', |
||||
|
}, |
||||
|
defaultChild: { |
||||
|
// 默认子节点图标 |
||||
|
name: 'note', |
||||
|
color: '', |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
const expandableComputed = computed(() => { |
||||
|
if (tools.props.treeLazyLoad && !props.row[lazyloadNoChildrenField]) { |
||||
|
return true; |
||||
|
} else if (props.row.children && props.row.children.length > 0) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}); |
||||
|
|
||||
|
// 当前行数据 |
||||
|
const rowDataComputed = computed(() => { |
||||
|
return tools.dataFM.getRow(tools.table.rows, props.row[rowKeyField], false); |
||||
|
}); |
||||
|
|
||||
|
const draggableComputed = computed(() => { |
||||
|
return ( |
||||
|
tools.props.dndMode && !Tools.isEmpty(Constant.DND_MODE.getAll()[tools.props.dndMode]) && tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.NONE |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
const iconComputed = computed(() => { |
||||
|
if (!Tools.isEmpty(props.row['_dragIcon_'])) { |
||||
|
return { |
||||
|
name: props.row['_dragIcon_'], |
||||
|
color: DragAndDrop.DRAG_ICON.color, |
||||
|
}; |
||||
|
} else if (tools.props.treeIcon) { |
||||
|
return tools.props.treeIcon(props.row); |
||||
|
} else { |
||||
|
if (tools.props.treeLazyLoad && !props.row[lazyloadNoChildrenField]) { |
||||
|
return { |
||||
|
name: style.icon.defaultTop.name, |
||||
|
color: style.icon.defaultTop.color, |
||||
|
}; |
||||
|
} else if (props.row.children && props.row.children.length > 0) { |
||||
|
return { |
||||
|
name: style.icon.defaultTop.name, |
||||
|
color: style.icon.defaultTop.color, |
||||
|
}; |
||||
|
} else { |
||||
|
return { |
||||
|
name: style.icon.defaultChild.name, |
||||
|
color: style.icon.defaultChild.color, |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const showInlineEditComponentComputed = computed(() => { |
||||
|
return tools.editFM.isShowInlineEditor(props.col, props.row[rowKeyField]); |
||||
|
}); |
||||
|
|
||||
|
// 展开/收缩按钮触发事件 |
||||
|
const expandFun = async () => { |
||||
|
if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.NONE) { |
||||
|
if (tools.props.treeLazyLoad && (!props.row.children || props.row.children.length === 0)) { |
||||
|
lazyLoadExpandHandle(props.row); |
||||
|
} else { |
||||
|
rowDataComputed.value[expandField] = !props.row[expandField]; |
||||
|
} |
||||
|
} else { |
||||
|
NotifyManager.info('请先保存或取消编辑状态'); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const lazyLoadExpandHandle = async (row: any) => { |
||||
|
lazyloading.value = true; |
||||
|
const childrenData = await tools.reqApiFM.treeFetchDataByForeignKey(row[tools.props.primaryKey]); |
||||
|
if (childrenData.length > 0) { |
||||
|
tools.apiFM.localMode.addLocalData(childrenData); |
||||
|
} else { |
||||
|
rowDataComputed.value[lazyloadNoChildrenField] = true; |
||||
|
} |
||||
|
rowDataComputed.value[expandField] = true; |
||||
|
lazyloading.value = false; |
||||
|
}; |
||||
|
|
||||
|
// checkbox触发事件 |
||||
|
const selectedFun = (value, event) => { |
||||
|
const row = rowDataComputed.value; |
||||
|
if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.NONE) { |
||||
|
if (value) { |
||||
|
row[tickedField] = true; |
||||
|
} else { |
||||
|
row[tickedField] = false; |
||||
|
} |
||||
|
selectedChildren(row, value); |
||||
|
selectedParent(row, value); |
||||
|
if (tools.props.onUpdateTicked) { |
||||
|
tools.props.onUpdateTicked(event, row); |
||||
|
} |
||||
|
} else { |
||||
|
row[tickedField] = !row[tickedField]; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 设置子节点选中状态 |
||||
|
const selectedChildren = (row, value) => { |
||||
|
if (row.children && row.children.length > 0) { |
||||
|
for (let child of row.children) { |
||||
|
child[tickedField] = value; |
||||
|
selectedChildren(child, value); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 设置父节点选中状态 |
||||
|
const selectedParent = (row, value) => { |
||||
|
if (row.parent) { |
||||
|
const parent = tools.dataFM.getRow(tools.table.rows, row.parent, true); |
||||
|
if (parent) { |
||||
|
if (value && childrenSelectedStatus(parent).allSelected) { |
||||
|
parent[tickedField] = true; |
||||
|
} else if (value) { |
||||
|
parent[tickedField] = null; |
||||
|
} else { |
||||
|
if (childrenSelectedStatus(parent).partSelected) { |
||||
|
parent[tickedField] = null; |
||||
|
} else { |
||||
|
parent[tickedField] = false; |
||||
|
} |
||||
|
} |
||||
|
selectedParent(parent, value); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 子节点选中状态 |
||||
|
const childrenSelectedStatus = (row) => { |
||||
|
let result = { |
||||
|
allSelected: true, |
||||
|
partSelected: false, |
||||
|
}; |
||||
|
if (row.children && row.children.length > 0) { |
||||
|
for (let i = 0; i < row.children.length; i++) { |
||||
|
if (!row.children[i][tickedField]) { |
||||
|
result.allSelected = false; |
||||
|
} else { |
||||
|
result.partSelected = true; |
||||
|
} |
||||
|
const temp = childrenSelectedStatus(row.children[i]); |
||||
|
if (!temp.allSelected) { |
||||
|
result.allSelected = false; |
||||
|
} |
||||
|
if (temp.partSelected) { |
||||
|
result.partSelected = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
}; |
||||
|
|
||||
|
watch( |
||||
|
() => tools.dndFM.iconAndNameDwellTime.value, |
||||
|
(newVal, oldVal) => { |
||||
|
if ( |
||||
|
newVal >= 3 && |
||||
|
tools.dndFM.dwellRow && |
||||
|
!tools.dndFM.dwellRow[Constant.FIELD_NAMES.EXPAND] && |
||||
|
tools.dndFM.dwellRow[Constant.FIELD_NAMES.ROW_KEY] === props.row[Constant.FIELD_NAMES.ROW_KEY] |
||||
|
) { |
||||
|
if (tools.props.treeLazyLoad && (!props.row.children || props.row.children.length === 0)) { |
||||
|
lazyLoadExpandHandle(tools.dndFM.dwellRow); |
||||
|
} else { |
||||
|
tools.apiFM.operator.expand(tools.dndFM.dwellRow); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="css"></style> |
@ -1,907 +0,0 @@ |
|||||
<template> |
|
||||
<q-tr |
|
||||
ref="trRef" |
|
||||
:no-hover="props.grid.props.selectMode === selectMode.row ? false : true" |
|
||||
:class="row[table.selectedField] && props.grid.props.selectMode === selectMode.row ? 'selected' : ''" |
|
||||
:draggable="draggableComputed" |
|
||||
@click.stop.prevent="click($event, row, props.rowIndex)" |
|
||||
@dblclick.stop.prevent="dbClick($event, row, props.rowIndex)" |
|
||||
@dragleave="draggableComputed ? onDragLeave($event, row) : () => {}" |
|
||||
@dragover="draggableComputed ? onDragOver($event, row) : () => {}" |
|
||||
@drop="draggableComputed ? onDrop($event, row) : () => {}" |
|
||||
@dragstart="draggableComputed ? onDragStart($event, row) : () => {}" |
|
||||
> |
|
||||
<q-td |
|
||||
:ref="(el) => setTdRef(el, cols[0])" |
|
||||
:class="props.grid.props.selectMode === selectMode.cell ? firstTdClassComputed : 'nowrap text-nowrap'" |
|
||||
:style="cols[0].type && table.bodyEditStatus !== 'none' ? 'width: ' + (tdWidth[cols[0].name] - 24) + 'px;' : ''" |
|
||||
@click=" |
|
||||
() => { |
|
||||
// 判定是否要退出编辑模式 |
|
||||
if (props.grid.props.localMode && table.bodyEditStatus === editStatus.cell && props.grid.props.selectMode === selectMode.cell) { |
|
||||
if (table['cellSelected']['colName'] !== cols[0]['name'] || table['cellSelected']['row'][props.rowKey] !== props.row[props.rowKey]) { |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
} |
|
||||
} else if (props.grid.props.localMode && table.bodyEditStatus === editStatus.row && props.grid.props.selectMode === selectMode.cell) { |
|
||||
if (table['cellSelected']['row'][props.rowKey] !== props.row[props.rowKey]) { |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
} |
|
||||
} |
|
||||
if (table.bodyEditStatus === 'none' && props.grid.props.selectMode === selectMode.cell) { |
|
||||
table['cellSelected'] = { |
|
||||
row: toRaw(props.row), |
|
||||
rowKey: props.row[props.rowKey], |
|
||||
primaryKey: props.row[props.grid.props.primaryKey], |
|
||||
colName: cols[0]['name'], |
|
||||
value: props.row[cols[0]['name']], |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
" |
|
||||
> |
|
||||
<div ref="tdDivRef" class="flex flex-nowrap items-center h-full w-full" style="flex-wrap: nowrap"> |
|
||||
<!--层级占位符--> |
|
||||
<span :style="`width:${24 * props.level}px;`"></span> |
|
||||
<!--展开按钮--> |
|
||||
<q-btn |
|
||||
v-if="row.children && row.children.length > 0" |
|
||||
flat |
|
||||
dense |
|
||||
padding="0px 0px" |
|
||||
:icon="row.expand ? 'bi-dash' : 'bi-plus'" |
|
||||
@click.stop="expandFun(row)" |
|
||||
@dblclick.stop="() => {}" |
|
||||
/> |
|
||||
<!--展开按钮占位符--> |
|
||||
<span v-else style="width: 24px"></span> |
|
||||
<!--选择框--> |
|
||||
<q-checkbox |
|
||||
v-if="table.checkboxSelection" |
|
||||
v-model="props.getRow(table.rows, row[props.rowKey], false)[table.tickedField]" |
|
||||
flat |
|
||||
dense |
|
||||
@dblclick.stop="() => {}" |
|
||||
@update:model-value="selectedFun(row[table.tickedField], row, $event)" |
|
||||
/> |
|
||||
<!--图标--> |
|
||||
<q-icon v-if="typeof iconComputed === 'string'" :name="iconComputed" size="20px" class="px-1"></q-icon> |
|
||||
<q-icon v-else-if="typeof iconComputed === 'object'" size="20px" v-bind="iconComputed" class="px-1"></q-icon> |
|
||||
<span v-else class="px-1"></span> |
|
||||
<template v-if="cols[0]"> |
|
||||
<template |
|
||||
v-if=" |
|
||||
!Tools.isEmpty(cols[0]['type']) && |
|
||||
((isSelectedRowComputed && table.bodyEditStatus === 'rowEdit') || |
|
||||
table.bodyEditStatus === 'rowsEdit' || |
|
||||
(isSelectedCell(cols[0]) && table.bodyEditStatus === 'cellEdit')) |
|
||||
" |
|
||||
> |
|
||||
<template v-if="props.grid.props.localMode && table.bodyEditStatus !== editStatus.rows"> |
|
||||
<component |
|
||||
:is="cols[0]['type']" |
|
||||
:ref="(el) => setComponentRef(el, props.row, cols[0])" |
|
||||
v-bind="cols[0]['attrs']" |
|
||||
v-model="props.getRow(table.rows, props.row[props.rowKey], false)[cols[0]['name']]" |
|
||||
bg-color="light-green-1" |
|
||||
@blur=" |
|
||||
() => { |
|
||||
if (props.grid.props.selectMode === selectMode.cell && table.bodyEditStatus === editStatus.cell) { |
|
||||
// 失去焦点,退出编辑模式 |
|
||||
table.bodyEditStatus = editStatus.none; |
|
||||
table['cellSelected'] = {}; |
|
||||
} |
|
||||
} |
|
||||
" |
|
||||
></component> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<component |
|
||||
:is="cols[0]['type']" |
|
||||
:ref="(el) => setComponentRef(el, props.row, cols[0])" |
|
||||
v-bind="cols[0]['attrs']" |
|
||||
v-model="props.getRow(table.rows, props.row[props.rowKey], false)['_rowOldValue'][cols[0]['name']]" |
|
||||
bg-color="light-green-1" |
|
||||
></component> |
|
||||
</template> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<template v-if="cols[0].value && typeof cols[0].value === 'object' && cols[0].value.componentType"> |
|
||||
<component :is="cols[0].value.componentType" v-bind="cols[0].value.attrs"></component> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<span v-dompurify-html="cols[0].value || ''"></span> |
|
||||
</template> |
|
||||
</template> |
|
||||
</template> |
|
||||
</div> |
|
||||
</q-td> |
|
||||
<template v-for="(col, index) in cols" :key="col.name"> |
|
||||
<q-td |
|
||||
v-if="index > 0" |
|
||||
:ref="(el) => setTdRef(el, col)" |
|
||||
:class="props.grid.props.selectMode === selectMode.cell ? tdClassComputed(col) : col.__thClass + ' ' + col.classes" |
|
||||
:title="getTitle(col)" |
|
||||
:style="col.style + (col.type && table.bodyEditStatus !== 'none' ? ';width: ' + (tdWidth[col.name] - 24) + 'px;' : '')" |
|
||||
@click=" |
|
||||
() => { |
|
||||
// 判定是否要退出编辑模式 |
|
||||
if (props.grid.props.localMode && table.bodyEditStatus === editStatus.cell && props.grid.props.selectMode === selectMode.cell) { |
|
||||
if (table['cellSelected']['colName'] !== col['name'] || table['cellSelected']['row'][props.rowKey] !== props.row[props.rowKey]) { |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
} |
|
||||
} else if (props.grid.props.localMode && table.bodyEditStatus === editStatus.row && props.grid.props.selectMode === selectMode.cell) { |
|
||||
if (table['cellSelected']['row'][props.rowKey] !== props.row[props.rowKey]) { |
|
||||
table.bodyEditStatus = 'none'; |
|
||||
} |
|
||||
} |
|
||||
if (table.bodyEditStatus === 'none' && props.grid.props.selectMode === selectMode.cell) { |
|
||||
table['cellSelected'] = { |
|
||||
row: toRaw(props.row), |
|
||||
rowKey: props.row[props.rowKey], |
|
||||
primaryKey: props.row[props.grid.props.primaryKey], |
|
||||
colName: col['name'], |
|
||||
value: props.row[col['name']], |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
" |
|
||||
> |
|
||||
<template |
|
||||
v-if=" |
|
||||
!Tools.isEmpty(col.type) && |
|
||||
((isSelectedRowComputed && table.bodyEditStatus === 'rowEdit') || |
|
||||
table.bodyEditStatus === 'rowsEdit' || |
|
||||
(isSelectedCell(col) && table.bodyEditStatus === 'cellEdit')) |
|
||||
" |
|
||||
> |
|
||||
<template v-if="props.grid.props.localMode && table.bodyEditStatus !== editStatus.rows"> |
|
||||
<component |
|
||||
:is="col.type" |
|
||||
:ref="(el) => setComponentRef(el, props.row, col)" |
|
||||
v-bind="componentAttrs(col)" |
|
||||
v-model="props.getRow(table.rows, props.row[props.rowKey], false)[col.name]" |
|
||||
bg-color="light-green-1" |
|
||||
></component> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<component |
|
||||
:is="col.type" |
|
||||
:ref="(el) => setComponentRef(el, props.row, col)" |
|
||||
v-bind="componentAttrs(col)" |
|
||||
v-model="props.getRow(table.rows, props.row[props.rowKey], false)['_rowOldValue'][col.name]" |
|
||||
bg-color="light-green-1" |
|
||||
></component> |
|
||||
</template> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<template v-if="col.value && typeof col.value === 'object' && col.value.componentType && col.value.bindModelValue"> |
|
||||
<component |
|
||||
:is="col.value.componentType" |
|
||||
v-bind="col.value.attrs" |
|
||||
v-model="props.getRow(table.rows, row[props.rowKey], false)[col.name]" |
|
||||
></component> |
|
||||
</template> |
|
||||
<template v-else-if="col.value && typeof col.value === 'object' && col.value.componentType"> |
|
||||
<component :is="col.value.componentType" v-bind="col.value.attrs"></component> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<span v-dompurify-html="!Tools.isEmpty(col.value) ? col.value : ''"></span> |
|
||||
</template> |
|
||||
</template> |
|
||||
</q-td> |
|
||||
</template> |
|
||||
</q-tr> |
|
||||
<GridEditToolbar |
|
||||
:grid="props.grid" |
|
||||
:url="props.url" |
|
||||
:row-key-name="props.rowKey" |
|
||||
:row="props.row" |
|
||||
:get-row="props.getRow" |
|
||||
:get-row-component-refs="props.getRowComponentRefs" |
|
||||
:set-old-value="props.setOldValue" |
|
||||
:no-data-tr-colspan="props.noDataTrColspan" |
|
||||
></GridEditToolbar> |
|
||||
<template v-for="(child, index) in row.children" :key="child[rowKey]"> |
|
||||
<TreeGridRow |
|
||||
v-if="row.expand" |
|
||||
:columns-map="props.columnsMap" |
|
||||
:row="child" |
|
||||
:cols="childColsHandler(child)" |
|
||||
:level="props.level + 1" |
|
||||
:row-key="props.rowKey" |
|
||||
:grid="props.grid" |
|
||||
:grid-row-click="props.gridRowClick" |
|
||||
:grid-row-db-click="gridRowDbClick" |
|
||||
:after-drag-and-drop="props.afterDragAndDrop" |
|
||||
:get-row="props.getRow" |
|
||||
:url="props.url" |
|
||||
:get-row-component-refs="props.getRowComponentRefs" |
|
||||
:set-old-value="props.setOldValue" |
|
||||
:no-data-tr-colspan="props.noDataTrColspan" |
|
||||
:updates="props.updates" |
|
||||
:row-index="index" |
|
||||
></TreeGridRow> |
|
||||
</template> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, computed, inject, toRaw, reactive, onMounted } from 'vue'; |
|
||||
import { Tools, NotifyManager } from '@/platform'; |
|
||||
import GridEditToolbar from './GridEditToolbar.vue'; |
|
||||
import { dndImage, dndMode, selectMode, editStatus } from './ts/grid'; |
|
||||
|
|
||||
const trRef = ref(); |
|
||||
const tdDivRef = ref(); |
|
||||
const componentOneRef = ref(); |
|
||||
const componentRef = ref({}); |
|
||||
const tdRef = ref({}); |
|
||||
const tdWidth = ref({}); |
|
||||
const props = defineProps({ |
|
||||
level: { type: Number, default: 0 }, |
|
||||
columnsMap: { |
|
||||
type: Map, |
|
||||
default: () => { |
|
||||
return new Map(); |
|
||||
}, |
|
||||
}, |
|
||||
row: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
cols: { |
|
||||
type: Array, |
|
||||
default: () => { |
|
||||
return []; |
|
||||
}, |
|
||||
}, |
|
||||
rowKey: { type: String, default: '_rowKey_' }, |
|
||||
rowIndex: { type: Number, default: undefined }, |
|
||||
grid: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
getRow: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
gridRowClick: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
gridRowDbClick: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
afterDragAndDrop: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
url: { |
|
||||
// 表格所有的url |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
getRowComponentRefs: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
setOldValue: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
noDataTrColspan: { |
|
||||
type: Number, |
|
||||
default: () => { |
|
||||
return 0; |
|
||||
}, |
|
||||
}, |
|
||||
updates: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
}); |
|
||||
const table = inject('table'); |
|
||||
|
|
||||
const style = { |
|
||||
icon: { |
|
||||
defaultTop: { |
|
||||
// 默认顶级节点图标 |
|
||||
name: 'bi-folder-fill', |
|
||||
color: 'amber', |
|
||||
}, |
|
||||
defaultChild: { |
|
||||
// 默认子节点图标 |
|
||||
name: 'note', |
|
||||
color: '', |
|
||||
}, |
|
||||
drag: { |
|
||||
// 拖拽时父节点图标 |
|
||||
name: 'bi-folder-symlink-fill', |
|
||||
color: 'amber', |
|
||||
}, |
|
||||
}, |
|
||||
dragLine: { |
|
||||
// 拖拽线样式 |
|
||||
width: '2px', |
|
||||
style: 'dashed', |
|
||||
color: 'orange', |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
const getTitle = (col) => { |
|
||||
if (table['columns'] && table['columns'].length > 0) { |
|
||||
const column = table['columns'].find((item) => item['name'] === col.name); |
|
||||
if (column && column['title'] && typeof column['title'] === 'function') { |
|
||||
return column.title({ grid: props.grid, row: toRaw(props.row), value: props.row[col.name] }); |
|
||||
} else if (column && column['title']) { |
|
||||
return column['title']; |
|
||||
} |
|
||||
} |
|
||||
if (col.classes?.indexOf('truncate') > -1 && !Tools.isEmpty(props.row[col.name]) && typeof props.row[col.name] !== 'object') { |
|
||||
return props.row[col.name]; |
|
||||
} |
|
||||
return ''; |
|
||||
}; |
|
||||
|
|
||||
const firstTdClassComputed = computed(() => { |
|
||||
const tdClass = <any>['nowrap', 'text-nowrap']; |
|
||||
if (props.grid.props.selectMode === selectMode.cell) { |
|
||||
tdClass.push('cellHover'); |
|
||||
} |
|
||||
if (table && table['cellSelected'] && Tools.hasOwnProperty(table['cellSelected'], 'colName')) { |
|
||||
if (table['cellSelected']['colName'] === props.cols[0]['name'] && table['cellSelected']['rowKey'] === props.row[props.rowKey]) { |
|
||||
tdClass.push('cellSelected'); |
|
||||
} |
|
||||
} |
|
||||
return tdClass; |
|
||||
}); |
|
||||
const tdClassComputed = computed(() => { |
|
||||
return (col) => { |
|
||||
let tdClass = col.__thClass + ' ' + col.classes; |
|
||||
if (props.grid.props.selectMode === selectMode.cell) { |
|
||||
tdClass += ' cellHover'; |
|
||||
} |
|
||||
if (table && table['cellSelected'] && Tools.hasOwnProperty(table['cellSelected'], 'colName')) { |
|
||||
if (table['cellSelected']['colName'] === col['name'] && table['cellSelected']['rowKey'] === props.row[props.rowKey]) { |
|
||||
tdClass += ' cellSelected'; |
|
||||
} |
|
||||
} |
|
||||
return tdClass; |
|
||||
}; |
|
||||
}); |
|
||||
|
|
||||
const draggableComputed = computed(() => { |
|
||||
if ( |
|
||||
props.grid.props.dndMode && |
|
||||
typeof props.grid.props.dndMode === 'string' && |
|
||||
!Tools.isEmpty(dndMode[props.grid.props.dndMode]) && |
|
||||
table.bodyEditStatus === editStatus.none |
|
||||
) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
|
|
||||
const isSelectedRowComputed = computed(() => { |
|
||||
const selected = props.grid.getSelectedRow(); |
|
||||
if (!Tools.isEmpty(selected)) { |
|
||||
return props.row[props.rowKey] === props.grid.getSelectedRow()[props.rowKey]; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
const isSelectedCell = (col) => { |
|
||||
const selected = props.grid.getSelectedCell(); |
|
||||
if (selected?.colName && props.row[props.rowKey] === selected['row'][props.rowKey] && col['name'] === selected.colName) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}; |
|
||||
|
|
||||
const iconComputed = computed(() => { |
|
||||
if (!Tools.isEmpty(props.row['_dragIcon_'])) { |
|
||||
return { |
|
||||
name: props.row['_dragIcon_'], |
|
||||
color: style.icon.drag.color, |
|
||||
}; |
|
||||
} else if (props.grid.props.treeIcon) { |
|
||||
return props.grid.props.treeIcon(props.row); |
|
||||
} else { |
|
||||
if (props.row.children && props.row.children.length > 0) { |
|
||||
return { |
|
||||
name: style.icon.defaultTop.name, |
|
||||
color: style.icon.defaultTop.color, |
|
||||
}; |
|
||||
} else { |
|
||||
return { |
|
||||
name: style.icon.defaultChild.name, |
|
||||
color: style.icon.defaultChild.color, |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
const componentAttrs = (col) => { |
|
||||
if (col.attrs) { |
|
||||
return col.attrs; |
|
||||
} else if (props.grid.props.editor?.form?.fields) { |
|
||||
const field = props.grid.props.editor.form.fields.find((item) => item['name'] === col.name); |
|
||||
if (field) { |
|
||||
return { ...field, label: undefined }; |
|
||||
} |
|
||||
} |
|
||||
return undefined; |
|
||||
}; |
|
||||
|
|
||||
// 展开/收缩按钮触发事件 |
|
||||
const expandFun = (row) => { |
|
||||
if (table.bodyEditStatus === 'none') { |
|
||||
row.expand = !row.expand; |
|
||||
} else { |
|
||||
NotifyManager.info('请先保存或取消编辑状态'); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// checkbox触发事件 |
|
||||
const selectedFun = (value, row, event) => { |
|
||||
if (table.bodyEditStatus === 'none') { |
|
||||
if (value) { |
|
||||
props.getRow(table.rows, row[props.rowKey], false)[table.tickedField] = true; |
|
||||
} else { |
|
||||
props.getRow(table.rows, row[props.rowKey], false)[table.tickedField] = false; |
|
||||
} |
|
||||
selectedChildren(row, value); |
|
||||
selectedParent(row, value); |
|
||||
if (props.grid.props.onUpdateTicked) { |
|
||||
props.grid.props.onUpdateTicked(event, row); |
|
||||
} |
|
||||
} else { |
|
||||
props.getRow(table.rows, row[props.rowKey], false)[table.tickedField] = !props.getRow(table.rows, row[props.rowKey], false)[table.tickedField]; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 设置子节点选中状态 |
|
||||
const selectedChildren = (row, value) => { |
|
||||
if (row.children && row.children.length > 0) { |
|
||||
for (let child of row.children) { |
|
||||
child[table.tickedField] = value; |
|
||||
selectedChildren(child, value); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 设置父节点选中状态 |
|
||||
const selectedParent = (row, value) => { |
|
||||
if (row.parent) { |
|
||||
const parent = props.getRow(table.rows, row.parent, true); |
|
||||
if (parent) { |
|
||||
if (value && childrenSelectedStatus(parent).allSelected) { |
|
||||
parent[table.tickedField] = true; |
|
||||
// selectedPush(parent); |
|
||||
} else if (value) { |
|
||||
parent[table.tickedField] = null; |
|
||||
} else { |
|
||||
// selectedRemove(parent); |
|
||||
if (childrenSelectedStatus(parent).partSelected) { |
|
||||
parent[table.tickedField] = null; |
|
||||
} else { |
|
||||
parent[table.tickedField] = false; |
|
||||
} |
|
||||
} |
|
||||
selectedParent(parent, value); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 子节点选中状态 |
|
||||
const childrenSelectedStatus = (row) => { |
|
||||
let result = { |
|
||||
allSelected: true, |
|
||||
partSelected: false, |
|
||||
}; |
|
||||
if (row.children && row.children.length > 0) { |
|
||||
for (let i = 0; i < row.children.length; i++) { |
|
||||
if (!row.children[i][table.tickedField]) { |
|
||||
result.allSelected = false; |
|
||||
} else { |
|
||||
result.partSelected = true; |
|
||||
} |
|
||||
const temp = childrenSelectedStatus(row.children[i]); |
|
||||
if (!temp.allSelected) { |
|
||||
result.allSelected = false; |
|
||||
} |
|
||||
if (temp.partSelected) { |
|
||||
result.partSelected = true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return result; |
|
||||
}; |
|
||||
|
|
||||
// 子节点cols属性处理 |
|
||||
const childColsHandler = (child) => { |
|
||||
const cols = <any>[]; |
|
||||
props.cols.forEach((col) => { |
|
||||
if (props.columnsMap.get(col.name) && props.columnsMap.get(col.name).format) { |
|
||||
let value = child[col.name]; |
|
||||
try { |
|
||||
value = props.columnsMap.get(col.name).format(child[col.name], child); |
|
||||
} catch (error) { |
|
||||
console.error('format error!'); |
|
||||
} |
|
||||
cols.push({ ...col, value: value }); |
|
||||
} else { |
|
||||
cols.push({ ...col, value: child[col.name] }); |
|
||||
} |
|
||||
}); |
|
||||
return cols; |
|
||||
}; |
|
||||
|
|
||||
document.addEventListener('dragstart', function (e) { |
|
||||
if (tdDivRef?.value) { |
|
||||
tdDivRef.value.classList.add('gridTdDiv'); |
|
||||
} |
|
||||
}); |
|
||||
document.addEventListener('drop', function (e) { |
|
||||
if (tdDivRef?.value) { |
|
||||
tdDivRef.value.classList.remove('gridTdDiv'); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 拖拽开始 |
|
||||
const onDragStart = (e, dataRow) => { |
|
||||
const img = new Image(); |
|
||||
img.src = dndImage; |
|
||||
e.dataTransfer.setDragImage(img, 0, 0); |
|
||||
const selecteds = props.grid.getSelectedRows(); |
|
||||
if ( |
|
||||
selecteds.length > 0 && |
|
||||
selecteds.findIndex((item) => { |
|
||||
return item[props.rowKey] === dataRow[props.rowKey]; |
|
||||
}) > -1 |
|
||||
) { |
|
||||
table.dragRecords = selecteds; |
|
||||
} else { |
|
||||
props.grid.clearSelected(); |
|
||||
// 触发选择 |
|
||||
dataRow[table.selectedField] = true; |
|
||||
table.dragRecords = [dataRow]; |
|
||||
} |
|
||||
e.dataTransfer.effectAllowed = 'move'; |
|
||||
}; |
|
||||
const addDragTopStyle = (e) => { |
|
||||
if (e.target?.parentNode?.children && e.target.nodeName === 'TD') { |
|
||||
for (let i = 0; i < e.target.parentNode.children.length; i++) { |
|
||||
e.target.parentNode.children[i].style.borderTopWidth = style.dragLine.width; |
|
||||
e.target.parentNode.children[i].style.borderTopStyle = style.dragLine.style; |
|
||||
e.target.parentNode.children[i].style.borderTopColor = style.dragLine.color; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
const removeDragTopStyle = (e) => { |
|
||||
if (e.target?.parentNode?.children && e.target.nodeName === 'TD') { |
|
||||
for (let i = 0; i < e.target.parentNode.children.length; i++) { |
|
||||
e.target.parentNode.children[i].style.borderTopWidth = ''; |
|
||||
e.target.parentNode.children[i].style.borderTopStyle = ''; |
|
||||
e.target.parentNode.children[i].style.borderTopColor = ''; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
const addDragBottomStyle = (e) => { |
|
||||
if (e.target?.parentNode?.children && e.target.nodeName === 'TD') { |
|
||||
for (let i = 0; i < e.target.parentNode.children.length; i++) { |
|
||||
e.target.parentNode.children[i].style.borderBottomWidth = style.dragLine.width; |
|
||||
e.target.parentNode.children[i].style.borderBottomStyle = style.dragLine.style; |
|
||||
e.target.parentNode.children[i].style.borderBottomColor = style.dragLine.color; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
const removeDragBottomStyle = (e) => { |
|
||||
if (e.target?.parentNode?.children && e.target.nodeName === 'TD') { |
|
||||
for (let i = 0; i < e.target.parentNode.children.length; i++) { |
|
||||
e.target.parentNode.children[i].style.borderBottomWidth = ''; |
|
||||
e.target.parentNode.children[i].style.borderBottomStyle = ''; |
|
||||
e.target.parentNode.children[i].style.borderBottomColor = ''; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 设置拖拽目标的父节点icon图标 |
|
||||
const setDragIcon = (arr, key: string = '') => { |
|
||||
for (let i = 0; i < arr.length; i++) { |
|
||||
if (arr[i].children && arr[i].children.length > 0) { |
|
||||
if (!Tools.isEmpty(key) && arr[i][props.grid.props.primaryKey] === key) { |
|
||||
if (Tools.isEmpty(arr[i]['_dragIcon_']) || arr[i]['_dragIcon_'] !== style.icon.drag.name) { |
|
||||
arr[i]['_dragIcon_'] = style.icon.drag.name; |
|
||||
} |
|
||||
} else { |
|
||||
arr[i]['_dragIcon_'] = ''; |
|
||||
} |
|
||||
setDragIcon(arr[i].children, key); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 判断是否可以拖拽 |
|
||||
* @param dragRecords 拖拽的记录集 |
|
||||
* @param targetParentKey 当前鼠标所在的目标行的父节点key |
|
||||
*/ |
|
||||
const canDrag = (dragRecords, targetParentKey) => { |
|
||||
if (Tools.isEmpty(targetParentKey)) { |
|
||||
return true; |
|
||||
} |
|
||||
// 判断拖拽的行是否为当前鼠标所在的目标行的父节点(只要存在父,一直往上找) |
|
||||
let result = true; |
|
||||
let parentKey = targetParentKey; |
|
||||
while (!Tools.isEmpty(parentKey)) { |
|
||||
if ( |
|
||||
dragRecords.findIndex((item) => { |
|
||||
return item[props.grid.props.primaryKey] === parentKey; |
|
||||
}) > -1 |
|
||||
) { |
|
||||
result = false; |
|
||||
parentKey = null; |
|
||||
} else { |
|
||||
parentKey = props.getRow(table.rows, parentKey, true)[props.grid.props.foreignKey]; |
|
||||
} |
|
||||
} |
|
||||
return result; |
|
||||
}; |
|
||||
|
|
||||
// 得到表格数据行的中间高度 |
|
||||
const gridTrMiddleHeightComputed = computed(() => { |
|
||||
if (trRef?.value) { |
|
||||
return trRef.value.$el.offsetHeight / 2; |
|
||||
} else { |
|
||||
return (table.dense || table.denseBody ? 24 : 48) / 2; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 根据传入的数据行判断是否在当前拖拽的记录集合中 |
|
||||
const dragRecordsContains = (dataRow) => { |
|
||||
return ( |
|
||||
table.dragRecords.findIndex((item) => { |
|
||||
return item[props.rowKey] === dataRow[props.rowKey]; |
|
||||
}) > -1 |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
// 拖拽至不可放置区域触发 |
|
||||
const onDragLeave = (e, dataRow) => { |
|
||||
removeDragTopStyle(e); |
|
||||
removeDragBottomStyle(e); |
|
||||
if (tdDivRef?.value) { |
|
||||
tdDivRef.value.classList.remove('gridTdDiv'); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 拖拽过程触发 |
|
||||
* @param e 拖拽的html元素 |
|
||||
* @param dataRow 当前鼠标所在的目标行数据 |
|
||||
*/ |
|
||||
const onDragOver = (e, dataRow) => { |
|
||||
e.preventDefault(); |
|
||||
if (!canDrag(table.dragRecords, dataRow[props.grid.props.foreignKey])) { |
|
||||
removeDragTopStyle(e); |
|
||||
removeDragBottomStyle(e); |
|
||||
setDragIcon(table.rows); |
|
||||
e.dataTransfer.dropEffect = 'none'; |
|
||||
} else { |
|
||||
if (e.target.nodeName === 'TD' && e.offsetY <= gridTrMiddleHeightComputed.value && !dragRecordsContains(dataRow)) { |
|
||||
removeDragBottomStyle(e); |
|
||||
addDragTopStyle(e); |
|
||||
} else if (e.target.nodeName === 'TD' && e.offsetY > gridTrMiddleHeightComputed.value && !dragRecordsContains(dataRow)) { |
|
||||
removeDragTopStyle(e); |
|
||||
addDragBottomStyle(e); |
|
||||
} |
|
||||
if (!Tools.isUndefinedOrNull(dataRow[props.grid.props.foreignKey])) { |
|
||||
setDragIcon(table.rows, dataRow[props.grid.props.foreignKey]); |
|
||||
} else { |
|
||||
setDragIcon(table.rows); |
|
||||
} |
|
||||
e.dataTransfer.dropEffect = 'move'; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 删除表格中的数据 |
|
||||
const removeRecord = (arr, removeData) => { |
|
||||
let index = 0; |
|
||||
for (let i = 0; i < arr.length; i++) { |
|
||||
if (arr[i][props.rowKey] === removeData[props.rowKey]) { |
|
||||
arr.splice(i, 1); |
|
||||
index = i; |
|
||||
break; |
|
||||
} else if (arr[i].children && arr[i].children.length > 0) { |
|
||||
const a = removeRecord(arr[i].children, removeData); |
|
||||
if (a > 0) { |
|
||||
index = a; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return index; |
|
||||
}; |
|
||||
|
|
||||
// 待更新的排序数据 |
|
||||
const updateOrderData = <any>[]; |
|
||||
|
|
||||
// 处理树节点的增加 |
|
||||
const nodeSplice = (e, dragIndex, targetIndex, drag, target, arr) => { |
|
||||
if ( |
|
||||
(Tools.isEmpty(drag[props.grid.props.foreignKey]) && Tools.isEmpty(target[props.grid.props.foreignKey])) || |
|
||||
drag[props.grid.props.foreignKey] === target[props.grid.props.foreignKey] |
|
||||
) { |
|
||||
const lessThanResult = dragLessThanTarget(dragIndex, targetIndex); |
|
||||
// 父节点相同的同级处理 |
|
||||
if (e.offsetY <= gridTrMiddleHeightComputed.value && lessThanResult) { |
|
||||
arr.splice(targetIndex - 1, 0, drag); |
|
||||
} else if (e.offsetY > gridTrMiddleHeightComputed.value && !lessThanResult) { |
|
||||
arr.splice(targetIndex + 1, 0, drag); |
|
||||
} else { |
|
||||
arr.splice(targetIndex, 0, drag); |
|
||||
} |
|
||||
} else { |
|
||||
// 拖拽节点与目标节点不同级处理 |
|
||||
if (e.offsetY <= gridTrMiddleHeightComputed.value) { |
|
||||
arr.splice(targetIndex, 0, drag); |
|
||||
} else if (e.offsetY > gridTrMiddleHeightComputed.value) { |
|
||||
arr.splice(targetIndex + 1, 0, drag); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 设置排序号 |
|
||||
const setOrder = (arr) => { |
|
||||
arr.forEach((item, index) => { |
|
||||
item[props.grid.props.dndOrderBy] = index + 1; |
|
||||
// 添加至待更新集合中 |
|
||||
updateOrderData.push({ ...toRaw(item), children: null }); |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
// 增加并重新设置父属性 |
|
||||
const childrenResetOrder = (e, arr, addData, targetData, dragIndex, targetIndex) => { |
|
||||
for (let i = 0; i < arr.length; i++) { |
|
||||
if (arr[i][props.grid.props.primaryKey] === targetData[props.grid.props.foreignKey]) { |
|
||||
nodeSplice(e, dragIndex, targetIndex, addData, targetData, arr[i].children); |
|
||||
// 修改父 |
|
||||
addData[props.grid.props.foreignKey] = arr[i][props.grid.props.primaryKey]; |
|
||||
// 重新设置排序号 |
|
||||
setOrder(arr[i].children); |
|
||||
break; |
|
||||
} else if (arr[i].children && arr[i].children.length > 0) { |
|
||||
childrenResetOrder(e, arr[i].children, addData, targetData, dragIndex, targetIndex); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const dragLessThanTarget = (dragIndex, targetIndex) => { |
|
||||
if (dragIndex < targetIndex) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}; |
|
||||
|
|
||||
// 数据处理 |
|
||||
const resetOrderDataHandler = (e, dragRecords, targetData, targetIndex) => { |
|
||||
dragRecords.forEach((item) => { |
|
||||
// 从表格数据中将拖拽数据删除 |
|
||||
const itemIndex = removeRecord(table.rows, item); |
|
||||
if (Tools.isEmpty(targetData[props.grid.props.foreignKey])) { |
|
||||
nodeSplice(e, itemIndex, targetIndex, item, targetData, table.rows); |
|
||||
item[props.grid.props.foreignKey] = null; |
|
||||
setOrder(table.rows); |
|
||||
} else { |
|
||||
childrenResetOrder(e, table.rows, item, targetData, itemIndex, targetIndex); |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
// 重新设置树的排序 |
|
||||
const resetOrder = (e, dragRecords, arr, targetData) => { |
|
||||
for (let i = 0; i < arr.length; i++) { |
|
||||
if (arr[i][props.rowKey] === targetData[props.rowKey]) { |
|
||||
resetOrderDataHandler(e, dragRecords, targetData, i); |
|
||||
break; |
|
||||
} else if (arr[i].children && arr[i].children.length > 0) { |
|
||||
resetOrder(e, dragRecords, arr[i].children, targetData); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 拖拽放置时触发 |
|
||||
const onDrop = (e, dataRow) => { |
|
||||
e.preventDefault(); |
|
||||
removeDragTopStyle(e); |
|
||||
removeDragBottomStyle(e); |
|
||||
setDragIcon(table.rows); |
|
||||
if ( |
|
||||
table.dragRecords.findIndex((item) => { |
|
||||
return item[props.rowKey] === dataRow[props.rowKey]; |
|
||||
}) > -1 |
|
||||
) { |
|
||||
return; |
|
||||
} |
|
||||
// 每次放置时清空待更新集合 |
|
||||
updateOrderData.splice(0, updateOrderData.length); |
|
||||
// 将拖动的数据及其子节点放到目标数据位置,并将目标数据父节点下的子节点重新排序。 |
|
||||
resetOrder(e, table.dragRecords, table.rows, dataRow); |
|
||||
|
|
||||
// 请求后端更新排序 |
|
||||
if (props.grid.props.dndMode === dndMode.server && updateOrderData?.length > 0) { |
|
||||
props.updates(updateOrderData); |
|
||||
} |
|
||||
|
|
||||
props.afterDragAndDrop(updateOrderData); |
|
||||
}; |
|
||||
|
|
||||
const click = (evt, row, rowIndex) => { |
|
||||
const selected = props.grid.getSelectedRow(); |
|
||||
if ( |
|
||||
props.grid.props.localMode && |
|
||||
table.bodyEditStatus !== editStatus.none && |
|
||||
props.grid.props.selectMode === selectMode.row && |
|
||||
selected && |
|
||||
row[props.rowKey] !== selected[props.rowKey] |
|
||||
) { |
|
||||
// 退出编辑状态 |
|
||||
table.bodyEditStatus = editStatus.none; |
|
||||
} else if (table.bodyEditStatus === editStatus.none && props.grid.props.selectMode !== selectMode.none) { |
|
||||
if (!evt.ctrlKey) { |
|
||||
props.grid.clearSelected(); |
|
||||
} |
|
||||
row[table.selectedField] = true; |
|
||||
if (props.grid.props.onRowClick) { |
|
||||
props.grid.props.onRowClick({ |
|
||||
grid: props.grid, |
|
||||
evt: evt, |
|
||||
row: row, |
|
||||
index: rowIndex, |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
const dbClick = (evt, row, rowIndex) => { |
|
||||
props.gridRowDbClick(evt, row, rowIndex); |
|
||||
}; |
|
||||
|
|
||||
const setComponentRef = (el, row, col) => { |
|
||||
if (el && !Tools.isEmpty(col.type)) { |
|
||||
componentRef.value[row[props.rowKey] + '_' + col.name] = el; |
|
||||
} |
|
||||
}; |
|
||||
const setTdRef = (el, col) => { |
|
||||
if (el && !tdRef.value[col['name']]) { |
|
||||
tdRef.value[col['name']] = el; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const getComponentOneRef = () => { |
|
||||
return componentOneRef.value; |
|
||||
}; |
|
||||
const getComponentRef = () => { |
|
||||
return componentRef.value; |
|
||||
}; |
|
||||
|
|
||||
onMounted(() => { |
|
||||
Object.keys(tdRef.value).forEach((item) => { |
|
||||
tdWidth.value[item] = tdRef.value[item].$el.clientWidth; |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
defineExpose({ |
|
||||
getComponentRef, |
|
||||
getComponentOneRef, |
|
||||
}); |
|
||||
</script> |
|
||||
|
|
||||
<style lang="css"> |
|
||||
.gridTdDiv { |
|
||||
pointer-events: none; |
|
||||
} |
|
||||
</style> |
|
File diff suppressed because it is too large
@ -0,0 +1,216 @@ |
|||||
|
<template> |
||||
|
<w-dialog |
||||
|
ref="dialogRef" |
||||
|
v-bind="tools.props.editor.dialog" |
||||
|
:title="dialog.dialogTitle" |
||||
|
:buttons="dialogButtonsComputed" |
||||
|
@hide=" |
||||
|
() => { |
||||
|
saveLoading = false; |
||||
|
} |
||||
|
" |
||||
|
> |
||||
|
<w-form ref="dialogFormRef" v-bind="tools.props.editor.form" class="pt-1.5 px-1.5"></w-form> |
||||
|
</w-dialog> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { $t, Tools } from '@/platform'; |
||||
|
import { computed, inject, reactive, ref } from 'vue'; |
||||
|
import { Constant, GridTools } from '../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const dialogRef = ref(); |
||||
|
const dialogFormRef = ref(); |
||||
|
const props = defineProps({}); |
||||
|
|
||||
|
const dialogButtonsComputed = computed(() => { |
||||
|
if (tools.props.editor?.dialog?.buttons) { |
||||
|
return [...tools.props.editor.dialog.buttons, ...dialog.dialogButtons]; |
||||
|
} |
||||
|
return dialog.dialogButtons; |
||||
|
}); |
||||
|
|
||||
|
const save = async () => { |
||||
|
saveLoading.value = true; |
||||
|
const formStatus = dialogFormRef.value.getStatus(); |
||||
|
const validate = await dialogFormRef.value.validate(); |
||||
|
if (validate) { |
||||
|
let dialogFormData = dialogFormRef.value.getData(); |
||||
|
const selected = tools.apiFM.getData.getSelectedRow(); |
||||
|
if (formStatus === Constant.FORM_STATUS.EDIT && selected && selected[tools.props.primaryKey] && Tools.isEmpty(dialogFormData[tools.props.primaryKey])) { |
||||
|
dialogFormData[tools.props.primaryKey] = selected[tools.props.primaryKey]; |
||||
|
} |
||||
|
// 触发事件 |
||||
|
const eventResult = tools.em.beforeEditorDataSubmit(dialogFormData); |
||||
|
dialogFormData = eventResult.data; |
||||
|
if (eventResult.submit) { |
||||
|
setForeignKey(dialogFormData, formStatus, selected); |
||||
|
if (formStatus === Constant.FORM_STATUS.EDIT) { |
||||
|
// 将行数据默认添加到传递给后端的数据中 |
||||
|
dialogFormData = { ...selected, ...dialogFormData }; |
||||
|
} |
||||
|
if (tools.props.localMode) { |
||||
|
localModeSave(dialogFormData, formStatus, eventResult.closeDialog); |
||||
|
} else { |
||||
|
serverSave(dialogFormData, formStatus, selected, eventResult.closeDialog); |
||||
|
} |
||||
|
} else { |
||||
|
saveLoading.value = false; |
||||
|
if (eventResult.closeDialog) { |
||||
|
dialogRef.value.hide(); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
saveLoading.value = false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const saveLoading = ref(false); |
||||
|
const dialog = reactive({ |
||||
|
dialogTitle: $t('action.addNew'), |
||||
|
dialogButtons: [ |
||||
|
{ |
||||
|
icon: 'beenhere', |
||||
|
labelI18nKey: tools.props.localMode ? 'confirm' : 'action.submit', |
||||
|
label: $t(tools.props.localMode ? 'confirm' : 'action.submit'), |
||||
|
loading: saveLoading.value, |
||||
|
click: () => { |
||||
|
save(); |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}); |
||||
|
|
||||
|
// 新增树表格中的数据 |
||||
|
const addTreeRow = (row) => { |
||||
|
if (Tools.isEmpty(row[tools.props.foreignKey])) { |
||||
|
tools.table.rows.push(row); |
||||
|
} else { |
||||
|
const parent = tools.dataFM.getRow(tools.table.rows, row[tools.props.foreignKey], true); |
||||
|
if (parent) { |
||||
|
if (parent['children'] && Array.isArray(parent['children'])) { |
||||
|
parent['children'].push(row); |
||||
|
} else { |
||||
|
parent['children'] = [row]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const addData = (dialogFormData) => { |
||||
|
if (tools.props.tree) { |
||||
|
addTreeRow(dialogFormData); |
||||
|
tools.dataFM.setExtraProperty(tools.table.rows); |
||||
|
} else { |
||||
|
tools.apiFM.localMode.addLocalData(dialogFormData, undefined); |
||||
|
} |
||||
|
}; |
||||
|
const updateData = (dialogFormData) => { |
||||
|
const selected = tools.apiFM.getData.getSelectedRow(); |
||||
|
if (!selected) { |
||||
|
return; |
||||
|
} |
||||
|
if (Tools.isEmpty(dialogFormData[tools.props.primaryKey])) { |
||||
|
dialogFormData[tools.props.primaryKey] = selected[tools.props.primaryKey]; |
||||
|
} |
||||
|
dialogFormData[tools.props.selectedField] = true; |
||||
|
if (selected['children']) { |
||||
|
dialogFormData['children'] = selected['children']; |
||||
|
} |
||||
|
tools.apiFM.localMode.updateLocalData(dialogFormData); |
||||
|
}; |
||||
|
|
||||
|
const localModeSave = (dialogFormData: any, formStatus: string, closeDialog: boolean) => { |
||||
|
if (isAdd(formStatus)) { |
||||
|
addData(dialogFormData); |
||||
|
} else { |
||||
|
updateData(dialogFormData); |
||||
|
} |
||||
|
saveLoading.value = false; |
||||
|
if (closeDialog) { |
||||
|
dialogRef.value.hide(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const serverSave = (dialogFormData: any, formStatus: string, selected: any, closeDialog: boolean) => { |
||||
|
const method = getMethod(formStatus); |
||||
|
const url = getUrl(formStatus, selected); |
||||
|
const callback = (resp: any) => { |
||||
|
saveLoading.value = false; |
||||
|
if (closeDialog) { |
||||
|
dialogRef.value.hide(); |
||||
|
} |
||||
|
if (tools.props.refreshData || !tools.props.tree) { |
||||
|
tools.apiFM.operator.refreshGrid(); |
||||
|
} else if (resp.data && isAdd(formStatus)) { |
||||
|
addData(resp.data); |
||||
|
} else if (resp.data) { |
||||
|
updateData(resp.data); |
||||
|
} |
||||
|
}; |
||||
|
const errorCallback = () => { |
||||
|
saveLoading.value = false; |
||||
|
}; |
||||
|
tools.reqApiFM.save(method, dialogFormData, url, dialogFormRef.value, callback, errorCallback); |
||||
|
}; |
||||
|
|
||||
|
const setForeignKey = (dialogFormData: any, formStatus: string, selected: any) => { |
||||
|
if (formStatus === Constant.FORM_STATUS.ADD) { |
||||
|
dialogFormData[tools.props.foreignKey] = null; |
||||
|
} else if (formStatus === Constant.FORM_STATUS.ADD_CHILD) { |
||||
|
dialogFormData[tools.props.foreignKey] = selected[tools.props.primaryKey]; |
||||
|
} else if ((formStatus === Constant.FORM_STATUS.EDIT || formStatus === Constant.FORM_STATUS.CLONE) && selected[tools.props.foreignKey]) { |
||||
|
dialogFormData[tools.props.foreignKey] = selected[tools.props.foreignKey]; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const isAdd = (formStatus: string) => { |
||||
|
return ( |
||||
|
formStatus === Constant.FORM_STATUS.ADD || |
||||
|
formStatus === Constant.FORM_STATUS.CLONE || |
||||
|
formStatus === Constant.FORM_STATUS.ADD_TOP || |
||||
|
formStatus === Constant.FORM_STATUS.ADD_CHILD |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const getMethod = (formStatus: string) => { |
||||
|
if (isAdd(formStatus)) { |
||||
|
return 'POST'; |
||||
|
} else { |
||||
|
return 'PUT'; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const getUrl = (formStatus: string, selected: any) => { |
||||
|
if (isAdd(formStatus)) { |
||||
|
if (!Tools.isEmpty(tools.table.url.addDataUrl)) { |
||||
|
return tools.table.url.addDataUrl; |
||||
|
} else { |
||||
|
return tools.table.url.dataUrl; |
||||
|
} |
||||
|
} else { |
||||
|
if (!Tools.isEmpty(tools.table.url.editDataUrl)) { |
||||
|
return tools.table.url.editDataUrl + '/' + selected[tools.props.primaryKey]; |
||||
|
} else { |
||||
|
return tools.table.url.dataUrl + '/' + selected[tools.props.primaryKey]; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const resetButtonLabel = () => { |
||||
|
dialog.dialogButtons[0].label = $t(dialog.dialogButtons[0].labelI18nKey); |
||||
|
}; |
||||
|
const getDialog = () => { |
||||
|
return dialogRef.value; |
||||
|
}; |
||||
|
const getForm = () => { |
||||
|
return dialogFormRef.value; |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
resetButtonLabel, |
||||
|
getDialog, |
||||
|
getForm, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="css"></style> |
@ -0,0 +1,71 @@ |
|||||
|
<template> |
||||
|
<w-drawer ref="drawerRef" :title="$t('action.view')" v-bind="tools.props.viewer?.drawer"> |
||||
|
<div class="p-2.5"> |
||||
|
<w-info-panel ref="infoRef" v-bind="tools.props.viewer?.panel" :info="infoArray"></w-info-panel> |
||||
|
</div> |
||||
|
</w-drawer> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { $t, NotifyManager, t } from '@/platform'; |
||||
|
import { inject, ref } from 'vue'; |
||||
|
import { GridTools } from '../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const drawerRef = ref(); |
||||
|
const infoRef = ref(); |
||||
|
const gridColumnObjects = GridTools.arrayToObject(tools.table.originalColumns, 'name'); |
||||
|
|
||||
|
const props = defineProps({}); |
||||
|
|
||||
|
const infoArray = ref(<any>[]); |
||||
|
|
||||
|
const view = () => { |
||||
|
const selected = tools.apiFM.getData.getSelectedRow(); |
||||
|
if (!selected) { |
||||
|
NotifyManager.warn(t('action.view.tip')); |
||||
|
} else { |
||||
|
infoArray.value = []; |
||||
|
if (tools.props.viewer.panel.fields && tools.props.viewer.panel.fields.length > 0) { |
||||
|
for (let item of tools.props.viewer.panel.fields) { |
||||
|
const value = formatValue(item, selected); |
||||
|
infoArray.value.push({ label: item.label, value: value, originalValue: selected[item.name] }); |
||||
|
} |
||||
|
} else { |
||||
|
for (let key in gridColumnObjects) { |
||||
|
const item = gridColumnObjects[key]; |
||||
|
const value = formatValue(item, selected); |
||||
|
infoArray.value.push({ label: item.label, value: value, originalValue: selected[key] }); |
||||
|
} |
||||
|
} |
||||
|
drawerRef.value.show(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const formatValue = (item, selected) => { |
||||
|
let value = selected[item.name]; |
||||
|
try { |
||||
|
if (item.format) { |
||||
|
value = item.format(value, selected); |
||||
|
} else if (gridColumnObjects[item.name] && gridColumnObjects[item.name].format) { |
||||
|
value = gridColumnObjects[item.name].format(value, selected); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('[w-grid]column `' + item.name + '` format error:', error); |
||||
|
} |
||||
|
return value; |
||||
|
}; |
||||
|
|
||||
|
const getViewerDrawer = () => { |
||||
|
return drawerRef.value; |
||||
|
}; |
||||
|
const getInfoPanel = () => { |
||||
|
return infoRef.value; |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
getViewerDrawer, |
||||
|
getInfoPanel, |
||||
|
view, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="css"></style> |
@ -0,0 +1,58 @@ |
|||||
|
<template> |
||||
|
<w-query-builder ref="queryBuilderRef" v-model="tools.table.advancedQueryModelValue" :options="options"></w-query-builder> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject, onBeforeMount, ref } from 'vue'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const queryBuilderRef = ref(); |
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const options: any = []; |
||||
|
|
||||
|
const buildOptions = () => { |
||||
|
options.splice(0, options.length - 1); |
||||
|
tools.props.queryFormFields.forEach((item) => { |
||||
|
const field = { |
||||
|
label: item['label'], |
||||
|
value: item['name'], |
||||
|
useComponent: { |
||||
|
...item, |
||||
|
label: undefined, // 覆盖label |
||||
|
name: undefined, // 覆盖name |
||||
|
type: typeConversion(item['type']), // 覆盖类型 |
||||
|
}, |
||||
|
}; |
||||
|
options.push(field); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 对于特定的类型,使用 w-query-build 组件时会存在矛盾点. |
||||
|
* 比如普通查询配置使用日期范围,但是在w-query-build中本身就提供范围选项,所以需要将其转换成普通日期类型 |
||||
|
* @param type |
||||
|
*/ |
||||
|
const typeConversion = (type: string) => { |
||||
|
let result = ''; |
||||
|
switch (type) { |
||||
|
case 'w-date-range': |
||||
|
result = 'w-date'; |
||||
|
break; |
||||
|
default: |
||||
|
result = type; |
||||
|
break; |
||||
|
} |
||||
|
return result; |
||||
|
}; |
||||
|
|
||||
|
onBeforeMount(() => { |
||||
|
buildOptions(); |
||||
|
}); |
||||
|
|
||||
|
const reset = () => { |
||||
|
queryBuilderRef.value.clearValue(); |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
reset: reset, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,21 @@ |
|||||
|
<template> |
||||
|
<template v-if="typeof props.value === 'object' && props.value.componentType"> |
||||
|
<component :is="props.value.componentType" v-bind="props.value.attrs"></component> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<span v-dompurify-html="!Tools.isEmpty(props.value) ? props.value : ''"></span> |
||||
|
</template> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { Tools } from '@/platform'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
value: { |
||||
|
type: [Object, String, Number], |
||||
|
default: () => { |
||||
|
return ''; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,39 @@ |
|||||
|
<template> |
||||
|
<template v-if="showAppendRowsComputed"> |
||||
|
<q-tr v-for="(appendRow, rowIndex) in tools.props.appendRows" :key="rowIndex" :no-hover="true" :class="''"> |
||||
|
<q-td v-for="(col, colIndex) in appendRow" :key="colIndex" :rowspan="col['rowSpan'] ? col['rowSpan'] : 1" :colspan="col['colSpan'] ? col['colSpan'] : 1"> |
||||
|
<template v-if="typeof col['value'] === 'function'"> |
||||
|
<AppendContent :value="col.value({ grid: tools.instance, rows: tools.apiFM.getData.getRows() })"></AppendContent> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<span v-dompurify-html="!Tools.isEmpty(col['value']) ? col['value'] : ''"></span> |
||||
|
</template> |
||||
|
</q-td> |
||||
|
</q-tr> |
||||
|
</template> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { inject, computed } from 'vue'; |
||||
|
import { Tools } from '@/platform'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
import AppendContent from './AppendContent.vue'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const props = defineProps({ |
||||
|
scope: { |
||||
|
// 顶部插槽属性 |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const showAppendRowsComputed = computed(() => { |
||||
|
if (Array.isArray(tools.props.appendRows) && tools.props.appendRows.length > 0) { |
||||
|
return tools.dataFM.isLastRow(props.scope.row); |
||||
|
} |
||||
|
return false; |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,54 @@ |
|||||
|
<template> |
||||
|
<q-expansion-item label="分组"> |
||||
|
<q-card> |
||||
|
<q-card-section> |
||||
|
<template v-for="col in tools.cm.displayColumns.value" :key="col.name"> |
||||
|
<q-item v-if="col !== Constant.FIELD_NAMES.SORT_NO" v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="dynamicModel[col]" dense @update:model-value="groupByFieldChange(col)" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>{{ displayColumnLabel[col] }}</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</template> |
||||
|
</q-card-section> |
||||
|
</q-card> |
||||
|
</q-expansion-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject, ref } from 'vue'; |
||||
|
import { GridTools, Constant } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const dynamicModel = ref({}); |
||||
|
const displayColumnLabel = ref({}); |
||||
|
|
||||
|
const getColumn = (name) => { |
||||
|
return tools.table.columns.find((item) => item['name'] === name); |
||||
|
}; |
||||
|
|
||||
|
tools.cm.displayColumns.value.forEach((item) => { |
||||
|
const column = getColumn(item); |
||||
|
if (column) { |
||||
|
displayColumnLabel.value[item] = column.label; |
||||
|
} |
||||
|
dynamicModel.value[item] = item === tools.table.configStore.aloneGroupByField || false; |
||||
|
}); |
||||
|
|
||||
|
const groupByFieldChange = (col: string) => { |
||||
|
const value = dynamicModel.value[col]; |
||||
|
if (value) { |
||||
|
// 清空其他勾选状态 |
||||
|
Object.keys(dynamicModel.value).forEach((item) => { |
||||
|
if (item !== col) { |
||||
|
dynamicModel.value[item] = false; |
||||
|
} |
||||
|
}); |
||||
|
tools.table.configStore.aloneGroupByField = col; |
||||
|
} else { |
||||
|
tools.table.configStore.aloneGroupByField = undefined; |
||||
|
} |
||||
|
tools.dataFM.setExtraProperty(tools.table.rows); |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,36 @@ |
|||||
|
<template> |
||||
|
<q-item :clickable="true" @click="click"> |
||||
|
<q-item-section> |
||||
|
<q-item-label>复选框</q-item-label> |
||||
|
</q-item-section> |
||||
|
<q-item-section side> |
||||
|
<q-btn-group outline flat dense unelevated spread> |
||||
|
<q-btn |
||||
|
:color="tools.table.configStore.useCheckboxSelection ? 'primary' : ''" |
||||
|
:outline="tools.table.configStore.useCheckboxSelection ? false : true" |
||||
|
dense |
||||
|
label="显示" |
||||
|
unelevated |
||||
|
/> |
||||
|
<q-btn |
||||
|
:color="tools.table.configStore.useCheckboxSelection ? '' : 'primary'" |
||||
|
:outline="tools.table.configStore.useCheckboxSelection ? true : false" |
||||
|
dense |
||||
|
label="隐藏" |
||||
|
unelevated |
||||
|
/> |
||||
|
</q-btn-group> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
|
||||
|
const click = () => { |
||||
|
tools.table.configStore.useCheckboxSelection = !tools.table.configStore.useCheckboxSelection; |
||||
|
tools.opFM.resetStyleVariableValue(100); |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,60 @@ |
|||||
|
<template> |
||||
|
<q-list padding style="min-width: 250px"> |
||||
|
<!-- 全屏 --> |
||||
|
<Fullscreen :scope="props.scope"></Fullscreen> |
||||
|
<q-separator /> |
||||
|
|
||||
|
<!-- 复选框 --> |
||||
|
<CheckboxSelection></CheckboxSelection> |
||||
|
<q-separator /> |
||||
|
|
||||
|
<!-- 序号 --> |
||||
|
<template v-if="!tools.props.tree"> |
||||
|
<SortNo></SortNo> |
||||
|
<q-separator /> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 分割线 --> |
||||
|
<Separator></Separator> |
||||
|
<q-separator /> |
||||
|
|
||||
|
<!-- 紧凑模式 --> |
||||
|
<Dense></Dense> |
||||
|
<q-separator /> |
||||
|
|
||||
|
<!-- 独立分组 --> |
||||
|
<template v-if="!tools.props.tree && tools.props.groupMode === Constant.GROUP_MODE.ALONE"> |
||||
|
<AloneGorup></AloneGorup> |
||||
|
<q-separator /> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 固定列 --> |
||||
|
<StickyColumn></StickyColumn> |
||||
|
<q-separator /> |
||||
|
|
||||
|
<!-- 显示列 --> |
||||
|
<DisplayColumn></DisplayColumn> |
||||
|
</q-list> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { Constant, GridTools } from '../../ts/index'; |
||||
|
import CheckboxSelection from './CheckboxSelection.vue'; |
||||
|
import Dense from './Dense.vue'; |
||||
|
import DisplayColumn from './DisplayColumn.vue'; |
||||
|
import Fullscreen from './Fullscreen.vue'; |
||||
|
import Separator from './Separator.vue'; |
||||
|
import SortNo from './SortNo.vue'; |
||||
|
import StickyColumn from './StickyColumn.vue'; |
||||
|
import AloneGorup from './AloneGroup.vue'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const props = defineProps({ |
||||
|
scope: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,57 @@ |
|||||
|
<template> |
||||
|
<q-expansion-item label="紧凑模式"> |
||||
|
<q-card> |
||||
|
<q-card-section> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="tools.table.configStore.dense" dense @update:model-value="denseChange" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>紧凑</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="tools.table.configStore.denseToolbar" dense @update:model-value="denseChange" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>按钮栏紧凑</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="tools.table.configStore.denseHeader" dense @update:model-value="denseChange" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>列头紧凑</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="tools.table.configStore.denseBody" dense @update:model-value="denseChange" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>内容紧凑</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="tools.table.configStore.denseBottom" dense @update:model-value="denseChange" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>分页栏紧凑</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</q-card-section> |
||||
|
</q-card> |
||||
|
</q-expansion-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const denseChange = () => { |
||||
|
tools.opFM.resetStyleVariableValue(0); |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,47 @@ |
|||||
|
<template> |
||||
|
<q-expansion-item label="显示列"> |
||||
|
<q-card> |
||||
|
<q-card-section> |
||||
|
<template v-for="col in tools.table.columns" :key="col.name"> |
||||
|
<q-item v-if="showColumn(col.name)" v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox |
||||
|
v-model="col.showIf" |
||||
|
dense |
||||
|
@update:model-value=" |
||||
|
() => { |
||||
|
tools.opFM.resetStyleVariableValue(100); |
||||
|
} |
||||
|
" |
||||
|
/> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>{{ col.label || col.name }}</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</template> |
||||
|
</q-card-section> |
||||
|
</q-card> |
||||
|
</q-expansion-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const showColumn = (name) => { |
||||
|
// if (props.moreColumnTitleArray.length > 0) { |
||||
|
// if ( |
||||
|
// props.moreColumnTitleArray[0].findIndex((item) => { |
||||
|
// return item.name === name; |
||||
|
// }) > -1 |
||||
|
// ) { |
||||
|
// return true; |
||||
|
// } else { |
||||
|
// return false; |
||||
|
// } |
||||
|
// } else { |
||||
|
// } |
||||
|
return true; |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,35 @@ |
|||||
|
<template> |
||||
|
<q-item |
||||
|
:clickable="true" |
||||
|
@click=" |
||||
|
() => { |
||||
|
scope.toggleFullscreen(); |
||||
|
tools.table.configStore.showConfigPanel = false; |
||||
|
} |
||||
|
" |
||||
|
> |
||||
|
<q-item-section> |
||||
|
<q-item-label>全屏</q-item-label> |
||||
|
</q-item-section> |
||||
|
<q-item-section side> |
||||
|
<q-btn-group outline flat dense unelevated spread> |
||||
|
<q-btn :color="!scope.inFullscreen ? 'primary' : ''" :outline="scope.inFullscreen ? true : false" dense label="正常" unelevated /> |
||||
|
<q-btn :color="scope.inFullscreen ? 'primary' : ''" :outline="!scope.inFullscreen ? true : false" dense label="全屏" unelevated /> |
||||
|
</q-btn-group> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const props = defineProps({ |
||||
|
scope: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,96 @@ |
|||||
|
<template> |
||||
|
<q-expansion-item label="分割线"> |
||||
|
<q-card> |
||||
|
<q-card-section> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="separatorHorizontal" dense @update:model-value="separatorChange('horizontal', separatorHorizontal)" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>水平</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="separatorVertical" dense @update:model-value="separatorChange('vertical', separatorVertical)" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>垂直</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="separatorCell" dense @update:model-value="separatorChange('cell', separatorCell)" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>单元格</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
<q-item v-ripple tag="label" dense> |
||||
|
<q-item-section side> |
||||
|
<q-checkbox v-model="separatorNone" dense @update:model-value="separatorChange('none', separatorNone)" /> |
||||
|
</q-item-section> |
||||
|
<q-item-section> |
||||
|
<q-item-label>无</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</q-card-section> |
||||
|
</q-card> |
||||
|
</q-expansion-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject, ref, watch } from 'vue'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
|
||||
|
const separatorHorizontal = ref(false); |
||||
|
const separatorVertical = ref(false); |
||||
|
const separatorCell = ref(false); |
||||
|
const separatorNone = ref(false); |
||||
|
|
||||
|
const setSeparatorValue = (value) => { |
||||
|
if (value === 'horizontal') { |
||||
|
separatorHorizontal.value = true; |
||||
|
separatorVertical.value = false; |
||||
|
separatorCell.value = false; |
||||
|
separatorNone.value = false; |
||||
|
} else if (value === 'vertical') { |
||||
|
separatorHorizontal.value = false; |
||||
|
separatorVertical.value = true; |
||||
|
separatorCell.value = false; |
||||
|
separatorNone.value = false; |
||||
|
} else if (value === 'cell') { |
||||
|
separatorHorizontal.value = false; |
||||
|
separatorVertical.value = false; |
||||
|
separatorCell.value = true; |
||||
|
separatorNone.value = false; |
||||
|
} else if (value === 'none') { |
||||
|
separatorHorizontal.value = false; |
||||
|
separatorVertical.value = false; |
||||
|
separatorCell.value = false; |
||||
|
separatorNone.value = true; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
if (tools.table.configStore.separator) { |
||||
|
setSeparatorValue(tools.table.configStore.separator); |
||||
|
} |
||||
|
|
||||
|
const separatorChange = (separator, value) => { |
||||
|
if (!value) { |
||||
|
setSeparatorValue(tools.table.configStore.separator); |
||||
|
} else { |
||||
|
tools.table.configStore.separator = separator; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
watch( |
||||
|
() => tools.table.configStore.separator, |
||||
|
(newVal, oldVal) => { |
||||
|
if (newVal) { |
||||
|
setSeparatorValue(newVal); |
||||
|
} |
||||
|
}, |
||||
|
); |
||||
|
</script> |
@ -0,0 +1,36 @@ |
|||||
|
<template> |
||||
|
<q-item :clickable="true" @click="click"> |
||||
|
<q-item-section> |
||||
|
<q-item-label>序号</q-item-label> |
||||
|
</q-item-section> |
||||
|
<q-item-section side> |
||||
|
<q-btn-group outline flat dense unelevated spread> |
||||
|
<q-btn |
||||
|
:color="tools.table.configStore.showSortNo ? 'primary' : ''" |
||||
|
:outline="tools.table.configStore.showSortNo ? false : true" |
||||
|
dense |
||||
|
label="显示" |
||||
|
unelevated |
||||
|
/> |
||||
|
<q-btn |
||||
|
:color="tools.table.configStore.showSortNo ? '' : 'primary'" |
||||
|
:outline="tools.table.configStore.showSortNo ? true : false" |
||||
|
dense |
||||
|
label="隐藏" |
||||
|
unelevated |
||||
|
/> |
||||
|
</q-btn-group> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const click = () => { |
||||
|
tools.table.columns[0].showIf = !tools.table.columns[0].showIf; |
||||
|
tools.table.configStore.showSortNo = !tools.table.configStore.showSortNo; |
||||
|
tools.opFM.resetStyleVariableValue(100); |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,44 @@ |
|||||
|
<template> |
||||
|
<q-item :clickable="false"> |
||||
|
<q-item-section> |
||||
|
<q-item-label class="nowrap text-nowrap">固定列</q-item-label> |
||||
|
</q-item-section> |
||||
|
<q-item-section side> |
||||
|
<q-select |
||||
|
v-model="tools.table.configStore.stickyNum" |
||||
|
emit-value |
||||
|
map-options |
||||
|
:hide-bottom-space="true" |
||||
|
:hide-hint="true" |
||||
|
:outlined="true" |
||||
|
:dense="true" |
||||
|
:options="stickyOptions" |
||||
|
@update:model-value=" |
||||
|
() => { |
||||
|
tools.opFM.resetStyleVariableValue(500); |
||||
|
} |
||||
|
" |
||||
|
> |
||||
|
</q-select> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const stickyOptions = [ |
||||
|
{ label: '不固定', value: 0 }, |
||||
|
{ label: '固定1列', value: 1 }, |
||||
|
{ label: '固定2列', value: 2 }, |
||||
|
{ label: '固定3列', value: 3 }, |
||||
|
{ label: '固定4列', value: 4 }, |
||||
|
{ label: '固定5列', value: 5 }, |
||||
|
{ label: '固定6列', value: 6 }, |
||||
|
{ label: '固定7列', value: 7 }, |
||||
|
{ label: '固定8列', value: 8 }, |
||||
|
{ label: '固定9列', value: 9 }, |
||||
|
{ label: '固定10列', value: 10 }, |
||||
|
]; |
||||
|
</script> |
@ -0,0 +1,54 @@ |
|||||
|
<template> |
||||
|
<template v-if="props.scope.pageIndex === 0 && tools.table.configStore.aloneGroupRecords.length > 0"> |
||||
|
<template v-for="(record, index) in tools.table.configStore.aloneGroupRecords" :key="index"> |
||||
|
<q-tr ref="trRef" @click.stop="changeExpand(record)"> |
||||
|
<q-td :colspan="tools.cm.displayColumnNum.value"> |
||||
|
<q-btn |
||||
|
:size="tools.table.configStore.dense || tools.table.configStore.denseBody ? 'xs' : 'sm'" |
||||
|
color="primary" |
||||
|
round |
||||
|
dense |
||||
|
:icon="record['expand'] ? 'remove' : 'add'" |
||||
|
@click.stop="changeExpand(record)" |
||||
|
/> |
||||
|
<span class="ml-3 font-medium">{{ record['groupName'] }}</span> |
||||
|
</q-td> |
||||
|
</q-tr> |
||||
|
<template v-if="record['expand']"> |
||||
|
<Tr v-for="(groupRow, groupRowIndex) in record['rows']" :key="groupRowIndex" :scope="buildScope(groupRow)"></Tr> |
||||
|
</template> |
||||
|
</template> |
||||
|
</template> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import Tr from '../../Tr.vue'; |
||||
|
import { GridTools, Constant } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const props = defineProps({ |
||||
|
scope: { |
||||
|
// 插槽属性 |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const changeExpand = (record: any) => { |
||||
|
record['expand'] = !record['expand']; |
||||
|
}; |
||||
|
|
||||
|
const buildScope = (groupRow: any) => { |
||||
|
const result = GridTools.buildChildrenScope( |
||||
|
props.scope, |
||||
|
groupRow, |
||||
|
groupRow[Constant.FIELD_NAMES.ROW_INDEX], |
||||
|
tools.props.selectedField, |
||||
|
tools.props.tickedField, |
||||
|
); |
||||
|
return result; |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,97 @@ |
|||||
|
<template> |
||||
|
<w-dialog ref="dialogRef" :title="dialog.title" v-bind="tools.props.editor?.cellEditor" :buttons="dialog.buttons"> |
||||
|
<w-form ref="dialogFormRef" :cols-num="1" :fields="fieldsComputed" class="pt-1.5 px-1.5"></w-form> |
||||
|
</w-dialog> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { t, Tools } from '@/platform'; |
||||
|
import { computed, inject, ref, toRaw } from 'vue'; |
||||
|
import { Constant, GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const dialogRef = ref(); |
||||
|
const dialogFormRef = ref(); |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
|
||||
|
const fieldsComputed = computed(() => { |
||||
|
const fields = <any>[]; |
||||
|
const cellSelected = tools.table.store.cellSelected; |
||||
|
if (cellSelected) { |
||||
|
const column = tools.editFM.getEditorFieldByName(cellSelected.colName); |
||||
|
if (column) { |
||||
|
fields.push({ |
||||
|
...column, |
||||
|
name: column['name'], |
||||
|
type: column['type'], |
||||
|
label: column['label'], |
||||
|
defaultValue: cellSelected.value, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
return fields; |
||||
|
}); |
||||
|
|
||||
|
const saveLoading = ref(false); |
||||
|
const dialog = { |
||||
|
title: t('action.edit'), |
||||
|
buttons: [ |
||||
|
{ |
||||
|
icon: 'beenhere', |
||||
|
labelI18nKey: tools.props.localMode ? 'confirm' : 'action.submit', |
||||
|
label: t(tools.props.localMode ? 'confirm' : 'action.submit'), |
||||
|
loading: saveLoading.value, |
||||
|
click: async () => { |
||||
|
saveLoading.value = true; |
||||
|
const result = await dialogFormRef.value.validate(); |
||||
|
if (result) { |
||||
|
const cellSelected = tools.table.store.cellSelected; |
||||
|
if (!cellSelected) { |
||||
|
return; |
||||
|
} |
||||
|
const data = dialogFormRef.value.getData(); |
||||
|
const row = tools.dataFM.getRow(tools.table.rows, cellSelected.row[Constant.FIELD_NAMES.ROW_KEY], false); |
||||
|
if (row) { |
||||
|
row[cellSelected.colName] = data[cellSelected.colName]; |
||||
|
// 判定是否需要往后端发送修改操作 |
||||
|
if (!tools.props.localMode) { |
||||
|
const data = GridTools.removeExtraFields(toRaw(row)); |
||||
|
const url = getUrl(data[tools.props.primaryKey]); |
||||
|
const callback = () => { |
||||
|
saveLoading.value = false; |
||||
|
dialogRef.value.hide(); |
||||
|
}; |
||||
|
const errorCallback = () => { |
||||
|
saveLoading.value = false; |
||||
|
}; |
||||
|
tools.reqApiFM.save('PUT', data, url, dialogFormRef.value, callback, errorCallback); |
||||
|
} else { |
||||
|
dialogRef.value.hide(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
saveLoading.value = false; |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
|
||||
|
const getUrl = (primaryKey) => { |
||||
|
if (!Tools.isEmpty(tools.table.url.editDataUrl)) { |
||||
|
return tools.table.url.editDataUrl + '/' + primaryKey; |
||||
|
} else { |
||||
|
return tools.table.url.dataUrl + '/' + primaryKey; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const getDialog = () => { |
||||
|
return dialogRef.value; |
||||
|
}; |
||||
|
const getForm = () => { |
||||
|
return dialogFormRef.value; |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
getDialog, |
||||
|
getForm, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,71 @@ |
|||||
|
<template> |
||||
|
<template v-if="isDirectlyBindComputed"> |
||||
|
<component |
||||
|
:is="componentProps['type']" |
||||
|
:ref="(el) => tools.editFM.setComponentRef(el, props.row, props.col)" |
||||
|
v-bind="{ ...componentProps, label: undefined }" |
||||
|
v-model="gridRow[props.col['name']]" |
||||
|
bg-color="light-green-1" |
||||
|
@blur=" |
||||
|
() => { |
||||
|
if (needExit()) { |
||||
|
tools.editFM.exitInlineEdit(); |
||||
|
} |
||||
|
} |
||||
|
" |
||||
|
></component> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<component |
||||
|
:is="componentProps['type']" |
||||
|
:ref="(el) => tools.editFM.setComponentRef(el, props.row, props.col)" |
||||
|
v-bind="{ ...componentProps, label: undefined }" |
||||
|
v-model="gridRow[Constant.FIELD_NAMES.ROW_OLD_VALUE][props.col['name']]" |
||||
|
bg-color="light-green-1" |
||||
|
@blur=" |
||||
|
() => { |
||||
|
if (needExit()) { |
||||
|
tools.editFM.exitInlineEdit(); |
||||
|
} |
||||
|
} |
||||
|
" |
||||
|
></component> |
||||
|
</template> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { computed, inject } from 'vue'; |
||||
|
import { GridTools, Constant } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const props = defineProps({ |
||||
|
col: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
row: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
const gridRow = tools.dataFM.getRow(tools.table.rows, props.row[Constant.FIELD_NAMES.ROW_KEY], false); |
||||
|
const componentProps = tools.editFM.getEditorFieldByName(props.col['name']); |
||||
|
|
||||
|
/** |
||||
|
* 是否直接绑定数据 |
||||
|
*/ |
||||
|
const isDirectlyBindComputed = computed(() => { |
||||
|
return tools.props.localMode && tools.table.store.inlineEditStatus !== Constant.EDIT_STATUS.ROWS; |
||||
|
}); |
||||
|
|
||||
|
const needExit = () => { |
||||
|
if (tools.props.localMode && tools.props.selectMode === Constant.SELECT_MODE.CELL && tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.CELL) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,249 @@ |
|||||
|
<template> |
||||
|
<q-tr v-if="showComputed"> |
||||
|
<q-td :colspan="tools.cm.displayColumnNum.value"> |
||||
|
<div class="editButton text-center"> |
||||
|
<w-toolbar v-model="buttons" :dense="true" align="center" :grid="tools.instance"></w-toolbar> |
||||
|
</div> |
||||
|
</q-td> |
||||
|
</q-tr> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { NotifyManager, Tools, $t } from '@/platform'; |
||||
|
import { computed, inject, ref } from 'vue'; |
||||
|
import { Constant, GridTools } from '../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const props = defineProps({ |
||||
|
row: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const buttons = ref([ |
||||
|
{ |
||||
|
label: '保存', |
||||
|
name: 'save', |
||||
|
icon: 'save', |
||||
|
color: 'primary', |
||||
|
outline: false, |
||||
|
click: async (args) => { |
||||
|
save(args); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: '取消', |
||||
|
name: 'cancel', |
||||
|
icon: 'close', |
||||
|
color: 'blue-grey', |
||||
|
outline: false, |
||||
|
click: (args) => { |
||||
|
if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROW || tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.CELL) { |
||||
|
tools.dataFM.setOldValue(args.selected); |
||||
|
} else if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROWS) { |
||||
|
tools.apiFM.getData.getRows().forEach((item) => { |
||||
|
tools.dataFM.setOldValue(item); |
||||
|
}); |
||||
|
} |
||||
|
tools.table.store.inlineEditStatus = Constant.EDIT_STATUS.NONE; |
||||
|
}, |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
const showComputed = computed(() => { |
||||
|
if (tools.props.localMode && tools.table.store.inlineEditStatus !== Constant.EDIT_STATUS.ROWS) { |
||||
|
return false; |
||||
|
} |
||||
|
if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.CELL) { |
||||
|
const selected = tools.apiFM.getData.getSelectedCell(); |
||||
|
if ( |
||||
|
selected?.colName && |
||||
|
selected.colName === tools.table.store.cellSelected?.colName && |
||||
|
selected.row[Constant.FIELD_NAMES.ROW_KEY] === props.row[Constant.FIELD_NAMES.ROW_KEY] |
||||
|
) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} else if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROW && tools.dataFM.isSelectedRow(props.row[Constant.FIELD_NAMES.ROW_KEY])) { |
||||
|
return true; |
||||
|
} else if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROWS && tools.table.rows.length > 0 && isLastRowComputed.value) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const isLastRowComputed = computed(() => { |
||||
|
return tools.dataFM.isLastRow(props.row); |
||||
|
}); |
||||
|
|
||||
|
const validate = async (refs) => { |
||||
|
let result = true; |
||||
|
for (let i = 0; i < refs.length; i++) { |
||||
|
const component = refs[i]; |
||||
|
if (component && !Tools.isEmpty(component.validate)) { |
||||
|
const componentValidateResult = await component.validate(); |
||||
|
if (!componentValidateResult) { |
||||
|
result = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
}; |
||||
|
|
||||
|
const save = async (args) => { |
||||
|
let refs: any = undefined; |
||||
|
if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.CELL) { |
||||
|
refs = [tools.editFM.getComponentRef(args.selected[Constant.FIELD_NAMES.ROW_KEY], args.selectedColName)]; |
||||
|
} else if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROW) { |
||||
|
refs = tools.editFM.getComponentRefs(args.selected[Constant.FIELD_NAMES.ROW_KEY]); |
||||
|
} else if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROWS) { |
||||
|
refs = tools.editFM.getComponentRefs([]); |
||||
|
} |
||||
|
const result = await validate(refs); |
||||
|
if (!result) { |
||||
|
NotifyManager.error('验证未通过'); |
||||
|
} else { |
||||
|
if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROW || tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.CELL) { |
||||
|
rowSave(args); |
||||
|
} else if (tools.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROWS) { |
||||
|
rowsSave(args); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const rowSave = (args) => { |
||||
|
let data = args.selected[Constant.FIELD_NAMES.ROW_OLD_VALUE]; |
||||
|
let submitFlag = true; |
||||
|
let localeUpdateFlag = false; |
||||
|
let url = ''; |
||||
|
// 执行保存 |
||||
|
tools.em.beforeEditorDataSubmit({ |
||||
|
grid: tools.instance, |
||||
|
data: data, |
||||
|
callback: (handlerRequestParams: any | boolean, localeUpdate: boolean = false) => { |
||||
|
if (typeof handlerRequestParams === 'boolean' && handlerRequestParams === false) { |
||||
|
submitFlag = false; |
||||
|
} else { |
||||
|
data = handlerRequestParams; |
||||
|
} |
||||
|
localeUpdateFlag = localeUpdate; |
||||
|
}, |
||||
|
}); |
||||
|
if (localeUpdateFlag && submitFlag) { |
||||
|
// 只进行本地修改,不访问服务器 |
||||
|
tools.apiFM.localMode.updateLocalData(data); |
||||
|
tools.table.store.inlineEditStatus = Constant.EDIT_STATUS.NONE; |
||||
|
} else { |
||||
|
if (submitFlag) { |
||||
|
data = { ...args.selected, ...data }; |
||||
|
if (!Tools.isEmpty(tools.table.url.editDataUrl)) { |
||||
|
url = tools.table.url.editDataUrl + '/' + args.selected[tools.props.primaryKey]; |
||||
|
} else { |
||||
|
url = tools.table.url.dataUrl + '/' + args.selected[tools.props.primaryKey]; |
||||
|
} |
||||
|
const callback = (resp) => { |
||||
|
if (tools.props.refreshData || !tools.props.tree) { |
||||
|
tools.apiFM.operator.refreshGrid(); |
||||
|
} else if (resp.data) { |
||||
|
tools.apiFM.localMode.updateLocalData(data); |
||||
|
} |
||||
|
tools.editFM.exitInlineEdit(); |
||||
|
}; |
||||
|
const errorCallback = (error) => { |
||||
|
if (error?.response.data.code === 1001) { |
||||
|
NotifyManager.error('服务器验证未通过'); |
||||
|
} |
||||
|
}; |
||||
|
tools.reqApiFM.save('PUT', data, url, undefined, callback, errorCallback); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const rowsSave = (args) => { |
||||
|
let data = <any>[]; |
||||
|
const rows = args.grid.getRows(); |
||||
|
const isTree = tools.props.tree; |
||||
|
rows.forEach((item) => { |
||||
|
if (checkDataModified(item)) { |
||||
|
data.push(item[Constant.FIELD_NAMES.ROW_OLD_VALUE]); |
||||
|
} |
||||
|
if (isTree && item.children) { |
||||
|
const childrenData = treeDataPush(item.children); |
||||
|
if (childrenData.length > 0) { |
||||
|
data.push(...childrenData); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
let submitFlag = true; |
||||
|
let localeUpdateFlag = false; |
||||
|
// 执行保存 |
||||
|
tools.em.afterEditorDataSubmit({ |
||||
|
grid: tools.instance, |
||||
|
data: data, |
||||
|
callback: (handlerRequestParams: any | boolean, localeUpdate: boolean = false) => { |
||||
|
if (typeof handlerRequestParams === 'boolean' && handlerRequestParams === false) { |
||||
|
submitFlag = false; |
||||
|
} else { |
||||
|
data = handlerRequestParams; |
||||
|
} |
||||
|
localeUpdateFlag = localeUpdate; |
||||
|
}, |
||||
|
}); |
||||
|
if ((localeUpdateFlag && submitFlag) || tools.props.localMode) { |
||||
|
// 只进行本地修改,不访问服务器 |
||||
|
data.forEach((item) => { |
||||
|
tools.apiFM.localMode.updateLocalData(item); |
||||
|
}); |
||||
|
tools.table.store.inlineEditStatus = Constant.EDIT_STATUS.NONE; |
||||
|
} else { |
||||
|
if (submitFlag) { |
||||
|
tools.reqApiFM.updates(data, (callbackData) => { |
||||
|
NotifyManager.info($t('tip.operationSuccess')); |
||||
|
if (tools.props.refreshData || !tools.props.tree) { |
||||
|
tools.apiFM.operator.refreshGrid(); |
||||
|
} else if (!Tools.isEmpty(callbackData)) { |
||||
|
callbackData.forEach((item) => { |
||||
|
tools.apiFM.localMode.updateLocalData(item); |
||||
|
}); |
||||
|
} |
||||
|
tools.editFM.exitInlineEdit(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 检查数据是否修改过 |
||||
|
const checkDataModified = (row) => { |
||||
|
const keys = Object.keys(row[Constant.FIELD_NAMES.ROW_OLD_VALUE]); |
||||
|
return keys.some((key) => row[key] !== row[Constant.FIELD_NAMES.ROW_OLD_VALUE][key]); |
||||
|
}; |
||||
|
|
||||
|
const treeDataPush = (arr) => { |
||||
|
const data = <any>[]; |
||||
|
if (arr && arr.length > 0) { |
||||
|
arr.forEach((item) => { |
||||
|
if (checkDataModified(item)) { |
||||
|
data.push(item[Constant.FIELD_NAMES.ROW_OLD_VALUE]); |
||||
|
} |
||||
|
const childrenData = treeDataPush(item.children); |
||||
|
if (childrenData.length > 0) { |
||||
|
data.push(...childrenData); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
return data; |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="css"> |
||||
|
.editButton { |
||||
|
position: sticky; |
||||
|
background-color: white; |
||||
|
left: 45%; |
||||
|
width: 150px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,48 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { GridTools } from './GridTools'; |
||||
|
import { TableType } from './types/TableType'; |
||||
|
import type { PropsType } from './types/PropsType'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 基类 |
||||
|
*/ |
||||
|
export class Base { |
||||
|
/** |
||||
|
* w-grid 配置属性 |
||||
|
*/ |
||||
|
props: PropsType; |
||||
|
/** |
||||
|
* w-grid 表格全局变量管理类,可供所有子组件直接使用,避免通过组件属性反复传递。 |
||||
|
*/ |
||||
|
table: TableType; |
||||
|
/** |
||||
|
* w-grid 组件实例 |
||||
|
*/ |
||||
|
instance?: any; |
||||
|
/** |
||||
|
* w-grid 工具类 |
||||
|
*/ |
||||
|
tools?: GridTools; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
this.props = props; |
||||
|
this.table = table; |
||||
|
} |
||||
|
|
||||
|
instanceError() { |
||||
|
if (Tools.isEmpty(this.instance)) { |
||||
|
throw new Error('[w-grid] The `instance` variable of `FunctionTools` class is undefined.'); |
||||
|
} |
||||
|
} |
||||
|
tableError() { |
||||
|
if (Tools.isEmpty(this.table)) { |
||||
|
throw new Error('[w-grid] The `table` variable of `FunctionTools` class is undefined.'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 设置组件实例
|
||||
|
setInstance(instance: any, tools?: GridTools) { |
||||
|
this.instance = instance; |
||||
|
this.tools = tools; |
||||
|
} |
||||
|
} |
@ -0,0 +1,328 @@ |
|||||
|
import { reactive } from 'vue'; |
||||
|
import { Constant } from './constant/Constant'; |
||||
|
import { TableType } from './types/TableType'; |
||||
|
import type { PropsType } from './types/PropsType'; |
||||
|
import { Init } from './Init'; |
||||
|
import { getCssVar } from 'quasar'; |
||||
|
import { Environment, Tools } from '@/platform'; |
||||
|
|
||||
|
import { ComputedManager } from './computed/ComputedManager'; |
||||
|
import { EventManager } from './event/EventManager'; |
||||
|
import { ExposeApiManager } from './expose-api/ExposeApiManager'; |
||||
|
import { Criteria } from './function/Criteria'; |
||||
|
import { DragAndDrop } from './function/DragAndDrop'; |
||||
|
import { InlineEdit } from './function/InlineEdit'; |
||||
|
import { Operator } from './function/Operator'; |
||||
|
import { RequestApi } from './function/RequestApi'; |
||||
|
import { RowData } from './function/RowData'; |
||||
|
import { ButtonManager } from './toolbar/ButtonManager'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 工具类 |
||||
|
*/ |
||||
|
export class GridTools { |
||||
|
/** |
||||
|
* w-grid 配置属性 |
||||
|
*/ |
||||
|
props: PropsType; |
||||
|
/** |
||||
|
* w-grid 表格全局变量管理类,可供所有子组件直接使用,避免通过组件属性反复传递。 |
||||
|
*/ |
||||
|
table: TableType; |
||||
|
/** |
||||
|
* w-grid 组件实例 |
||||
|
*/ |
||||
|
instance?: any; |
||||
|
|
||||
|
/** |
||||
|
* 表格事件管理器 |
||||
|
*/ |
||||
|
em: EventManager; |
||||
|
/** |
||||
|
* 计算属性管理器 |
||||
|
*/ |
||||
|
cm: ComputedManager; |
||||
|
/** |
||||
|
* 表格内置按钮管理器 |
||||
|
*/ |
||||
|
bm: ButtonManager; |
||||
|
/** |
||||
|
* 对外暴露api 函数管理器 |
||||
|
*/ |
||||
|
apiFM: ExposeApiManager; |
||||
|
/** |
||||
|
* 行数据处理函数管理器 |
||||
|
*/ |
||||
|
dataFM: RowData; |
||||
|
/** |
||||
|
* 表格操作函数管理器 |
||||
|
*/ |
||||
|
opFM: Operator; |
||||
|
/** |
||||
|
* 内联编辑函数管理器 |
||||
|
*/ |
||||
|
editFM: InlineEdit; |
||||
|
/** |
||||
|
* 拖拽排序函数管理器 |
||||
|
*/ |
||||
|
dndFM: DragAndDrop; |
||||
|
/** |
||||
|
* 请求后端API函数管理器 |
||||
|
*/ |
||||
|
reqApiFM: RequestApi; |
||||
|
/** |
||||
|
* criteria 查询函数管理器 |
||||
|
*/ |
||||
|
criteriaFM: Criteria; |
||||
|
|
||||
|
constructor(props: PropsType) { |
||||
|
this.props = props; |
||||
|
|
||||
|
const darkCssVar = getCssVar('dark') || ''; |
||||
|
const gc = Environment.getConfigure(); |
||||
|
|
||||
|
const dense = props.dense !== undefined ? props.dense : false; |
||||
|
const denseBottom = props.denseBottom !== undefined ? props.denseBottom : false; |
||||
|
let bodyCellHeight = 48; |
||||
|
if (denseBottom) { |
||||
|
bodyCellHeight = 24; |
||||
|
} else if (dense !== false) { |
||||
|
bodyCellHeight = 24; |
||||
|
} |
||||
|
|
||||
|
let alongGroupByField: any = undefined; |
||||
|
if (this.props.groupMode === Constant.GROUP_MODE.ALONE) { |
||||
|
if (typeof this.props.groupByField === 'string') { |
||||
|
alongGroupByField = this.props.groupByField; |
||||
|
} else if (Array.isArray(this.props.groupByField)) { |
||||
|
alongGroupByField = this.props.groupByField[0]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.table = reactive({ |
||||
|
originalColumns: [...props.columns], |
||||
|
columns: Init.initColumn(props.columns, props), |
||||
|
rows: [], |
||||
|
queryCriteria: { ...props.queryCriteria }, |
||||
|
moreQueryStatus: false, |
||||
|
advancedQueryStatus: false, |
||||
|
advancedQueryModelValue: undefined, |
||||
|
queryFormDisplayFields: [], |
||||
|
componentRef: { |
||||
|
getTableRef: () => {}, |
||||
|
getTopRef: () => {}, |
||||
|
getHeaderRef: () => {}, |
||||
|
getBodyRef: () => {}, |
||||
|
getEditorRef: () => {}, |
||||
|
getCellEditorRef: () => {}, |
||||
|
getViewRef: () => {}, |
||||
|
}, |
||||
|
color: { |
||||
|
darkBgColor: darkCssVar, |
||||
|
headBgColor: gc.theme.dark ? darkCssVar : gc.theme?.grid?.headBgColor || '#f5f7fa', |
||||
|
borderColor: gc.theme?.grid?.borderColor || 'rgba(0, 0, 0, 0.12)', |
||||
|
stickyBgColor: gc.theme.dark ? darkCssVar : gc.theme?.grid?.stickyBgColor || '#ffffff', |
||||
|
}, |
||||
|
url: { |
||||
|
dataUrl: props.dataUrl, |
||||
|
fetchDataUrl: props.fetchDataUrl, |
||||
|
addDataUrl: props.addDataUrl, |
||||
|
editDataUrl: props.editDataUrl, |
||||
|
removeDataUrl: props.removeDataUrl, |
||||
|
}, |
||||
|
configStore: { |
||||
|
showConfigPanel: false, |
||||
|
isFullscreen: false, |
||||
|
separator: props.separator, |
||||
|
stickyNum: props.stickyNum, |
||||
|
useCheckboxSelection: props.checkboxSelection, |
||||
|
showSortNo: props.sortNo, |
||||
|
dense: dense, |
||||
|
denseToolbar: props.denseToolbar !== undefined ? props.denseToolbar : false, |
||||
|
denseHeader: props.denseHeader !== undefined ? props.denseHeader : false, |
||||
|
denseBody: props.denseBody !== undefined ? props.denseBody : false, |
||||
|
denseBottom: denseBottom, |
||||
|
aloneGroupRecords: [], |
||||
|
aloneGroupByField: alongGroupByField, |
||||
|
}, |
||||
|
store: { |
||||
|
expand: props.treeDefaultExpandAll, |
||||
|
expandDatas: [], |
||||
|
alreadyLoadAllData: false, |
||||
|
lazyloadNoChildrenDatas: [], |
||||
|
headerTicked: false, |
||||
|
dragRecords: [], |
||||
|
inlineEditStatus: Constant.EDIT_STATUS.NONE, |
||||
|
cellSelected: undefined, |
||||
|
mergeGroupRecords: {}, |
||||
|
spaceHeight: 4, |
||||
|
noDataLabelI18nKey: 'tip.noData', |
||||
|
loading: false, |
||||
|
loadingLabelI18nKey: 'tip.dataLoading', |
||||
|
bodyCellHeight: bodyCellHeight, |
||||
|
resizeFlag: false, |
||||
|
location: { |
||||
|
yLocation: 0, |
||||
|
noDataTrYLocation: 0, |
||||
|
topHeight: 0, |
||||
|
bottomHeight: 0, |
||||
|
middleWidth: 0, |
||||
|
middleScrollWidth: 0, |
||||
|
middleHeight: 0, |
||||
|
columnHeadHeight: 0, |
||||
|
titleTotalHeight: 0, |
||||
|
hideHeaderNoDataHeight: 0, |
||||
|
tableInFullscreenY: 0, |
||||
|
noDataTrInFullscreenY: 0, |
||||
|
}, |
||||
|
pagination: { |
||||
|
config: { |
||||
|
boundaryLinks: true, |
||||
|
boundaryNumbers: false, |
||||
|
directionLinks: false, |
||||
|
ellipses: false, |
||||
|
maxPages: 5, |
||||
|
}, |
||||
|
sortBy: '', |
||||
|
descending: false, |
||||
|
reqPageStart: props.pagination['reqPageStart'] || 0, |
||||
|
page: 1, |
||||
|
rowsPerPage: props.pagination['rowsPerPage'] || 10, |
||||
|
rowsNumber: 10, |
||||
|
rowsPerPageOptions: [5, 10, 20, 50, 100], |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
this.em = new EventManager(props, this.table); |
||||
|
this.cm = new ComputedManager(props, this.table); |
||||
|
this.bm = new ButtonManager(props, this.table); |
||||
|
this.apiFM = new ExposeApiManager(props, this.table); |
||||
|
this.dataFM = new RowData(props, this.table); |
||||
|
this.opFM = new Operator(props, this.table); |
||||
|
this.editFM = new InlineEdit(props, this.table); |
||||
|
this.dndFM = new DragAndDrop(props, this.table); |
||||
|
this.reqApiFM = new RequestApi(props, this.table); |
||||
|
this.criteriaFM = new Criteria(props, this.table); |
||||
|
} |
||||
|
|
||||
|
// 设置组件实例
|
||||
|
setInstance(instance: any) { |
||||
|
const this_ = this; |
||||
|
this.instance = instance; |
||||
|
this.em.setInstance(instance, this_); |
||||
|
this.cm.setInstance(instance, this_); |
||||
|
this.apiFM.setInstance(instance, this_); |
||||
|
this.bm.setInstance(instance, this_); |
||||
|
this.dataFM.setInstance(instance, this_); |
||||
|
this.opFM.setInstance(instance, this_); |
||||
|
this.editFM.setInstance(instance, this_); |
||||
|
this.dndFM.setInstance(instance, this_); |
||||
|
this.reqApiFM.setInstance(instance, this_); |
||||
|
this.criteriaFM.setInstance(instance, this_); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 移除表格数据中附加的字段 |
||||
|
* @param target |
||||
|
*/ |
||||
|
static removeExtraFields(target: any) { |
||||
|
if (!target) { |
||||
|
console.error('[w-grid] target is undefined.'); |
||||
|
return; |
||||
|
} |
||||
|
if (Array.isArray(target)) { |
||||
|
const tmp = [...target]; |
||||
|
tmp.forEach((item) => { |
||||
|
GridTools.removeFields(item); |
||||
|
}); |
||||
|
return tmp; |
||||
|
} else { |
||||
|
const tmp = { ...target }; |
||||
|
GridTools.removeFields(tmp); |
||||
|
return tmp; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static removeFields(target: any) { |
||||
|
if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.ROW_KEY)) { |
||||
|
delete target[Constant.FIELD_NAMES.ROW_KEY]; |
||||
|
} |
||||
|
if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.EXPAND)) { |
||||
|
delete target[Constant.FIELD_NAMES.EXPAND]; |
||||
|
} |
||||
|
if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.TICKED_COUNT)) { |
||||
|
delete target[Constant.FIELD_NAMES.TICKED_COUNT]; |
||||
|
} |
||||
|
if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT)) { |
||||
|
delete target[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT]; |
||||
|
} |
||||
|
if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.ROW_OLD_VALUE)) { |
||||
|
delete target[Constant.FIELD_NAMES.ROW_OLD_VALUE]; |
||||
|
} |
||||
|
if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.ROW_INDEX)) { |
||||
|
delete target[Constant.FIELD_NAMES.ROW_INDEX]; |
||||
|
} |
||||
|
if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN)) { |
||||
|
delete target[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 对象数组转对象 |
||||
|
* 说明:[{id: 1, value: '张三'}, {id: 2, value: '李四'}] ==> { 1: { id: 1, value: '张三' }, 2: { id: 2, value: '李四'} } |
||||
|
* @param array 对象数组 |
||||
|
* @param propertyName 属性名 |
||||
|
* @returns 转换结果 |
||||
|
*/ |
||||
|
static arrayToObject(array: Array<any>, propertyName: string) { |
||||
|
const result = array.reduce((tmp, item) => { |
||||
|
tmp[item[propertyName]] = item; |
||||
|
return tmp; |
||||
|
}, {}); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 构建孩子行所需的 scope 属性 |
||||
|
* @param currentScope 当前scope |
||||
|
* @param child 孩子行数据 |
||||
|
* @param childIndex 孩子行下标 |
||||
|
* @param selectedFieldName 选中配置的字段名 |
||||
|
* @returns |
||||
|
*/ |
||||
|
static buildChildrenScope(currentScope: any, child: any, childIndex: number, selectedFieldName: string, tickedFieldName: string) { |
||||
|
const cols = GridTools.buildChildrenCols(currentScope, child); |
||||
|
const childScope = { |
||||
|
...currentScope, |
||||
|
cols: cols, |
||||
|
key: child[Constant.FIELD_NAMES.ROW_KEY], |
||||
|
[Constant.FIELD_NAMES.EXPAND]: child[Constant.FIELD_NAMES.EXPAND], |
||||
|
pageIndex: childIndex, |
||||
|
rowIndex: childIndex, |
||||
|
row: child, |
||||
|
[selectedFieldName]: child[selectedFieldName], |
||||
|
[tickedFieldName]: child[tickedFieldName], |
||||
|
}; |
||||
|
return childScope; |
||||
|
} |
||||
|
// 构建孩子行 scope 中 cols 属性
|
||||
|
private static buildChildrenCols(currentScope: any, child: any) { |
||||
|
const cols = <any>[]; |
||||
|
currentScope.cols.forEach((col) => { |
||||
|
cols.push({ ...col, value: GridTools.formatValue(child, col) }); |
||||
|
}); |
||||
|
return cols; |
||||
|
} |
||||
|
// 格式化值
|
||||
|
private static formatValue(child: any, col: any) { |
||||
|
if (col.format && typeof col.format === 'function') { |
||||
|
try { |
||||
|
return col.format(child[col.name], child); |
||||
|
} catch (error) { |
||||
|
console.error('[w-grid]column `' + col.name + '` format error:', error); |
||||
|
} |
||||
|
} |
||||
|
return child[col.name]; |
||||
|
} |
||||
|
} |
@ -0,0 +1,226 @@ |
|||||
|
import { Tools, $t } from '@/platform'; |
||||
|
import { Constant } from './index'; |
||||
|
|
||||
|
/** |
||||
|
* 初始化工具类 |
||||
|
*/ |
||||
|
export class Init { |
||||
|
/** |
||||
|
* 创建 props 并设置默认值 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static createProps() { |
||||
|
return { |
||||
|
height: { type: Number, default: 0 }, // 表格高度
|
||||
|
title: { type: String, default: '' }, // 表格标题
|
||||
|
autoFetchData: { type: Boolean, default: true }, // 自动加载数据
|
||||
|
localMode: { type: Boolean, default: false }, // 本地模式,启用该模式后,除了查询数据依旧通过 autoFetchData 与 url 控制外,其他所有与后端交互的请求都失效,同时分页属性也失效
|
||||
|
selectMode: { type: String, default: Constant.SELECT_MODE.ROW }, // 选择模式
|
||||
|
dndMode: { type: String, default: undefined }, // 拖拽数据行排序的模式,默认不可拖拽,字符串`local`为本地排序,只根据配置的字段进行本地排序,不会将数据发送至后端,字符串`server`为远程排序,拖拽完成后会将数据发送至后端。
|
||||
|
dndOrderBy: { type: String, default: 'order' }, // 拖拽排序时根据哪个字段进行排序,默认为`order`字段
|
||||
|
pageable: { type: Boolean, default: true }, // 是否需要分页,当启用树形表格模式时该属性失效,树形表格不支持分页。
|
||||
|
configButton: { type: Boolean, default: true }, // 是否显示表格配置按钮
|
||||
|
dataUrl: { type: String, default: '' }, // 表格数据操作请求的URL前缀
|
||||
|
fetchDataUrl: { type: String, default: '' }, // 获取数据URL
|
||||
|
addDataUrl: { type: String, default: '' }, // 新增数据url
|
||||
|
editDataUrl: { type: String, default: '' }, // 修改数据url
|
||||
|
removeDataUrl: { type: String, default: '' }, // 删除数据url
|
||||
|
dense: { type: Boolean, default: undefined }, // 表格整体紧凑模式
|
||||
|
denseHeader: { type: Boolean, default: undefined }, // 表格列标题紧凑模式
|
||||
|
denseBody: { type: Boolean, default: undefined }, // 表格数据行紧凑模式
|
||||
|
denseBottom: { type: Boolean, default: undefined }, // 表格底部分页栏紧凑模式
|
||||
|
denseToolbar: { type: Boolean, default: true }, // 表格toolbar按钮栏紧凑模式
|
||||
|
sortNo: { type: Boolean, default: false }, // 是否显示排序号
|
||||
|
stickyNum: { type: Number, default: 0 }, // 从左侧开始冻结列数,复选框与排序列不计算在内,支持1-10列。
|
||||
|
checkboxSelection: { type: Boolean, default: true }, // checkbox选择模式,默认启用
|
||||
|
tickedField: { type: String, default: 'ticked' }, // 行数据中记录checkbox勾选状态的字段名
|
||||
|
selectedField: { type: String, default: 'selected' }, // 行数据中记录行点击选中状态的字段名
|
||||
|
tree: { type: Boolean, default: false }, // 树形表格模式,按照层级关系构建数据并以树形展现
|
||||
|
treeLazyLoad: { type: Boolean, default: false }, // 树型表格模式,懒加载
|
||||
|
treeIcon: { type: Function, default: undefined }, // 树形表格模式,图标函数,带有一个参数,行数据
|
||||
|
treeDefaultExpandAll: { type: Boolean, default: false }, // 树形表格模式,树形表格数据加载后是否全部展开
|
||||
|
treeTickStrategy: { type: String, default: 'leaf' }, // 树形表格模式,树勾选策略,strict:节点自己,leaf:包含子节点
|
||||
|
treeRelationship: { type: String, default: 'parent' }, // 树形表格模式的数据关系,包括:parent, children,当为parent时组件根据数据主键与数据外键构建树形数据,否则需要自己构建好树形数据放到children属性中。
|
||||
|
primaryKey: { type: String, default: 'id' }, // 数据主键(常规表格模式时,该字段可用作内置的编辑删除等功能对应的后端API入参,如:继承RestCrudController的update所需的入参。树形表格模式时,该字段为构建树数据的主键)
|
||||
|
foreignKey: { type: String, default: 'parent' }, // 数据外键(常规表格模式时,该字段暂时无用,将来可用作多个表格的数据关系字段。树形表格模式时,该字段为构建树数据的关系字段)
|
||||
|
refreshData: { type: Boolean, default: false }, // 新增、删除、修改成功后是否刷新数据列表,默认不刷新但是新增修改后台必须返回对应的行数据对象,删除则必须返回删除的记录集primaryKey集合。
|
||||
|
dbClickOperation: { type: String, default: 'view' }, // 默认的双击操作:可填写内置或自定义按钮name,执行的操作为按钮对应的click,固定值提供:expand(展开双击的行)、none(双击不执行任何动作)
|
||||
|
separator: { type: String, default: 'cell' }, // 表格分割线,支持:horizontal、vertical、cell、none
|
||||
|
hideHeader: { type: Boolean, default: false }, // 隐藏表头
|
||||
|
hideBottom: { type: Boolean, default: false }, // 隐藏底部
|
||||
|
advancedQuery: { type: Boolean, default: false }, // 高级查询
|
||||
|
groupMode: { type: String, default: undefined }, // 分组模式,支持:alone、merge
|
||||
|
groupStartOpen: { type: [String, Array, Function], default: Constant.GROUP_START_OPEN.ALL }, // alone分组模式下默认展开的组,字符串支持:all、first、none,数组可配置多个组名。
|
||||
|
groupByField: { |
||||
|
// 分组字段配置,当分组模式为 alone 时若配置的为数组,取数组第一个字段。
|
||||
|
type: [String, Array], |
||||
|
default: () => { |
||||
|
return []; |
||||
|
}, |
||||
|
}, |
||||
|
appendRows: { |
||||
|
// 表格追加行,添加到当前表格数据行尾,可添加多行,支持跨行跨列配置,用于添加合计或者额外信息。
|
||||
|
type: Array, |
||||
|
default: () => { |
||||
|
return []; |
||||
|
}, |
||||
|
}, |
||||
|
sortBy: { |
||||
|
type: Array, |
||||
|
default: () => { |
||||
|
return []; |
||||
|
}, |
||||
|
}, // 默认排序字段,示例:['userName', '-lastModifyDate'],其中“-”开头表示倒序。
|
||||
|
tickedRecord: { |
||||
|
// 默认勾选的记录,传入一个对象,包含:columnName(列名称,字符串类型,该属性不传默认根据数据主键查找)、data(需要勾选的数据,数组类型)
|
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
queryCriteria: { |
||||
|
// 查询条件,查询、翻页、刷新等操作都会带上的查询条件
|
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
columns: { |
||||
|
type: Array, |
||||
|
default: () => { |
||||
|
return []; |
||||
|
}, |
||||
|
}, |
||||
|
queryFormFields: { |
||||
|
type: Array, |
||||
|
default: () => { |
||||
|
return []; |
||||
|
}, |
||||
|
}, |
||||
|
queryFormColsNum: { |
||||
|
type: [Number, Object], |
||||
|
default: 0, |
||||
|
}, |
||||
|
queryFormRowNum: { type: Number, default: 1 }, |
||||
|
queryFormAttrs: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
toolbarActions: { |
||||
|
type: Array, |
||||
|
default: () => { |
||||
|
return []; |
||||
|
}, |
||||
|
}, |
||||
|
toolbarConfigure: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
pagination: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return { |
||||
|
reqPageStart: 0, |
||||
|
rowsPerPage: 10, |
||||
|
}; |
||||
|
}, |
||||
|
}, |
||||
|
editor: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return { |
||||
|
dialog: {}, |
||||
|
form: {}, |
||||
|
}; |
||||
|
}, |
||||
|
}, |
||||
|
viewer: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return { |
||||
|
drawer: {}, |
||||
|
panel: { |
||||
|
fields: [], |
||||
|
}, |
||||
|
}; |
||||
|
}, |
||||
|
}, |
||||
|
// 作为form中的组件使用----start
|
||||
|
showIf: { |
||||
|
type: [Boolean, Function], |
||||
|
default: () => { |
||||
|
return true; |
||||
|
}, |
||||
|
}, |
||||
|
form: { |
||||
|
type: Object, |
||||
|
default: undefined, |
||||
|
}, |
||||
|
// 作为form中的组件使用----end
|
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 列初始化,设置默认属性并将子列提取到同级 |
||||
|
* @param columns 所有列配置 |
||||
|
* @param props 表格props |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static initColumn(columns: Array<any>, props: any) { |
||||
|
const gridColumns = <any>[]; |
||||
|
if (columns && columns.length > 0) { |
||||
|
const sortable = true; |
||||
|
gridColumns.push({ name: '_sortNo_', align: 'center', label: $t('rownum'), field: '_sortNo_', showIf: props.sortNo }); |
||||
|
columns.forEach((item: any) => { |
||||
|
this.childrenHandler(item, gridColumns, sortable); |
||||
|
}); |
||||
|
return gridColumns; |
||||
|
} |
||||
|
return []; |
||||
|
} |
||||
|
|
||||
|
// 孩子列处理
|
||||
|
private static childrenHandler(item: any, gridColumns: any, sortable: boolean) { |
||||
|
if (item.columns && item.columns.length > 0) { |
||||
|
item.columns.forEach((column) => { |
||||
|
this.childrenHandler(column, gridColumns, sortable); |
||||
|
}); |
||||
|
} else { |
||||
|
this.columnStyle(item); |
||||
|
const col = { |
||||
|
...{ align: 'left', label: item.name, field: item.name, name: item.name, sortable: sortable, showIf: true }, |
||||
|
...item, |
||||
|
}; |
||||
|
if (Tools.isEmpty(col.name)) { |
||||
|
col.name = Tools.uuid(); |
||||
|
} |
||||
|
gridColumns.push(col); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 列样式处理
|
||||
|
private static columnStyle(item: any) { |
||||
|
let style = ''; |
||||
|
if (Tools.hasOwnProperty(item, 'style')) { |
||||
|
style = item.style; |
||||
|
} |
||||
|
if (Tools.hasOwnProperty(item, 'width')) { |
||||
|
if (typeof item.width === 'number') { |
||||
|
item.style = `min-width: ` + item.width + `px; width: ` + item.width + `px;max-width: ` + item.width + `px;` + style; |
||||
|
} else { |
||||
|
item.style = `min-width: ` + item.width + `; width: ` + item.width + `;max-width: ` + item.width + `;` + style; |
||||
|
} |
||||
|
delete item.width; |
||||
|
|
||||
|
if (Tools.hasOwnProperty(item, 'classes')) { |
||||
|
item.classes = item.classes + ' truncate'; |
||||
|
} else { |
||||
|
item.classes = 'truncate'; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,252 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { computed } from 'vue'; |
||||
|
import { Base } from '../Base'; |
||||
|
import { Constant } from '../constant/Constant'; |
||||
|
import { GridTools } from '../GridTools'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 表格计算属性管理器 |
||||
|
* 主要是将多个组件都会用到的计算属性进行统一管理,避免通过组件反复传递 |
||||
|
*/ |
||||
|
export class ComputedManager extends Base { |
||||
|
/** |
||||
|
* 当前列头是否紧凑 |
||||
|
*/ |
||||
|
denseHeader = computed(() => { |
||||
|
if (this.table?.configStore?.denseHeader) { |
||||
|
return true; |
||||
|
} else if (this.table?.configStore.dense !== false) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 当前内容是否紧凑 |
||||
|
*/ |
||||
|
denseBody = computed(() => { |
||||
|
if (this.table?.configStore?.denseBody) { |
||||
|
return true; |
||||
|
} else if (this.table?.configStore.dense !== false) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 当前分页工具栏是否紧凑 |
||||
|
*/ |
||||
|
denseBottom = computed(() => { |
||||
|
if (this.table?.configStore?.denseBottom) { |
||||
|
return true; |
||||
|
} else if (this.table?.configStore.dense !== false) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 当前表格显示的所有列名 |
||||
|
*/ |
||||
|
displayColumns = computed(() => { |
||||
|
const visibleColumns: string[] = []; |
||||
|
this.table?.columns.forEach((item) => { |
||||
|
if (!this.props.tree || (this.props.tree && item.name !== Constant.FIELD_NAMES.SORT_NO)) { |
||||
|
if (Tools.isEmpty(item.showIf)) { |
||||
|
visibleColumns.push(item.name); |
||||
|
} else if (typeof item.showIf === 'boolean' && item.showIf) { |
||||
|
visibleColumns.push(item.name); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
return visibleColumns; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 合并分组时分组字段 |
||||
|
*/ |
||||
|
mergeGroupByField = computed(() => { |
||||
|
if (typeof this.props.groupByField === 'string') { |
||||
|
[this.props.groupByField]; |
||||
|
} |
||||
|
return this.props.groupByField; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 计算额外拓展的列数(排序号、checkbox复选框) |
||||
|
*/ |
||||
|
extColumnNum = computed(() => { |
||||
|
let num = 0; |
||||
|
if (this.props.tree) { |
||||
|
return num; |
||||
|
} |
||||
|
if (this.table?.configStore.showSortNo) { |
||||
|
num += 1; |
||||
|
} |
||||
|
if (this.table?.configStore.useCheckboxSelection) { |
||||
|
num += 1; |
||||
|
} |
||||
|
return num; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 计算当前表格显示的列数(包括拓展的排序号、checkbox复选框) |
||||
|
*/ |
||||
|
displayColumnNum = computed(() => { |
||||
|
let colspan = this.extColumnNum.value; |
||||
|
colspan += this.displayColumns.value.length; |
||||
|
return colspan; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 无数据块的样式 |
||||
|
*/ |
||||
|
noDataStyle = computed(() => { |
||||
|
let style = {}; |
||||
|
if (this.props.hideHeader) { |
||||
|
style = { |
||||
|
height: this.table?.store.location.hideHeaderNoDataHeight + 'px', |
||||
|
display: 'flex', |
||||
|
'align-items': 'center', |
||||
|
'justify-content': 'center', |
||||
|
}; |
||||
|
} else { |
||||
|
let availableHeight = 0; |
||||
|
let otherHeight = 0; |
||||
|
let otherHeight2 = 0; |
||||
|
|
||||
|
const parentHtmlElement = this.instance?.getHtmlElement()?.parentElement; |
||||
|
if (parentHtmlElement) { |
||||
|
availableHeight = Math.floor(parentHtmlElement.clientHeight); |
||||
|
} |
||||
|
|
||||
|
otherHeight += this.table?.store.location.topHeight || 0; |
||||
|
otherHeight += this.table?.store.location.bottomHeight || 0; |
||||
|
otherHeight += this.table?.store.spaceHeight || 0; |
||||
|
|
||||
|
otherHeight2 += this.table?.store.location.titleTotalHeight; |
||||
|
otherHeight2 += this.table?.store.location.middleScrollWidth - this.table?.store.location.middleWidth > 0 ? 15 : 0; |
||||
|
|
||||
|
availableHeight -= otherHeight + otherHeight2; |
||||
|
availableHeight -= 2; //无数据增加的title行,下边框所占边框
|
||||
|
|
||||
|
style = { |
||||
|
height: this.props.height > 0 ? this.props.height - otherHeight2 - 1 + 'px' : availableHeight > 0 ? availableHeight + 'px' : '0px', |
||||
|
}; |
||||
|
} |
||||
|
if (this.table?.store.resizeFlag) { |
||||
|
return style; |
||||
|
} |
||||
|
return style; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 内置按钮入参 |
||||
|
*/ |
||||
|
actionArgs = computed(() => { |
||||
|
return { |
||||
|
selected: this.tools?.apiFM.getData.getSelectedRow(), |
||||
|
selecteds: this.tools?.apiFM.getData.getSelectedRows(), |
||||
|
ticked: this.tools?.apiFM.getData.getTickedRow(), |
||||
|
tickeds: this.tools?.apiFM.getData.getTickedRows(), |
||||
|
grid: this.instance, |
||||
|
selectedColName: this.table.store.cellSelected?.colName, |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 表格样式 |
||||
|
*/ |
||||
|
tableClass = computed(() => { |
||||
|
const classArr = ['sticky-header-column-table', 'w-grid']; |
||||
|
if (this.table.configStore.stickyNum && this.table.configStore.stickyNum > 0) { |
||||
|
if (this.table.componentRef.getHeaderRef().getColumnTitleState()?.columnTitleRowNum > 1) { |
||||
|
// 存在多行列头
|
||||
|
const stickyColumn = this.table.originalColumns.filter((item, index) => { |
||||
|
return index < this.table.configStore.stickyNum; |
||||
|
}); |
||||
|
let tdNum = this.extColumnNum.value; |
||||
|
stickyColumn.forEach((item: any, index: number) => { |
||||
|
tdNum += this.table.componentRef.getHeaderRef().getMoreColumnTitleMap()?.get(item.name)?.colspan; |
||||
|
}); |
||||
|
// 处理表格数据列锁定
|
||||
|
if (tdNum > 0) { |
||||
|
for (let n = 1; n <= tdNum; n++) { |
||||
|
classArr.push('sticky-header-column-table-td-' + n); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
// 不存在多行列头
|
||||
|
if (this.extColumnNum.value === 2) { |
||||
|
classArr.push('sticky-header-column-table-tr-1' + '-' + 1); |
||||
|
classArr.push('sticky-header-column-table-tr-1' + '-' + 2); |
||||
|
classArr.push('sticky-header-column-table-td-' + 1); |
||||
|
classArr.push('sticky-header-column-table-td-' + 2); |
||||
|
} else if (this.extColumnNum.value === 1) { |
||||
|
classArr.push('sticky-header-column-table-tr-1' + '-' + 1); |
||||
|
classArr.push('sticky-header-column-table-td-' + 1); |
||||
|
} |
||||
|
for (let i = 1; i <= this.table.configStore.stickyNum; i++) { |
||||
|
classArr.push('sticky-header-column-table-tr-1' + '-' + (i + this.extColumnNum.value)); |
||||
|
classArr.push('sticky-header-column-table-td-' + (i + this.extColumnNum.value)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return classArr; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 表格高度样式 |
||||
|
*/ |
||||
|
tableHeight = computed(() => { |
||||
|
let availableHeight = 0; |
||||
|
const parentHtmlElement = this.instance?.getHtmlElement()?.parentElement; |
||||
|
if (parentHtmlElement) { |
||||
|
availableHeight = Math.floor(parentHtmlElement.clientHeight); |
||||
|
} |
||||
|
|
||||
|
availableHeight -= this.table?.store.location.topHeight || 0; |
||||
|
availableHeight -= this.table?.store.location.bottomHeight || 0; |
||||
|
availableHeight -= this.table?.store.spaceHeight || 0; |
||||
|
|
||||
|
const style = { |
||||
|
height: this.props.height > 0 ? this.props.height + 'px' : availableHeight > 0 ? availableHeight + 'px' : '0px', |
||||
|
}; |
||||
|
if (this.table?.store.resizeFlag) { |
||||
|
return style; |
||||
|
} |
||||
|
return style; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 用户可选分页大小配置 |
||||
|
*/ |
||||
|
rowsPerPageOptions = computed(() => { |
||||
|
if (this.props.pageable && !this.props.localMode && !this.props.tree && this.table.store.location.middleWidth > 600) { |
||||
|
return this.table.store.pagination.rowsPerPageOptions; |
||||
|
} |
||||
|
return []; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 表格作为form中的字段使用时是否显示 |
||||
|
*/ |
||||
|
showIf = computed(() => { |
||||
|
if (typeof this.props.showIf === 'function') { |
||||
|
return this.props.showIf({ |
||||
|
value: null, |
||||
|
form: this.props.form, |
||||
|
}); |
||||
|
} else { |
||||
|
return this.props.showIf; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
setInstance(instance: any, tools?: GridTools): void { |
||||
|
this.instance = instance; |
||||
|
this.tools = tools; |
||||
|
} |
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
import { SelectMode } from './src/SelectMode'; |
||||
|
import { DndMode } from './src/DndMode'; |
||||
|
import { GroupMode } from './src/GroupMode'; |
||||
|
import { GroupStartOpen } from './src/GroupStartOpen'; |
||||
|
|
||||
|
import { FormStatus } from './src/FormStatus'; |
||||
|
import { EditStatus } from './src/EditStatus'; |
||||
|
|
||||
|
import { FieldNames } from './src/FieldNames'; |
||||
|
import { CriteriaOperator } from './src/CriteriaOperator'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 常量 |
||||
|
*/ |
||||
|
export class Constant { |
||||
|
/** |
||||
|
* 选择模式 |
||||
|
*/ |
||||
|
static SELECT_MODE = SelectMode; |
||||
|
|
||||
|
/** |
||||
|
* 拖拽排序模式 |
||||
|
*/ |
||||
|
static DND_MODE = DndMode; |
||||
|
/** |
||||
|
* 拖拽过程中透明背景图 |
||||
|
*/ |
||||
|
static DND_BG_IMAGE = `data:image/svg+xml,%3Csvg %3E%3Cpath /%3E%3C/svg%3E`; |
||||
|
|
||||
|
/** |
||||
|
* 数据分组模式 |
||||
|
*/ |
||||
|
static GROUP_MODE = GroupMode; |
||||
|
/** |
||||
|
* 独立分组模式下默认展开的记录 |
||||
|
*/ |
||||
|
static GROUP_START_OPEN = GroupStartOpen; |
||||
|
|
||||
|
/** |
||||
|
* Form表单状态 |
||||
|
*/ |
||||
|
static FORM_STATUS = FormStatus; |
||||
|
|
||||
|
/** |
||||
|
* 内联编辑状态 |
||||
|
*/ |
||||
|
static EDIT_STATUS = EditStatus; |
||||
|
|
||||
|
/** |
||||
|
* 行数据字段名称定义 |
||||
|
*/ |
||||
|
static FIELD_NAMES = FieldNames; |
||||
|
|
||||
|
/** |
||||
|
* criteria查询支持的操作 |
||||
|
*/ |
||||
|
static CRITERIA_OPERATOR = CriteriaOperator; |
||||
|
} |
@ -0,0 +1,85 @@ |
|||||
|
/** |
||||
|
* criteria查询支持的操作 |
||||
|
*/ |
||||
|
export class CriteriaOperator { |
||||
|
/** |
||||
|
* value is null or value='' |
||||
|
*/ |
||||
|
static isBlank = 'isBlank'; |
||||
|
/** |
||||
|
* value is not null && value<>'' |
||||
|
*/ |
||||
|
static notBlank = 'notBlank'; |
||||
|
/** |
||||
|
* value is null |
||||
|
*/ |
||||
|
static isNull = 'isNull'; |
||||
|
/** |
||||
|
* value is not null |
||||
|
*/ |
||||
|
static notNull = 'notNull'; |
||||
|
/** |
||||
|
* = |
||||
|
*/ |
||||
|
static equals = 'equals'; |
||||
|
/** |
||||
|
* <> |
||||
|
*/ |
||||
|
static notEqual = 'notEqual'; |
||||
|
/** |
||||
|
* > |
||||
|
*/ |
||||
|
static greaterThan = 'greaterThan'; |
||||
|
/** |
||||
|
* >= |
||||
|
*/ |
||||
|
static greaterOrEqual = 'greaterOrEqual'; |
||||
|
/** |
||||
|
* < |
||||
|
*/ |
||||
|
static lessThan = 'lessThan'; |
||||
|
/** |
||||
|
* <= |
||||
|
*/ |
||||
|
static lessOrEqual = 'lessOrEqual'; |
||||
|
/** |
||||
|
* like %xxx% |
||||
|
*/ |
||||
|
static contains = 'contains'; |
||||
|
/** |
||||
|
* not like %xxx% |
||||
|
*/ |
||||
|
static notContains = 'notContains'; |
||||
|
/** |
||||
|
* like xxx% |
||||
|
*/ |
||||
|
static startsWith = 'startsWith'; |
||||
|
/** |
||||
|
* not like xxx% |
||||
|
*/ |
||||
|
static notStartsWith = 'notStartsWith'; |
||||
|
/** |
||||
|
* like %xxx |
||||
|
*/ |
||||
|
static endsWith = 'endsWith'; |
||||
|
/** |
||||
|
* not like %xxx |
||||
|
*/ |
||||
|
static notEndsWith = 'notEndsWith'; |
||||
|
/** |
||||
|
* min<x and x<max |
||||
|
*/ |
||||
|
static between = 'between'; |
||||
|
/** |
||||
|
* min<=x and x<=max |
||||
|
*/ |
||||
|
static betweenInclusive = 'betweenInclusive'; |
||||
|
/** |
||||
|
* in () |
||||
|
*/ |
||||
|
static inSet = 'inSet'; |
||||
|
/** |
||||
|
* not in () |
||||
|
*/ |
||||
|
static notInSet = 'notInSet'; |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* 拖拽排序模式 |
||||
|
*/ |
||||
|
export class DndMode { |
||||
|
/** |
||||
|
* 本地模式 |
||||
|
*/ |
||||
|
public static LOCAL = 'local'; |
||||
|
/** |
||||
|
* 服务端模式 |
||||
|
*/ |
||||
|
public static SERVER = 'server'; |
||||
|
|
||||
|
/** |
||||
|
* 获得所有拖拽排序模式对象 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static getAll() { |
||||
|
return { |
||||
|
[DndMode.LOCAL]: DndMode.LOCAL, |
||||
|
[DndMode.SERVER]: DndMode.SERVER, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
/** |
||||
|
* 内联编辑状态 |
||||
|
*/ |
||||
|
export class EditStatus { |
||||
|
/** |
||||
|
* 不在编辑状态 |
||||
|
*/ |
||||
|
public static NONE = 'none'; |
||||
|
/** |
||||
|
* 单元格编辑状态 |
||||
|
*/ |
||||
|
public static CELL = 'cellEdit'; |
||||
|
/** |
||||
|
* 行编辑状态 |
||||
|
*/ |
||||
|
public static ROW = 'rowEdit'; |
||||
|
/** |
||||
|
* 所有行编辑状态 |
||||
|
*/ |
||||
|
public static ROWS = 'rowsEdit'; |
||||
|
|
||||
|
/** |
||||
|
* 获得所有内联编辑状态对象 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static getAll() { |
||||
|
return { |
||||
|
[EditStatus.NONE]: EditStatus.NONE, |
||||
|
[EditStatus.CELL]: EditStatus.CELL, |
||||
|
[EditStatus.ROW]: EditStatus.ROW, |
||||
|
[EditStatus.ROWS]: EditStatus.ROWS, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,63 @@ |
|||||
|
/** |
||||
|
* 行数据字段名称定义 |
||||
|
*/ |
||||
|
export class FieldNames { |
||||
|
/** |
||||
|
* 前端主键 |
||||
|
*/ |
||||
|
public static ROW_KEY = '_rowKey_'; |
||||
|
|
||||
|
/** |
||||
|
* 排序号 |
||||
|
*/ |
||||
|
public static SORT_NO = '_sortNo_'; |
||||
|
|
||||
|
/** |
||||
|
* 树型表格行数据是否展开状态 |
||||
|
*/ |
||||
|
public static EXPAND = 'expand'; |
||||
|
|
||||
|
/** |
||||
|
* 树型表格行数据用于处理父节点勾选状态的属性(记录自身勾选数量,要么为0要么为1) |
||||
|
*/ |
||||
|
public static TICKED_COUNT = '_tickedCount'; |
||||
|
|
||||
|
/** |
||||
|
* 树型表格行数据用于处理父节点勾选状态的属性(记录子节点勾选数量) |
||||
|
*/ |
||||
|
public static CHILDREN_TICKED_COUNT = '_childrenTickedCount'; |
||||
|
|
||||
|
/** |
||||
|
* 行数据被内联编辑前的值 |
||||
|
*/ |
||||
|
public static ROW_OLD_VALUE = '_rowOldValue'; |
||||
|
|
||||
|
/** |
||||
|
* 行下标 |
||||
|
*/ |
||||
|
public static ROW_INDEX = '_rowIndex'; |
||||
|
|
||||
|
/** |
||||
|
* 懒加载后仍然无数据 |
||||
|
*/ |
||||
|
public static LAZYLOAD_NO_CHILDREN = '_lazyloadNoChildren'; |
||||
|
|
||||
|
/** |
||||
|
* 获得行数据中拓展的字段属性名 |
||||
|
* @param props |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static getExtraFields(props: object) { |
||||
|
return { |
||||
|
rowKey: FieldNames.ROW_KEY, |
||||
|
ticked: props['tickedField'], // 行数据勾选状态
|
||||
|
selected: props['selectedField'], // 行数据选中状态
|
||||
|
expand: FieldNames.EXPAND, |
||||
|
tickedCount: FieldNames.TICKED_COUNT, |
||||
|
childrenTickedCount: FieldNames.CHILDREN_TICKED_COUNT, |
||||
|
rowOldValue: FieldNames.ROW_OLD_VALUE, |
||||
|
rowIndex: FieldNames.ROW_INDEX, |
||||
|
lazyloadNoChildren: FieldNames.LAZYLOAD_NO_CHILDREN, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
/** |
||||
|
* Form表单状态 |
||||
|
*/ |
||||
|
export class FormStatus { |
||||
|
/** |
||||
|
* 新增 |
||||
|
*/ |
||||
|
public static ADD = 'add'; |
||||
|
/** |
||||
|
* 编辑 |
||||
|
*/ |
||||
|
public static EDIT = 'edit'; |
||||
|
/** |
||||
|
* 复制 |
||||
|
*/ |
||||
|
public static CLONE = 'clone'; |
||||
|
/** |
||||
|
* 新增顶级节点 |
||||
|
*/ |
||||
|
public static ADD_TOP = 'addTop'; |
||||
|
/** |
||||
|
* 新增子节点 |
||||
|
*/ |
||||
|
public static ADD_CHILD = 'addChild'; |
||||
|
|
||||
|
/** |
||||
|
* 获得所有Form表单状态对象 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static getAll() { |
||||
|
return { |
||||
|
[FormStatus.ADD]: FormStatus.ADD, |
||||
|
[FormStatus.EDIT]: FormStatus.EDIT, |
||||
|
[FormStatus.CLONE]: FormStatus.CLONE, |
||||
|
[FormStatus.ADD_TOP]: FormStatus.ADD_TOP, |
||||
|
[FormStatus.ADD_CHILD]: FormStatus.ADD_CHILD, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* 数据分组模式 |
||||
|
*/ |
||||
|
export class GroupMode { |
||||
|
/** |
||||
|
* 独立行分组 |
||||
|
*/ |
||||
|
public static ALONE = 'alone'; |
||||
|
/** |
||||
|
* 合并行分组 |
||||
|
*/ |
||||
|
public static MERGE = 'merge'; |
||||
|
|
||||
|
/** |
||||
|
* 获得所有数据分组模式对象 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static getAll() { |
||||
|
return { |
||||
|
[GroupMode.ALONE]: GroupMode.ALONE, |
||||
|
[GroupMode.MERGE]: GroupMode.MERGE, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
/** |
||||
|
* 独立分组模式下默认展开的行 |
||||
|
*/ |
||||
|
export class GroupStartOpen { |
||||
|
/** |
||||
|
* 全部展开 |
||||
|
*/ |
||||
|
public static ALL = 'all'; |
||||
|
/** |
||||
|
* 第一条 |
||||
|
*/ |
||||
|
public static FIRST = 'first'; |
||||
|
/** |
||||
|
* 不展开 |
||||
|
*/ |
||||
|
public static NONE = 'none'; |
||||
|
|
||||
|
/** |
||||
|
* 获得所有数据分组模式对象 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static getAll() { |
||||
|
return { |
||||
|
[GroupStartOpen.ALL]: GroupStartOpen.ALL, |
||||
|
[GroupStartOpen.FIRST]: GroupStartOpen.FIRST, |
||||
|
[GroupStartOpen.NONE]: GroupStartOpen.NONE, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
/** |
||||
|
* 选择模式 |
||||
|
*/ |
||||
|
export class SelectMode { |
||||
|
/** |
||||
|
* 不允许选择 |
||||
|
*/ |
||||
|
public static NONE = 'none'; |
||||
|
/** |
||||
|
* 单行选择 |
||||
|
*/ |
||||
|
public static SINGLE_ROW = 'singleRow'; |
||||
|
/** |
||||
|
* 行选择 |
||||
|
*/ |
||||
|
public static ROW = 'row'; |
||||
|
/** |
||||
|
* 单元格选择 |
||||
|
*/ |
||||
|
public static CELL = 'cell'; |
||||
|
|
||||
|
/** |
||||
|
* 获得所有选择模式对象 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static getAll() { |
||||
|
return { |
||||
|
[SelectMode.NONE]: SelectMode.NONE, |
||||
|
[SelectMode.SINGLE_ROW]: SelectMode.SINGLE_ROW, |
||||
|
[SelectMode.ROW]: SelectMode.ROW, |
||||
|
[SelectMode.CELL]: SelectMode.CELL, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,175 @@ |
|||||
|
import type { PropsType } from '../types/PropsType'; |
||||
|
|
||||
|
import { RowClick } from './src/RowClick'; |
||||
|
import { RowDbClick } from './src/RowDbClick'; |
||||
|
import { AfterDragAndDrop } from './src/AfterDragAndDrop'; |
||||
|
import { UpdateTicked } from './src/UpdateTicked'; |
||||
|
import { UpdateTickeds } from './src/UpdateTickeds'; |
||||
|
import { BeforeRequestData } from './src/BeforeRequestData'; |
||||
|
import { AfterRequestData } from './src/AfterRequestData'; |
||||
|
import { BeforeEditorDataSubmit } from './src/BeforeEditorDataSubmit'; |
||||
|
import { AfterEditorDataSubmit } from './src/AfterEditorDataSubmit'; |
||||
|
import { BeforeRemove } from './src/BeforeRemove'; |
||||
|
import { AfterRemove } from './src/AfterRemove'; |
||||
|
import { AfterEditorOpen } from './src/AfterEditorOpen'; |
||||
|
import { GridTools } from '../GridTools'; |
||||
|
import { TableType } from '../types/TableType'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 表格事件管理器 |
||||
|
*/ |
||||
|
export class EventManager { |
||||
|
private _rowClick: RowClick; |
||||
|
private _rowDbClick: RowDbClick; |
||||
|
private _aftetDnd: AfterDragAndDrop; |
||||
|
private _updateTicked: UpdateTicked; |
||||
|
private _updateTickeds: UpdateTickeds; |
||||
|
private _beforeRequestData: BeforeRequestData; |
||||
|
private _afterRequestData: AfterRequestData; |
||||
|
private _beforeEditorDataSubmit: BeforeEditorDataSubmit; |
||||
|
private _afterEditorDataSubmit: AfterEditorDataSubmit; |
||||
|
private _beforeRemove: BeforeRemove; |
||||
|
private _afterRemove: AfterRemove; |
||||
|
private _afterEditorOpen: AfterEditorOpen; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
this._rowClick = new RowClick(props, table); |
||||
|
this._rowDbClick = new RowDbClick(props, table); |
||||
|
this._aftetDnd = new AfterDragAndDrop(props, table); |
||||
|
this._updateTicked = new UpdateTicked(props, table); |
||||
|
this._updateTickeds = new UpdateTickeds(props, table); |
||||
|
this._beforeRequestData = new BeforeRequestData(props, table); |
||||
|
this._afterRequestData = new AfterRequestData(props, table); |
||||
|
this._beforeEditorDataSubmit = new BeforeEditorDataSubmit(props, table); |
||||
|
this._afterEditorDataSubmit = new AfterEditorDataSubmit(props, table); |
||||
|
this._beforeRemove = new BeforeRemove(props, table); |
||||
|
this._afterRemove = new AfterRemove(props, table); |
||||
|
this._afterEditorOpen = new AfterEditorOpen(props, table); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 行点击事件 |
||||
|
* @param evt html事件对象 |
||||
|
* @param row 行对象 |
||||
|
* @param index 行下标 |
||||
|
*/ |
||||
|
public rowClick(evt: any, row: any, index: number) { |
||||
|
this._rowClick.execute(evt, row, index); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 行双击事件 |
||||
|
* @param evt html事件对象 |
||||
|
* @param row 行对象 |
||||
|
* @param index 行下标 |
||||
|
*/ |
||||
|
public rowDbClick(evt: any, row: any, index: number) { |
||||
|
this._rowDbClick.execute(evt, row, index); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 行拖拽排序后触发事件 |
||||
|
* @param updateData 排序更改的所有记录 |
||||
|
*/ |
||||
|
public afterDragAndDrop(updateData: any) { |
||||
|
this._aftetDnd.execute(updateData); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 行数据checkbox勾选状态改变事件 |
||||
|
* @param evt html事件对象 |
||||
|
* @param row 更改勾选状态的行对象 |
||||
|
*/ |
||||
|
public updateTicked(evt: any, row: any) { |
||||
|
this._updateTicked.execute(evt, row); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 列头checkbox全部勾选状态改变事件 |
||||
|
* @param value 更改的状态 |
||||
|
*/ |
||||
|
public updateTickeds(value: boolean | null) { |
||||
|
this._updateTickeds.execute(value); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 表格请求数据前触发事件 |
||||
|
* @param requestParams 请求参数 |
||||
|
* @returns 被修改过的或原始请求参数 |
||||
|
*/ |
||||
|
public async beforeRequestData(requestParams: any) { |
||||
|
return await this._beforeRequestData.execute(requestParams); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 表格请求数据完成后触发事件 |
||||
|
* @param rows 请求成功数据 |
||||
|
*/ |
||||
|
public afterRequestData(rows: any) { |
||||
|
this._afterRequestData.execute(rows); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 内置编辑窗口提交数据前触发事件 |
||||
|
* @param data 提交数据 |
||||
|
* @returns { |
||||
|
* data: data, //原始数据或被修改后的数据
|
||||
|
* submit: true,//是否提交
|
||||
|
* closeDialog: true,//提交成功后是否关闭窗口
|
||||
|
* } |
||||
|
*/ |
||||
|
public beforeEditorDataSubmit(data: any): any { |
||||
|
return this._beforeEditorDataSubmit.execute(data); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 内置编辑窗口提交数据完成后触发事件 |
||||
|
* @param data 提交成功数据 |
||||
|
*/ |
||||
|
public afterEditorDataSubmit(data: any) { |
||||
|
this._afterEditorDataSubmit.execute(data); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 内置删除功能提交前触发事件 |
||||
|
* @param ids 提交的id集合 |
||||
|
* @returns { |
||||
|
* submit: true,//是否提交
|
||||
|
* ids: <any> [],//被修改后的id集合
|
||||
|
* } |
||||
|
*/ |
||||
|
public async beforeRemove(ids: any) { |
||||
|
return await this._beforeRemove.execute(ids); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 内置删除功能提交完成后触发事件 |
||||
|
* @param ids 提交成功的id集合 |
||||
|
*/ |
||||
|
public afterRemove(ids: any) { |
||||
|
this._afterRemove.execute(ids); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 内置编辑窗口打开后触发事件 |
||||
|
* @param data |
||||
|
*/ |
||||
|
public afterEditorOpen(data: any) { |
||||
|
this._afterEditorOpen.execute(data); |
||||
|
} |
||||
|
|
||||
|
setInstance(instance: any, tools: GridTools) { |
||||
|
this._rowClick.setInstance(instance, tools); |
||||
|
this._rowDbClick.setInstance(instance, tools); |
||||
|
this._aftetDnd.setInstance(instance, tools); |
||||
|
this._updateTicked.setInstance(instance, tools); |
||||
|
this._updateTickeds.setInstance(instance, tools); |
||||
|
this._beforeRequestData.setInstance(instance, tools); |
||||
|
this._afterRequestData.setInstance(instance, tools); |
||||
|
this._beforeEditorDataSubmit.setInstance(instance, tools); |
||||
|
this._afterEditorDataSubmit.setInstance(instance, tools); |
||||
|
this._beforeRemove.setInstance(instance, tools); |
||||
|
this._afterRemove.setInstance(instance, tools); |
||||
|
this._afterEditorOpen.setInstance(instance, tools); |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 行拖拽排序后触发事件 |
||||
|
*/ |
||||
|
export class AfterDragAndDrop extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
static NAME = 'afterDragAndDrop'; |
||||
|
|
||||
|
public execute(updateData: any) { |
||||
|
this.instance.emit(AfterDragAndDrop.NAME, { grid: this.instance, data: updateData }); |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 内置编辑窗口数据提交完成后触发事件 |
||||
|
*/ |
||||
|
export class AfterEditorDataSubmit extends Base { |
||||
|
static NAME = 'afterEditorDataSubmit'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(data: any) { |
||||
|
this.instance.emit(AfterEditorDataSubmit.NAME, { grid: this.instance, data }); |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 内置编辑窗口打开之后触发事件 |
||||
|
*/ |
||||
|
export class AfterEditorOpen extends Base { |
||||
|
static NAME = 'afterEditorOpen'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(data: any) { |
||||
|
this.instance.emit(AfterEditorOpen.NAME, { grid: this.instance, data }); |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 内置删除功能提交完成后触发事件 |
||||
|
*/ |
||||
|
export class AfterRemove extends Base { |
||||
|
static NAME = 'afterRemove'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(ids: any) { |
||||
|
this.instance.emit(AfterRemove.NAME, { grid: this.instance, ids }); |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 数据请求完成后触发事件 |
||||
|
*/ |
||||
|
export class AfterRequestData extends Base { |
||||
|
static NAME = 'afterRequestData'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(rows: any) { |
||||
|
this.instance.emit(AfterRequestData.NAME, { grid: this.instance, rows }); |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 内置编辑窗口数据提交前触发事件 |
||||
|
*/ |
||||
|
export class BeforeEditorDataSubmit extends Base { |
||||
|
static NAME = 'beforeEditorDataSubmit'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(data: any): any { |
||||
|
const result = { |
||||
|
data: data, |
||||
|
submit: true, |
||||
|
closeDialog: true, |
||||
|
}; |
||||
|
this.instance.emit(BeforeEditorDataSubmit.NAME, { |
||||
|
grid: this.instance, |
||||
|
data: data, |
||||
|
callback: (_data: any, closeFlag: boolean = true) => { |
||||
|
if (typeof _data === 'boolean' && _data === false) { |
||||
|
result.submit = false; |
||||
|
} else { |
||||
|
result.data = _data; |
||||
|
} |
||||
|
result.closeDialog = closeFlag; |
||||
|
}, |
||||
|
}); |
||||
|
return result; |
||||
|
} |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 内置删除功能提交前触发事件 |
||||
|
*/ |
||||
|
export class BeforeRemove extends Base { |
||||
|
static NAME = 'beforeRemove'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public async execute(ids: any) { |
||||
|
const result = { |
||||
|
submit: true, |
||||
|
ids: <any>[], |
||||
|
}; |
||||
|
await this.instance.emit(BeforeRemove.NAME, { |
||||
|
grid: this.instance, |
||||
|
ids: ids, |
||||
|
callback: (_ids: any) => { |
||||
|
if (_ids && Array.isArray(_ids)) { |
||||
|
result.ids.push(..._ids); |
||||
|
} else { |
||||
|
result.submit = false; |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
return result; |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
/** |
||||
|
* w-grid 表格请求数据前事件 |
||||
|
*/ |
||||
|
export class BeforeRequestData extends Base { |
||||
|
static NAME = 'beforeRequestData'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public async execute(requestParams: any) { |
||||
|
let result = undefined; |
||||
|
await this.instance.emit(BeforeRequestData.NAME, { |
||||
|
grid: this.instance, |
||||
|
data: requestParams, |
||||
|
callback: (_requestParams: URLSearchParams | any) => { |
||||
|
result = _requestParams; |
||||
|
}, |
||||
|
}); |
||||
|
return result; |
||||
|
} |
||||
|
} |
@ -0,0 +1,61 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { Constant } from '../../constant/Constant'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 行单击事件 |
||||
|
*/ |
||||
|
export class RowClick extends Base { |
||||
|
static NAME = 'rowClick'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
this.handleRowClick = this.handleRowClick.bind(this); |
||||
|
this.processRowSelection = this.processRowSelection.bind(this); |
||||
|
this.clickExitInlineEdit = this.clickExitInlineEdit.bind(this); |
||||
|
this.execClick = this.execClick.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(evt: any, row: any, index: number) { |
||||
|
if (!row) { |
||||
|
return; |
||||
|
} |
||||
|
const selected = this.instance.getSelectedRow(); |
||||
|
this.handleRowClick(evt, row, index, selected); |
||||
|
} |
||||
|
private handleRowClick(evt: any, row: any, index: number, selected: any) { |
||||
|
if (this.clickExitInlineEdit(row, selected)) { |
||||
|
this.tools?.editFM.exitInlineEdit(); |
||||
|
return; |
||||
|
} |
||||
|
if (this.execClick()) { |
||||
|
this.processRowSelection(evt, row, index); |
||||
|
} |
||||
|
} |
||||
|
private processRowSelection(evt: any, row: any, index: number) { |
||||
|
if (!evt.ctrlKey) { |
||||
|
this.instance.clearSelected(); |
||||
|
if (!this.props.tree) { |
||||
|
this.instance.clearTicked(); |
||||
|
} |
||||
|
} |
||||
|
row[this.props.selectedField] = true; |
||||
|
if (!this.props.tree) { |
||||
|
row[this.props.tickedField] = true; |
||||
|
this.tools?.opFM.resetHeaderCheckbox(); |
||||
|
} |
||||
|
this.instance.emit(RowClick.NAME, { grid: this.instance, evt, row, index }); |
||||
|
} |
||||
|
private clickExitInlineEdit(row: any, selected: any) { |
||||
|
return ( |
||||
|
this.props.localMode && |
||||
|
this.table?.store.inlineEditStatus !== Constant.EDIT_STATUS.NONE && |
||||
|
this.props.selectMode === Constant.SELECT_MODE.ROW && |
||||
|
row[Constant.FIELD_NAMES.ROW_KEY] !== selected[Constant.FIELD_NAMES.ROW_KEY] |
||||
|
); |
||||
|
} |
||||
|
private execClick() { |
||||
|
return this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.NONE && this.props.selectMode !== Constant.SELECT_MODE.NONE; |
||||
|
} |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
import { Constant } from '../../constant/Constant'; |
||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 行双击事件 |
||||
|
*/ |
||||
|
export class RowDbClick extends Base { |
||||
|
static NAME = 'rowDbClick'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(evt: any, row: any, index: number) { |
||||
|
if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.NONE && this.props.selectMode !== Constant.SELECT_MODE.NONE) { |
||||
|
if (this.props.onRowDbClick) { |
||||
|
this.instance.emit(RowDbClick.NAME, { grid: this.instance, evt, row, index }); |
||||
|
} else if (this.props.dbClickOperation === 'view') { |
||||
|
this.tools?.apiFM.operator.viewData(); |
||||
|
} else if (this.props.dbClickOperation !== 'none') { |
||||
|
this.table.componentRef.getTopRef()?.dbClickOperation(row); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
import { Constant } from '../../constant/Constant'; |
||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 行数据checkbox勾选状态改变事件 |
||||
|
*/ |
||||
|
export class UpdateTicked extends Base { |
||||
|
static NAME = 'updateTicked'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(evt: Event, row: any) { |
||||
|
const selectedField = this.props.selectedField; |
||||
|
const tickedField = this.props.tickedField; |
||||
|
if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.NONE) { |
||||
|
row[selectedField] = row[tickedField]; |
||||
|
this.tools?.opFM.resetHeaderCheckbox(); |
||||
|
if (!row[tickedField]) { |
||||
|
// 取消选中时将选中的单元格也清空
|
||||
|
this.table.store.cellSelected = {}; |
||||
|
} |
||||
|
if (this.props.onUpdateTicked) { |
||||
|
this.instance.emit(UpdateTicked.NAME, { grid: this.instance, evt, row }); |
||||
|
} |
||||
|
} else { |
||||
|
row[tickedField] = !row[tickedField]; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
import { Constant } from '../../constant/Constant'; |
||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 列头全部勾选checkbox状态改变事件 |
||||
|
*/ |
||||
|
export class UpdateTickeds extends Base { |
||||
|
static NAME = 'updateTickeds'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.execute = this.execute.bind(this); |
||||
|
} |
||||
|
|
||||
|
public execute(value: boolean | null) { |
||||
|
const selectedField = this.props.selectedField; |
||||
|
const tickedField = this.props.tickedField; |
||||
|
if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.NONE) { |
||||
|
if (value) { |
||||
|
this.table.rows.forEach((item) => { |
||||
|
item[tickedField] = true; |
||||
|
item[selectedField] = true; |
||||
|
}); |
||||
|
} else { |
||||
|
this.table.rows.forEach((item) => { |
||||
|
item[tickedField] = false; |
||||
|
item[selectedField] = false; |
||||
|
}); |
||||
|
} |
||||
|
} else if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.ROW || this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.CELL) { |
||||
|
this.table.store.headerTicked = null; |
||||
|
} else if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.ROWS) { |
||||
|
this.table.store.headerTicked = false; |
||||
|
} |
||||
|
this.instance.emit(UpdateTickeds.NAME, { grid: this.instance, value: value }); |
||||
|
} |
||||
|
} |
@ -0,0 +1,134 @@ |
|||||
|
import { PropsType } from '../types/PropsType'; |
||||
|
|
||||
|
import { GridTools } from '../GridTools'; |
||||
|
import { Button } from './src/Button'; |
||||
|
import { ComponentRef } from './src/ComponentRef'; |
||||
|
import { GetData } from './src/GetData'; |
||||
|
import { LocalMode } from './src/LocalMode'; |
||||
|
import { Operator } from './src/Operator'; |
||||
|
import { ResetProperty } from './src/ResetProperty'; |
||||
|
import { TableType } from '../types/TableType'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 对外暴露API管理器 |
||||
|
*/ |
||||
|
export class ExposeApiManager { |
||||
|
/** |
||||
|
* 获取表格数据相关的API |
||||
|
*/ |
||||
|
getData: GetData; |
||||
|
/** |
||||
|
* 获取内置组件ref相关的API |
||||
|
*/ |
||||
|
componentRef: ComponentRef; |
||||
|
/** |
||||
|
* 表格操作相关的API |
||||
|
*/ |
||||
|
operator: Operator; |
||||
|
/** |
||||
|
* 本地模式API |
||||
|
*/ |
||||
|
localMode: LocalMode; |
||||
|
/** |
||||
|
* 重新设置表格属性API |
||||
|
*/ |
||||
|
resetProperty: ResetProperty; |
||||
|
/** |
||||
|
* 内置按钮API |
||||
|
*/ |
||||
|
button: Button; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
this.getData = new GetData(props, table); |
||||
|
this.componentRef = new ComponentRef(props, table); |
||||
|
this.operator = new Operator(props, table); |
||||
|
this.localMode = new LocalMode(props, table); |
||||
|
this.resetProperty = new ResetProperty(props, table); |
||||
|
this.button = new Button(props, table); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建暴露给用户使用的API |
||||
|
* @param tools |
||||
|
* @returns |
||||
|
*/ |
||||
|
static createExpose(tools: GridTools) { |
||||
|
return { |
||||
|
// 获取数据api
|
||||
|
getRows: tools.apiFM.getData.getRows, |
||||
|
getSelectedRow: tools.apiFM.getData.getSelectedRow, |
||||
|
getSelectedRows: tools.apiFM.getData.getSelectedRows, |
||||
|
getTickedRow: tools.apiFM.getData.getTickedRow, |
||||
|
getTickedRows: tools.apiFM.getData.getTickedRows, |
||||
|
getCascadeChildren: tools.apiFM.getData.getCascadeChildren, |
||||
|
getCascadeParents: tools.apiFM.getData.getCascadeParents, |
||||
|
getSelectedCell: tools.apiFM.getData.getSelectedCell, |
||||
|
|
||||
|
// 获取子组件ref的api
|
||||
|
getQueryForm: tools.apiFM.componentRef.getQueryForm, |
||||
|
getEditorDialog: tools.apiFM.componentRef.getEditorDialog, |
||||
|
getCellEditorDialog: tools.apiFM.componentRef.getCellEditorDialog, |
||||
|
getEditorForm: tools.apiFM.componentRef.getEditorForm, |
||||
|
getCellEditorForm: tools.apiFM.componentRef.getCellEditorForm, |
||||
|
getViewerDrawer: tools.apiFM.componentRef.getViewerDrawer, |
||||
|
getViewerPanel: tools.apiFM.componentRef.getViewerPanel, |
||||
|
|
||||
|
// 表格操作api
|
||||
|
setSelected: tools.apiFM.operator.setSelected, |
||||
|
setTicked: tools.apiFM.operator.setTicked, |
||||
|
clearSelected: tools.apiFM.operator.clearSelected, |
||||
|
clearTicked: tools.apiFM.operator.clearTicked, |
||||
|
expand: tools.apiFM.operator.expand, |
||||
|
collapse: tools.apiFM.operator.collapse, |
||||
|
|
||||
|
// 本地模式api
|
||||
|
setLocalData: tools.apiFM.localMode.setLocalData, |
||||
|
addLocalData: tools.apiFM.localMode.addLocalData, |
||||
|
updateLocalData: tools.apiFM.localMode.updateLocalData, |
||||
|
removeLocalData: tools.apiFM.localMode.removeLocalData, |
||||
|
|
||||
|
// 重新设置或覆盖现有表格属性配置api
|
||||
|
setQueryCriteria: tools.apiFM.resetProperty.setQueryCriteria, |
||||
|
setQueryCriteriaFieldValue: tools.apiFM.resetProperty.setQueryCriteriaFieldValue, |
||||
|
setDataUrl: tools.apiFM.resetProperty.setDataUrl, |
||||
|
setFetchDataUrl: tools.apiFM.resetProperty.setFetchDataUrl, |
||||
|
setAddDataUrl: tools.apiFM.resetProperty.setAddDataUrl, |
||||
|
setEditDataUrl: tools.apiFM.resetProperty.setEditDataUrl, |
||||
|
setRemoveDataUrl: tools.apiFM.resetProperty.setRemoveDataUrl, |
||||
|
|
||||
|
// 内置按钮api
|
||||
|
query: tools.apiFM.button.query, |
||||
|
moreQuery: tools.apiFM.button.moreQuery, |
||||
|
reset: tools.apiFM.button.reset, |
||||
|
refresh: tools.apiFM.button.refresh, |
||||
|
add: tools.apiFM.button.add, |
||||
|
addTop: tools.apiFM.button.addTop, |
||||
|
addChild: tools.apiFM.button.addChild, |
||||
|
edit: tools.apiFM.button.edit, |
||||
|
cellEdit: tools.apiFM.button.cellEdit, |
||||
|
inlineCellEdit: tools.apiFM.button.inlineCellEdit, |
||||
|
inlineRowEdit: tools.apiFM.button.inlineRowEdit, |
||||
|
inlineRowsEdit: tools.apiFM.button.inlineRowsEdit, |
||||
|
clone: tools.apiFM.button.clone, |
||||
|
remove: tools.apiFM.button.remove, |
||||
|
view: tools.apiFM.button.view, |
||||
|
export: tools.apiFM.button.exportData, |
||||
|
resetDefaultValues: tools.apiFM.button.resetDefaultValues, |
||||
|
|
||||
|
// =========其他API=========
|
||||
|
refreshStyle: tools.opFM.resetStyleVariableValue, |
||||
|
getHtmlElement: () => { |
||||
|
return tools.table.componentRef.getTableRef()?.$el; |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// 设置组件实例
|
||||
|
setInstance(instance: any, tools: GridTools) { |
||||
|
this.getData.setInstance(instance, tools); |
||||
|
this.componentRef.setInstance(instance, tools); |
||||
|
this.operator.setInstance(instance, tools); |
||||
|
this.localMode.setInstance(instance, tools); |
||||
|
this.resetProperty.setInstance(instance, tools); |
||||
|
} |
||||
|
} |
@ -0,0 +1,133 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { toRaw } from 'vue'; |
||||
|
import { Base } from '../../Base'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 内置按钮API |
||||
|
*/ |
||||
|
export class Button extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.getTopRef = this.getTopRef.bind(this); |
||||
|
this.getActionArgs = this.getActionArgs.bind(this); |
||||
|
this.query = this.query.bind(this); |
||||
|
this.moreQuery = this.moreQuery.bind(this); |
||||
|
this.reset = this.reset.bind(this); |
||||
|
this.refresh = this.refresh.bind(this); |
||||
|
this.add = this.add.bind(this); |
||||
|
this.addTop = this.addTop.bind(this); |
||||
|
this.addChild = this.addChild.bind(this); |
||||
|
this.edit = this.edit.bind(this); |
||||
|
this.cellEdit = this.cellEdit.bind(this); |
||||
|
this.inlineCellEdit = this.inlineCellEdit.bind(this); |
||||
|
this.inlineRowEdit = this.inlineRowEdit.bind(this); |
||||
|
this.inlineRowsEdit = this.inlineRowsEdit.bind(this); |
||||
|
this.clone = this.clone.bind(this); |
||||
|
this.remove = this.remove.bind(this); |
||||
|
this.view = this.view.bind(this); |
||||
|
this.exportData = this.exportData.bind(this); |
||||
|
this.resetDefaultValues = this.resetDefaultValues.bind(this); |
||||
|
this.beforeCellEdit = this.beforeCellEdit.bind(this); |
||||
|
} |
||||
|
|
||||
|
private getTopRef() { |
||||
|
return this.table.componentRef.getTopRef(); |
||||
|
} |
||||
|
private getActionArgs() { |
||||
|
return this.tools?.cm.actionArgs.value; |
||||
|
} |
||||
|
|
||||
|
query() { |
||||
|
this.tools?.bm.query.click(this.getActionArgs()); |
||||
|
} |
||||
|
moreQuery() { |
||||
|
this.tools?.bm.moreQuery.click(this.getActionArgs()); |
||||
|
} |
||||
|
reset() { |
||||
|
this.tools?.bm.reset.click(this.getActionArgs()); |
||||
|
} |
||||
|
refresh() { |
||||
|
this.tools?.bm.refresh.click(this.getActionArgs()); |
||||
|
} |
||||
|
add() { |
||||
|
this.tools?.bm.add.click(this.getActionArgs()); |
||||
|
} |
||||
|
addTop() { |
||||
|
this.tools?.bm.addTop.click(this.getActionArgs()); |
||||
|
} |
||||
|
addChild(target: any) { |
||||
|
if (Tools.isEmpty(target)) { |
||||
|
throw new Error('[w-grid] The `addChild` method parameter cannot be null.'); |
||||
|
} |
||||
|
this.tools?.apiFM.operator.setSelected(target); |
||||
|
this.tools?.bm.addChild.click(this.getActionArgs()); |
||||
|
} |
||||
|
edit(target: any) { |
||||
|
if (Tools.isEmpty(target)) { |
||||
|
throw new Error('[w-grid] The `edit` method parameter cannot be null.'); |
||||
|
} |
||||
|
this.tools?.apiFM.operator.setSelected(target); |
||||
|
this.tools?.bm.edit.click(this.getActionArgs()); |
||||
|
} |
||||
|
cellEdit(targetRow: any, targetColName: string) { |
||||
|
this.beforeCellEdit(targetRow, targetColName); |
||||
|
this.tools?.bm.cellEdit.click(this.getActionArgs()); |
||||
|
} |
||||
|
inlineCellEdit(targetRow: any, targetColName: string) { |
||||
|
this.beforeCellEdit(targetRow, targetColName); |
||||
|
this.tools?.bm.inlineCellEdit.click(this.getActionArgs()); |
||||
|
} |
||||
|
inlineRowEdit(target: any) { |
||||
|
this.tools?.apiFM.operator.setSelected(target); |
||||
|
this.tools?.bm.inlineRowEdit.click(this.getActionArgs()); |
||||
|
} |
||||
|
inlineRowsEdit() { |
||||
|
this.tools?.bm.inlineRowsEdit.click(this.getActionArgs()); |
||||
|
} |
||||
|
clone(target: any) { |
||||
|
this.tools?.apiFM.operator.setSelected(target); |
||||
|
this.tools?.bm.clone.click(this.getActionArgs()); |
||||
|
} |
||||
|
remove(target: any) { |
||||
|
this.tools?.apiFM.operator.setSelected(target); |
||||
|
this.getTopRef()?.remove(this.getActionArgs()); |
||||
|
} |
||||
|
view(target: any) { |
||||
|
this.tools?.apiFM.operator.setSelected(target); |
||||
|
this.tools?.bm.view.click(this.getActionArgs()); |
||||
|
} |
||||
|
exportData() { |
||||
|
this.tools?.bm.expand.click(this.getActionArgs()); |
||||
|
} |
||||
|
resetDefaultValues() { |
||||
|
this.tools?.bm.resetDefaultValues.click(this.getActionArgs()); |
||||
|
} |
||||
|
|
||||
|
private beforeCellEdit(targetRow: any, targetColName: string) { |
||||
|
if (!this.table) { |
||||
|
return; |
||||
|
} |
||||
|
if (Tools.isEmpty(targetRow)) { |
||||
|
throw new Error('[w-grid] The `targetRow` parameter of the `cellEdit` method cannot be null.'); |
||||
|
} |
||||
|
if (Tools.isEmpty(targetColName)) { |
||||
|
throw new Error('[w-grid] The `targetColName` parameter of the `cellEdit` method cannot be null.'); |
||||
|
} |
||||
|
const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, targetRow); |
||||
|
if (!result || result.length === 0) { |
||||
|
throw new Error('[w-grid] The `targetRow` does not exist in the table data.'); |
||||
|
} |
||||
|
const row = result[0]; |
||||
|
if (!row[targetColName]) { |
||||
|
throw new Error('[w-grid] The `targetColName` does not exist in the table data.'); |
||||
|
} |
||||
|
this.table.store.cellSelected = { |
||||
|
row: toRaw(row), |
||||
|
rowKey: row[Constant.FIELD_NAMES.ROW_KEY], |
||||
|
primaryKey: row[this.props.primaryKey], |
||||
|
colName: targetColName, |
||||
|
value: row[targetColName], |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 获取内置组件ref对象API |
||||
|
*/ |
||||
|
export class ComponentRef extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.getQueryForm = this.getQueryForm.bind(this); |
||||
|
this.getEditorDialog = this.getEditorDialog.bind(this); |
||||
|
this.getCellEditorDialog = this.getCellEditorDialog.bind(this); |
||||
|
this.getEditorForm = this.getEditorForm.bind(this); |
||||
|
this.getCellEditorForm = this.getCellEditorForm.bind(this); |
||||
|
this.getViewerDrawer = this.getViewerDrawer.bind(this); |
||||
|
this.getViewerPanel = this.getViewerPanel.bind(this); |
||||
|
} |
||||
|
|
||||
|
getQueryForm() { |
||||
|
return this.table.componentRef.getTopRef().getQueryForm(); |
||||
|
} |
||||
|
getEditorDialog() { |
||||
|
return this.table.componentRef.getEditorRef()?.getDialog(); |
||||
|
} |
||||
|
getCellEditorDialog() { |
||||
|
return this.table.componentRef.getCellEditorRef()?.getDialog(); |
||||
|
} |
||||
|
getEditorForm() { |
||||
|
return this.table.componentRef.getEditorRef()?.getForm(); |
||||
|
} |
||||
|
getCellEditorForm() { |
||||
|
return this.table.componentRef.getCellEditorRef()?.getForm(); |
||||
|
} |
||||
|
getViewerDrawer() { |
||||
|
return this.table.componentRef.getViewRef()?.getViewerDrawer(); |
||||
|
} |
||||
|
getViewerPanel() { |
||||
|
return this.table.componentRef.getViewRef()?.getInfoPanel(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,182 @@ |
|||||
|
import { toRaw } from 'vue'; |
||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 获取表格数据相关API |
||||
|
*/ |
||||
|
export class GetData extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.getRows = this.getRows.bind(this); |
||||
|
this.getSelectedRow = this.getSelectedRow.bind(this); |
||||
|
this.getSelectedRows = this.getSelectedRows.bind(this); |
||||
|
this.getTickedRow = this.getTickedRow.bind(this); |
||||
|
this.getTickedRows = this.getTickedRows.bind(this); |
||||
|
this.getSelectedCell = this.getSelectedCell.bind(this); |
||||
|
this.getCascadeChildren = this.getCascadeChildren.bind(this); |
||||
|
this.getCascadeParents = this.getCascadeParents.bind(this); |
||||
|
this.getSelectRowsByFieldName = this.getSelectRowsByFieldName.bind(this); |
||||
|
this.getCascadeChildrenData = this.getCascadeChildrenData.bind(this); |
||||
|
this.getCascadeParentsData = this.getCascadeParentsData.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取表格所有数据 |
||||
|
*/ |
||||
|
getRows() { |
||||
|
return toRaw(this.table?.rows); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取单条选中记录 |
||||
|
* @return 行数据对象 |
||||
|
*/ |
||||
|
getSelectedRow() { |
||||
|
const selectedRows = []; |
||||
|
if (!this.table) { |
||||
|
return selectedRows; |
||||
|
} |
||||
|
this.getSelectRowsByFieldName(this.table.rows, selectedRows, this.props.selectedField); |
||||
|
if (selectedRows && selectedRows.length > 0) { |
||||
|
return toRaw(selectedRows)[0]; |
||||
|
} else { |
||||
|
return undefined; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取所有选中记录 |
||||
|
* @return 行数据对象数组 |
||||
|
*/ |
||||
|
getSelectedRows() { |
||||
|
const selectedRows = []; |
||||
|
if (!this.table) { |
||||
|
return selectedRows; |
||||
|
} |
||||
|
this.getSelectRowsByFieldName(this.table.rows, selectedRows, this.props.selectedField); |
||||
|
return toRaw(selectedRows); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获得单条checkbox勾选的记录 |
||||
|
* @param containsNullStatus 是否包含半勾选状态的记录 |
||||
|
* @return 行数据对象 |
||||
|
*/ |
||||
|
getTickedRow = (containsNullStatus: boolean = false) => { |
||||
|
const tickedRows = []; |
||||
|
if (!this.table) { |
||||
|
return tickedRows; |
||||
|
} |
||||
|
this.getSelectRowsByFieldName(this.table.rows, tickedRows, this.props.tickedField, containsNullStatus); |
||||
|
if (tickedRows && tickedRows.length > 0) { |
||||
|
return toRaw(tickedRows)[0]; |
||||
|
} else { |
||||
|
return undefined; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 获得checkbox勾选的所有记录 |
||||
|
* @param containsNullStatus 是否包含半勾选状态的记录 |
||||
|
* return 数组 |
||||
|
*/ |
||||
|
getTickedRows(containsNullStatus: boolean = false) { |
||||
|
const tickedRows = []; |
||||
|
if (!this.table) { |
||||
|
return tickedRows; |
||||
|
} |
||||
|
this.getSelectRowsByFieldName(this.table.rows, tickedRows, this.props.tickedField, containsNullStatus); |
||||
|
return toRaw(tickedRows); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获得选中的单元格 |
||||
|
*/ |
||||
|
getSelectedCell() { |
||||
|
return { |
||||
|
row: this.table.store.cellSelected?.row, |
||||
|
colName: this.table.store.cellSelected?.colName, |
||||
|
value: this.table.store.cellSelected?.value, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取选择的行记录及所有子记录 |
||||
|
* @param fieldName 属性名称 |
||||
|
* @return 若传入了属性名称,则返回属性对应的数据数组 |
||||
|
* 若没有传入属性名称,返回行数据对象数组 |
||||
|
*/ |
||||
|
getCascadeChildren(fieldName: string) { |
||||
|
const row = this.getSelectedRow(); |
||||
|
const rows = row ? [row] : []; |
||||
|
return this.getCascadeChildrenData(rows, fieldName); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取选中的行记录及所有父记录 |
||||
|
* @param fieldName 属性名称 |
||||
|
* @return 若传入了属性名称,则返回属性对应的数据数组 |
||||
|
* 若没有传入属性名称,返回行数据对象数组 |
||||
|
*/ |
||||
|
getCascadeParents(fieldName: string) { |
||||
|
const row = this.getSelectedRow(); |
||||
|
if (row) { |
||||
|
const parent = this.getCascadeParentsData(row, fieldName); |
||||
|
const result = <any>[fieldName ? row[fieldName] : row]; |
||||
|
if (parent && parent.length > 0) { |
||||
|
result.push(...parent); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
return undefined; |
||||
|
} |
||||
|
|
||||
|
private getSelectRowsByFieldName(arr: Array<any>, selectedRows: Array<any>, fieldName: string, containsNullStatus: boolean = false) { |
||||
|
arr.forEach((item) => { |
||||
|
if (containsNullStatus && (item[fieldName] || item[fieldName] === null)) { |
||||
|
selectedRows.push(item); |
||||
|
} else if (item[fieldName]) { |
||||
|
selectedRows.push(item); |
||||
|
} |
||||
|
if (this.props.tree && item.children && item.children.length > 0) { |
||||
|
this.getSelectRowsByFieldName(item.children, selectedRows, fieldName); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private getCascadeChildrenData(rows: Array<any>, fieldName: string) { |
||||
|
const data = <any>[]; |
||||
|
if (rows && rows.length > 0) { |
||||
|
rows.forEach((item: any, index) => { |
||||
|
data.push(fieldName ? item[fieldName] : item); |
||||
|
if (this.props.tree && item.children && item.children.length > 0) { |
||||
|
const childrenData = this.getCascadeChildrenData(item.children, fieldName); |
||||
|
data.push(...childrenData); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获得所有父节点数据 |
||||
|
* @param row 行数据对象 |
||||
|
* @param fieldName 字段名称,为空时返回父节点对象,否则返回字段值 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getCascadeParentsData(row: any, fieldName: string | null | undefined) { |
||||
|
const data = <any>[]; |
||||
|
if (row && row[this.props.foreignKey]) { |
||||
|
const parent = this.tools?.dataFM.getRow(this.table.rows, row[this.props.foreignKey], true); |
||||
|
if (parent) { |
||||
|
data.push(fieldName ? parent[fieldName] : parent); |
||||
|
if (parent[this.props.foreignKey]) { |
||||
|
const parentData = this.getCascadeParentsData(parent, fieldName); |
||||
|
data.push(...parentData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return data; |
||||
|
} |
||||
|
} |
@ -0,0 +1,209 @@ |
|||||
|
import { Tools, TreeBuilder } from '@/platform'; |
||||
|
import { Base } from '../../Base'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 表格本地模式API |
||||
|
*/ |
||||
|
export class LocalMode extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.setLocalData = this.setLocalData.bind(this); |
||||
|
this.addLocalData = this.addLocalData.bind(this); |
||||
|
this.updateLocalData = this.updateLocalData.bind(this); |
||||
|
this.removeLocalData = this.removeLocalData.bind(this); |
||||
|
|
||||
|
this.treeAddLocalData = this.treeAddLocalData.bind(this); |
||||
|
this.updateLocalDataError = this.updateLocalDataError.bind(this); |
||||
|
this.replaceRowHandler = this.replaceRowHandler.bind(this); |
||||
|
this.removeTreeRows = this.removeTreeRows.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置本地记录 |
||||
|
* @param rows 行数据对象数组 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
setLocalData(rows: Array<any>) { |
||||
|
if (rows && Array.isArray(rows)) { |
||||
|
if (this.props.tree && this.props.treeRelationship === 'parent') { |
||||
|
const treeRows = TreeBuilder.build(rows, this.props.foreignKey, this.props.primaryKey); |
||||
|
this.table.rows = treeRows; |
||||
|
} else { |
||||
|
this.table.rows = rows; |
||||
|
} |
||||
|
this.tools?.dataFM.setExtraProperty(this.table.rows, undefined, true); |
||||
|
this.table.store.pagination.rowsNumber = this.table.rows.length; |
||||
|
this.tools?.opFM.resetStyleVariableValue(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 新增本地记录 |
||||
|
* @param data 行数据对象或行数据对象数组 |
||||
|
* @param index 增加的下标,可不传,默认增加到最后 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
addLocalData(data: any, index?: number) { |
||||
|
let success = false; |
||||
|
if (this.props.tree) { |
||||
|
success = this.treeAddLocalData(data); |
||||
|
} else { |
||||
|
if (data && Array.isArray(data) && data.length > 0) { |
||||
|
data.forEach((row) => { |
||||
|
if (!index || index >= this.table.rows.length) { |
||||
|
this.table.rows.push(row); |
||||
|
} else { |
||||
|
this.table.rows.splice(index, 0, row); |
||||
|
} |
||||
|
}); |
||||
|
success = true; |
||||
|
} else if (data) { |
||||
|
if (!index || index >= this.table.rows.length) { |
||||
|
this.table.rows.push(data); |
||||
|
} else { |
||||
|
this.table.rows.splice(index, 0, data); |
||||
|
} |
||||
|
success = true; |
||||
|
} |
||||
|
} |
||||
|
if (success) { |
||||
|
this.tools?.dataFM.setExtraProperty(this.table.rows); |
||||
|
this.table.store.pagination.rowsNumber = this.table.rows.length; |
||||
|
this.tools?.opFM.resetStyleVariableValue(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改本地记录 |
||||
|
* @param data 行数据对象或行数据对象数组 |
||||
|
* @param fieldName 默认根据数据行主键或者前端行主键进行修改,若传入列名称则根据传入的列名称进行匹配修改。 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
updateLocalData(data: any, fieldName: string = '') { |
||||
|
if (data && Array.isArray(data)) { |
||||
|
data.forEach((item) => { |
||||
|
this.updateLocalDataError(fieldName, item); |
||||
|
this.replaceRowHandler(this.table.rows, item, fieldName); |
||||
|
}); |
||||
|
} else if (data) { |
||||
|
this.updateLocalDataError(fieldName, data); |
||||
|
this.replaceRowHandler(this.table.rows, data, fieldName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除本地数据 TODO: 待优化 |
||||
|
* @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
removeLocalData(target) { |
||||
|
const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target); |
||||
|
if (result && result.length > 0) { |
||||
|
if (this.props.tree) { |
||||
|
result.forEach((item) => { |
||||
|
this.removeTreeRows(this.table.rows, item); |
||||
|
}); |
||||
|
this.tools?.dataFM.setExtraProperty(this.table.rows); |
||||
|
} else { |
||||
|
result.forEach((item) => { |
||||
|
this.table.rows.splice( |
||||
|
this.table.rows.findIndex((v) => { |
||||
|
if (!Tools.isEmpty(v[this.props.primaryKey]) && Tools.isEmpty(item[this.props.primaryKey])) { |
||||
|
return v[this.props.primaryKey] === item[this.props.primaryKey]; |
||||
|
} else { |
||||
|
return v[Constant.FIELD_NAMES.ROW_KEY] === item[Constant.FIELD_NAMES.ROW_KEY]; |
||||
|
} |
||||
|
}), |
||||
|
1, |
||||
|
); |
||||
|
}); |
||||
|
this.table.store.pagination.rowsNumber = this.table.rows.length; |
||||
|
} |
||||
|
this.tools?.opFM.resetHeaderCheckbox(); |
||||
|
this.tools?.opFM.resetStyleVariableValue(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 树表格新增 |
||||
|
* @param data |
||||
|
*/ |
||||
|
private treeAddLocalData(data: any) { |
||||
|
let success = false; |
||||
|
if (data && Array.isArray(data) && data.length > 0) { |
||||
|
data.forEach((row) => { |
||||
|
const foreignKey = row[this.props.foreignKey]; |
||||
|
if (foreignKey) { |
||||
|
const parent = this.tools?.dataFM.getRow(this.table.rows, foreignKey, false, this.props.primaryKey); |
||||
|
if (parent) { |
||||
|
if (parent.children) { |
||||
|
parent.children.push(row); |
||||
|
} else { |
||||
|
parent.children = [row]; |
||||
|
} |
||||
|
} else { |
||||
|
this.table.rows.push(row); |
||||
|
} |
||||
|
} else { |
||||
|
this.table.rows.push(row); |
||||
|
} |
||||
|
}); |
||||
|
success = true; |
||||
|
} else if (data) { |
||||
|
const foreignKey = data[this.props.foreignKey]; |
||||
|
if (foreignKey) { |
||||
|
const parent = this.tools?.dataFM.getRow(this.table.rows, foreignKey, false, this.props.primaryKey); |
||||
|
if (parent) { |
||||
|
if (parent.children) { |
||||
|
parent.children.push(data); |
||||
|
} else { |
||||
|
parent.children = [data]; |
||||
|
} |
||||
|
} else { |
||||
|
this.table.rows.push(data); |
||||
|
} |
||||
|
} else { |
||||
|
this.table.rows.push(data); |
||||
|
} |
||||
|
success = true; |
||||
|
} |
||||
|
return success; |
||||
|
} |
||||
|
|
||||
|
private updateLocalDataError(fieldName: string, data: any) { |
||||
|
if (Tools.isEmpty(fieldName) && Tools.isEmpty(data[this.props.primaryKey]) && Tools.isEmpty(data[Constant.FIELD_NAMES.ROW_KEY])) { |
||||
|
throw new Error('[w-grid]Data does not contain `primaryKey` or `rowKey`.'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private replaceRowHandler(arr: Array<any>, row: any, fieldName: string) { |
||||
|
for (let i = 0; i < arr.length; i++) { |
||||
|
if (!Tools.isEmpty(fieldName) && row[fieldName] === arr[i][fieldName]) { |
||||
|
// 根据设定的字段进行匹配
|
||||
|
arr[i] = { ...arr[i], ...row }; |
||||
|
break; |
||||
|
} else if (Tools.isEmpty(fieldName) && !Tools.isEmpty(row[this.props.primaryKey]) && row[this.props.primaryKey] === arr[i][this.props.primaryKey]) { |
||||
|
// 未设定根据哪个字段匹配时首先尝试用主键进行匹配。
|
||||
|
arr[i] = { ...arr[i], ...row }; |
||||
|
break; |
||||
|
} else if (Tools.isEmpty(fieldName) && row[Constant.FIELD_NAMES.ROW_KEY] === arr[i][Constant.FIELD_NAMES.ROW_KEY]) { |
||||
|
// 主键匹配失败则尝试用前端唯一键进行匹配
|
||||
|
arr[i] = { ...arr[i], ...row }; |
||||
|
break; |
||||
|
} else if (this.props.tree && arr[i].children && arr[i].children.length > 0) { |
||||
|
this.replaceRowHandler(arr[i].children, row, fieldName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private removeTreeRows(arr: Array<any>, row: any) { |
||||
|
arr.forEach((item, index) => { |
||||
|
if (row[this.props.primaryKey] === item[this.props.primaryKey] || row[Constant.FIELD_NAMES.ROW_KEY] === item[Constant.FIELD_NAMES.ROW_KEY]) { |
||||
|
arr.splice(index, 1); |
||||
|
} else if (item.children && item.children.length > 0) { |
||||
|
this.removeTreeRows(item.children, row); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,183 @@ |
|||||
|
import { nextTick } from 'vue'; |
||||
|
import { Base } from '../../Base'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 表格操作相关API |
||||
|
*/ |
||||
|
export class Operator extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.refreshGrid = this.refreshGrid.bind(this); |
||||
|
this.viewData = this.viewData.bind(this); |
||||
|
this.setSelected = this.setSelected.bind(this); |
||||
|
this.setTicked = this.setTicked.bind(this); |
||||
|
this.clearSelected = this.clearSelected.bind(this); |
||||
|
this.clearTicked = this.clearTicked.bind(this); |
||||
|
this.expand = this.expand.bind(this); |
||||
|
this.collapse = this.collapse.bind(this); |
||||
|
|
||||
|
this.processTree = this.processTree.bind(this); |
||||
|
this.processGroup = this.processGroup.bind(this); |
||||
|
this.setSelectedStatus = this.setSelectedStatus.bind(this); |
||||
|
this.setTickedStatus = this.setTickedStatus.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 刷新表格 |
||||
|
*/ |
||||
|
refreshGrid() { |
||||
|
nextTick(() => { |
||||
|
this.tools?.reqApiFM.fetchData({ pagination: this.table.store.pagination }); |
||||
|
this.table.store.headerTicked = false; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 数据查看 |
||||
|
*/ |
||||
|
viewData() { |
||||
|
this.table.componentRef.getViewRef()?.view(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置目标行选中 |
||||
|
* @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组。 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
setSelected(target: any) { |
||||
|
if (target) { |
||||
|
const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target); |
||||
|
if (result && result.length > 0) { |
||||
|
this.setSelectedStatus(result, true); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置目标行checkbox勾选 |
||||
|
* @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组。 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
setTicked(target: any) { |
||||
|
if (target) { |
||||
|
const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target); |
||||
|
if (result && result.length > 0) { |
||||
|
this.setTickedStatus(result, true); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清空目标行选中 |
||||
|
* @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组,不传或者为空则清空全部选中状态 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
clearSelected(target: any = undefined) { |
||||
|
if (target || (Array.isArray(target) && target.length > 0)) { |
||||
|
const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target); |
||||
|
if (result && result.length > 0) { |
||||
|
this.setSelectedStatus(result, false); |
||||
|
} |
||||
|
} else if (this.table) { |
||||
|
this.setSelectedStatus(this.table?.rows, false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清空目标checkbox勾选记录 |
||||
|
* @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组,不传或者为空则清空全部选中状态 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
clearTicked(target: any = undefined) { |
||||
|
if (target || (Array.isArray(target) && target.length > 0)) { |
||||
|
const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target); |
||||
|
if (result && result.length > 0) { |
||||
|
this.setTickedStatus(result, false); |
||||
|
} |
||||
|
} else if (this.table) { |
||||
|
this.setTickedStatus(this.table.rows, false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 展开表格行 |
||||
|
* @param target 树表格模式:可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组,不传或者为空则展开全部 |
||||
|
* 独立分组模式:可传入当前分组字段的值,不传或者为空则展开全部 |
||||
|
* @param cascadeChildren 树表格模式:是否级联子,级联模式下所有的子节点也展开,独立分组模式该参数无效。 |
||||
|
*/ |
||||
|
expand(target: any = undefined, cascadeChildren: boolean = false) { |
||||
|
const expand = true; |
||||
|
if (this.props.tree) { |
||||
|
// 树表格模式
|
||||
|
this.processTree(target, expand, cascadeChildren); |
||||
|
} else { |
||||
|
// 独立分组模式
|
||||
|
this.processGroup(target, expand); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 收起表格行 |
||||
|
* @param target 树表格模式:可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组,不传或者为空则收起全部 |
||||
|
* 独立分组模式:可传入当前分组字段的值,不传或者为空则收起全部 |
||||
|
* @param cascadeParents 树表格模式:是否级联父,级联模式下往上找,将传入数据的父节点收起,独立分组模式该参数无效。 |
||||
|
*/ |
||||
|
collapse(target: any = undefined, cascadeParents: boolean = false) { |
||||
|
const expand = false; |
||||
|
if (this.props.tree) { |
||||
|
// 树表格模式
|
||||
|
this.processTree(target, expand, cascadeParents); |
||||
|
} else { |
||||
|
// 独立分组模式
|
||||
|
this.processGroup(target, expand); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private processTree(target: any, expand: boolean, cascade: boolean) { |
||||
|
const rows = target ? this.tools?.dataFM.getRowsByTarget(this.table.rows, target) : this.table.rows; |
||||
|
if (rows) { |
||||
|
rows.forEach((item: any) => { |
||||
|
item[Constant.FIELD_NAMES.EXPAND] = expand; |
||||
|
if (cascade && item.children && item.children.length > 0) { |
||||
|
this.tools?.opFM.resetTreeGridExpand(item.children, expand); |
||||
|
} else if (!expand) { |
||||
|
const parents = this.tools?.apiFM.getData.getCascadeParentsData(item, null); |
||||
|
if (parents && parents.length > 0) { |
||||
|
this.tools?.opFM.resetTreeGridExpand(parents, expand); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
private processGroup(target: any, expand: boolean) { |
||||
|
if (target) { |
||||
|
const findResult = this.table.configStore.aloneGroupRecords.find((item) => item.groupName === target); |
||||
|
if (!findResult) { |
||||
|
console.error('[w-grid] groupRecords not find `' + target + '`.'); |
||||
|
return; |
||||
|
} |
||||
|
findResult['expand'] = expand; |
||||
|
} else { |
||||
|
this.table.configStore.aloneGroupRecords.forEach((item) => { |
||||
|
item['expand'] = expand; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
private setSelectedStatus(arr: Array<any>, status: boolean) { |
||||
|
arr.forEach((item) => { |
||||
|
item[this.props.selectedField] = status; |
||||
|
if (this.props.tree && item.children && item.children.length > 0) { |
||||
|
this.setSelectedStatus(item.children, status); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
private setTickedStatus(arr: Array<any>, status: boolean, cascadeChildren: boolean = true) { |
||||
|
arr.forEach((item) => { |
||||
|
item[this.props.tickedField] = status; |
||||
|
if (this.props.tree && cascadeChildren && item.children && item.children.length > 0) { |
||||
|
this.setTickedStatus(item.children, status, cascadeChildren); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,118 @@ |
|||||
|
import { Base } from '../../Base'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 重新设置表格属性API |
||||
|
*/ |
||||
|
export class ResetProperty extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.setQueryCriteria = this.setQueryCriteria.bind(this); |
||||
|
this.setQueryCriteriaFieldValue = this.setQueryCriteriaFieldValue.bind(this); |
||||
|
this.setDataUrl = this.setDataUrl.bind(this); |
||||
|
this.setFetchDataUrl = this.setFetchDataUrl.bind(this); |
||||
|
this.setAddDataUrl = this.setAddDataUrl.bind(this); |
||||
|
this.setEditDataUrl = this.setEditDataUrl.bind(this); |
||||
|
this.setRemoveDataUrl = this.setRemoveDataUrl.bind(this); |
||||
|
this.setQueryCriteriaFieldValueHandler = this.setQueryCriteriaFieldValueHandler.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置查询criteria |
||||
|
* @param criteria |
||||
|
* @returns |
||||
|
*/ |
||||
|
setQueryCriteria(criteria: any) { |
||||
|
if (!this.table) { |
||||
|
return; |
||||
|
} |
||||
|
this.table.queryCriteria = criteria; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置查询criteria中的字段值 |
||||
|
* @param fieldName 字段名称 |
||||
|
* @param fieldValue 字段值 |
||||
|
* @return 无返回 |
||||
|
*/ |
||||
|
setQueryCriteriaFieldValue(fieldName: string, fieldValue: any) { |
||||
|
if (Object.keys(this.table?.queryCriteria).length === 0) { |
||||
|
console.error('[w-grid]The property `queryCriteria` is configured.'); |
||||
|
return; |
||||
|
} |
||||
|
this.setQueryCriteriaFieldValueHandler(this.table?.queryCriteria, fieldName, fieldValue); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置基础URL |
||||
|
* @param url |
||||
|
*/ |
||||
|
setDataUrl(url: string) { |
||||
|
if (!this.table) { |
||||
|
return; |
||||
|
} |
||||
|
this.table.url.dataUrl = url; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置数据查询URL |
||||
|
* @param url |
||||
|
* @returns |
||||
|
*/ |
||||
|
setFetchDataUrl(url: string) { |
||||
|
if (!this.table) { |
||||
|
return; |
||||
|
} |
||||
|
this.table.url.fetchDataUrl = url; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置新增时保存URL |
||||
|
* @param url |
||||
|
* @returns |
||||
|
*/ |
||||
|
setAddDataUrl(url: string) { |
||||
|
if (!this.table) { |
||||
|
return; |
||||
|
} |
||||
|
this.table.url.addDataUrl = url; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置修改时保存URL |
||||
|
* @param url |
||||
|
* @returns |
||||
|
*/ |
||||
|
setEditDataUrl(url: string) { |
||||
|
if (!this.table) { |
||||
|
return; |
||||
|
} |
||||
|
this.table.url.editDataUrl = url; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置删除url |
||||
|
* @param url |
||||
|
* @returns |
||||
|
*/ |
||||
|
setRemoveDataUrl(url: string) { |
||||
|
if (!this.table) { |
||||
|
return; |
||||
|
} |
||||
|
this.table.url.removeDataUrl = url; |
||||
|
} |
||||
|
|
||||
|
private setQueryCriteriaFieldValueHandler(criteria: any, fieldName: string, fieldValue: any) { |
||||
|
if (criteria.criteria && criteria.criteria.length > 0) { |
||||
|
criteria.criteria.forEach((item) => { |
||||
|
if (!item.criteria && item.fieldName && item.fieldName === fieldName) { |
||||
|
item.value = fieldValue; |
||||
|
} else { |
||||
|
this.setQueryCriteriaFieldValueHandler(item.criteria, fieldName, fieldValue); |
||||
|
} |
||||
|
}); |
||||
|
} else if (criteria.fieldName && criteria.fieldName === fieldName) { |
||||
|
criteria.value = fieldValue; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,158 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { nextTick } from 'vue'; |
||||
|
import { Constant, PropsType, GridTools } from '../index'; |
||||
|
import { Base } from '../Base'; |
||||
|
import { criteriaOperator } from '../../../query-builder/criteria'; |
||||
|
import { TableType } from '../types/TableType'; |
||||
|
|
||||
|
// 定义数据结构类型
|
||||
|
interface CriteriaType { |
||||
|
fieldName?: string; |
||||
|
operator: string; |
||||
|
value?: any; |
||||
|
start?: any; |
||||
|
end?: any; |
||||
|
criteria?: CriteriaType[]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* w-grid Criteria相关函数 |
||||
|
*/ |
||||
|
export class Criteria extends Base { |
||||
|
/** |
||||
|
* 查询面板配置的字段对象 |
||||
|
*/ |
||||
|
queryFormFields: any; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.queryFormFields = GridTools.arrayToObject(props.queryFormFields, 'name'); |
||||
|
this.buildCriteria = this.buildCriteria.bind(this); |
||||
|
this.buildURLSearchParams = this.buildURLSearchParams.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据字段值与字段名构建 criteria 查询对象 |
||||
|
* @param value |
||||
|
* @param fieldName |
||||
|
* @returns |
||||
|
*/ |
||||
|
private buildCriteria(value: any, fieldName: string) { |
||||
|
const queryOperator = this.queryFormFields[fieldName]['queryOperator']; |
||||
|
if (!Tools.isEmpty(queryOperator)) { |
||||
|
return { |
||||
|
fieldName: fieldName, |
||||
|
operator: queryOperator, |
||||
|
value: value, |
||||
|
}; |
||||
|
} else if (typeof value === 'boolean' || typeof value === 'number' || (typeof value === 'string' && this.queryFormFields[fieldName]['type'] === 'w-date')) { |
||||
|
return { |
||||
|
fieldName: fieldName, |
||||
|
operator: Constant.CRITERIA_OPERATOR.equals, |
||||
|
value: value, |
||||
|
}; |
||||
|
} else if (Array.isArray(value)) { |
||||
|
return { |
||||
|
fieldName: fieldName, |
||||
|
operator: Constant.CRITERIA_OPERATOR.inSet, |
||||
|
value: value, |
||||
|
}; |
||||
|
} else if (typeof value === 'object' && this.queryFormFields[fieldName]['type'] === 'w-date-range') { |
||||
|
return { |
||||
|
fieldName: fieldName, |
||||
|
operator: Constant.CRITERIA_OPERATOR.betweenInclusive, |
||||
|
start: value['from'], |
||||
|
end: value['to'], |
||||
|
}; |
||||
|
} else { |
||||
|
return { |
||||
|
fieldName: fieldName, |
||||
|
operator: Constant.CRITERIA_OPERATOR.contains, |
||||
|
value: value, |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据请求入参拼上查询面板构建 criteria 查询对象,转换成 URLSearchParams 后返回 |
||||
|
* @param reqParams 请求入参 |
||||
|
* @returns URLSearchParams 对象 |
||||
|
*/ |
||||
|
buildURLSearchParams(reqParams) { |
||||
|
const urlSearchParams = new URLSearchParams(reqParams); |
||||
|
// 处理默认查询条件
|
||||
|
if (Object.keys(this.table.queryCriteria).length > 0) { |
||||
|
urlSearchParams.append('criteria', JSON.stringify(this.table.queryCriteria)); |
||||
|
} |
||||
|
if (this.table.advancedQueryStatus) { |
||||
|
const conditions = this.advancedQueryConditions(); |
||||
|
if (conditions && conditions.criteria.length > 0) { |
||||
|
urlSearchParams.append('criteria', JSON.stringify(conditions)); |
||||
|
} |
||||
|
} else { |
||||
|
const queryForm = this.table.componentRef.getTopRef()?.getQueryForm(); |
||||
|
if (queryForm) { |
||||
|
nextTick(() => { |
||||
|
const queryFormData = queryForm.getData(); |
||||
|
Object.keys(queryFormData).forEach((item) => { |
||||
|
if ( |
||||
|
(!Tools.isEmpty(queryFormData[item]) && !Array.isArray(queryFormData[item])) || |
||||
|
(!Tools.isEmpty(queryFormData[item]) && Array.isArray(queryFormData[item]) && queryFormData[item].length > 0) |
||||
|
) { |
||||
|
// 根据数据进行operator处理
|
||||
|
const criteria = this.buildCriteria(queryFormData[item], item); |
||||
|
urlSearchParams.append('criteria', JSON.stringify(criteria)); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
return urlSearchParams; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 高级查询条件处理,排除掉所有字段名或者值为空的条件 |
||||
|
* @returns |
||||
|
*/ |
||||
|
private advancedQueryConditions() { |
||||
|
let conditions: any = undefined; |
||||
|
if (this.table.advancedQueryModelValue && this.table.advancedQueryModelValue['criteria'][0]['fieldName']) { |
||||
|
conditions = { |
||||
|
...this.table.advancedQueryModelValue, |
||||
|
criteria: this.cleanCriteria(this.table.advancedQueryModelValue['criteria']), |
||||
|
}; |
||||
|
} |
||||
|
return conditions; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* 清除无效criteria |
||||
|
* @param criteria |
||||
|
* @returns |
||||
|
*/ |
||||
|
private cleanCriteria(criteria: CriteriaType[]): CriteriaType[] { |
||||
|
return criteria.filter((c) => { |
||||
|
if (Tools.isEmpty(c.fieldName)) { |
||||
|
return false; |
||||
|
} else if (c.operator === criteriaOperator.between.name || c.operator === criteriaOperator.notBetween.name) { |
||||
|
if (Tools.isEmpty(c.start) && Tools.isEmpty(c.end)) { |
||||
|
return false; |
||||
|
} |
||||
|
} else if ( |
||||
|
c.operator !== criteriaOperator.isBlank.name && |
||||
|
c.operator !== criteriaOperator.notBlank.name && |
||||
|
c.operator !== criteriaOperator.isNull.name && |
||||
|
c.operator !== criteriaOperator.notNull.name && |
||||
|
Tools.isEmpty(c.value) |
||||
|
) { |
||||
|
return false; |
||||
|
} |
||||
|
// 如果存在嵌套的criteria,则递归调用
|
||||
|
if (c.criteria) { |
||||
|
c.criteria = this.cleanCriteria(c.criteria); |
||||
|
} |
||||
|
return true; |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,411 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { toRaw, Ref, ref } from 'vue'; |
||||
|
import { Constant, PropsType, TableType } from '../index'; |
||||
|
import { Base } from '../Base'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 表格拖拽相关函数 |
||||
|
*/ |
||||
|
export class DragAndDrop extends Base { |
||||
|
/** |
||||
|
* 拖拽线样式 |
||||
|
*/ |
||||
|
static DRAG_LINE_STYLE = { |
||||
|
width: '2px', |
||||
|
style: 'dashed', |
||||
|
color: 'orange', |
||||
|
}; |
||||
|
static DRAG_ICON = { |
||||
|
// 拖拽时父节点图标
|
||||
|
name: 'bi-folder-symlink-fill', |
||||
|
color: 'amber', |
||||
|
}; |
||||
|
private static OPERATOR_ADD = 'add'; |
||||
|
private static OPERATOR_REMOVE = 'remove'; |
||||
|
private static POSITION_TOP = 'borderTop'; |
||||
|
private static POSITION_BOTTOM = 'borderBottom'; |
||||
|
|
||||
|
/** |
||||
|
* 拖拽过程中停留在图标与名字区域的计时器 |
||||
|
*/ |
||||
|
timer: any = undefined; |
||||
|
/** |
||||
|
* 拖拽过程中停留在图标与名字区域的时间(用来判定是否需要展开该节点) |
||||
|
*/ |
||||
|
iconAndNameDwellTime: Ref = ref(0); |
||||
|
/** |
||||
|
* 拖拽过程中停留在图标与名字区域行 |
||||
|
*/ |
||||
|
dwellRow: any = undefined; |
||||
|
|
||||
|
// 待更新排序数据
|
||||
|
private updateOrderData = <any>[]; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.onDragStart = this.onDragStart.bind(this); |
||||
|
this.onDragEnd = this.onDragEnd.bind(this); |
||||
|
this.iconAndNameDragOver = this.iconAndNameDragOver.bind(this); |
||||
|
this.onDragOver = this.onDragOver.bind(this); |
||||
|
this.onDragLeave = this.onDragLeave.bind(this); |
||||
|
this.onDrop = this.onDrop.bind(this); |
||||
|
this.setDragIcon = this.setDragIcon.bind(this); |
||||
|
|
||||
|
this.destroyTimer = this.destroyTimer.bind(this); |
||||
|
this.dropTargetIsIconAndName = this.dropTargetIsIconAndName.bind(this); |
||||
|
this.resetOrder = this.resetOrder.bind(this); |
||||
|
this.resetOrderDataHandler = this.resetOrderDataHandler.bind(this); |
||||
|
this.removeRecord = this.removeRecord.bind(this); |
||||
|
this.nodeSplice = this.nodeSplice.bind(this); |
||||
|
this.dragLessThanTarget = this.dragLessThanTarget.bind(this); |
||||
|
this.setOrder = this.setOrder.bind(this); |
||||
|
this.childrenResetOrder = this.childrenResetOrder.bind(this); |
||||
|
this.canDrag = this.canDrag.bind(this); |
||||
|
this.iconAndNameCanDrag = this.iconAndNameCanDrag.bind(this); |
||||
|
this.dragRecordsContains = this.dragRecordsContains.bind(this); |
||||
|
this.setDndStyle = this.setDndStyle.bind(this); |
||||
|
} |
||||
|
|
||||
|
// 拖拽开始
|
||||
|
public onDragStart(e: any, scope: any) { |
||||
|
const img = new Image(); |
||||
|
img.src = Constant.DND_BG_IMAGE; |
||||
|
e.dataTransfer.setDragImage(img, 0, 0); |
||||
|
const selecteds = this.instance.getSelectedRows(); |
||||
|
if ( |
||||
|
selecteds.length > 0 && |
||||
|
selecteds.findIndex((item) => { |
||||
|
return item[Constant.FIELD_NAMES.ROW_KEY] === scope.row[Constant.FIELD_NAMES.ROW_KEY]; |
||||
|
}) > -1 |
||||
|
) { |
||||
|
this.table.store.dragRecords = selecteds; |
||||
|
} else { |
||||
|
this.instance.clearSelected(); |
||||
|
// 触发选择
|
||||
|
scope.row[this.props.selectedField] = true; |
||||
|
this.table.store.dragRecords = [scope.row]; |
||||
|
} |
||||
|
e.dataTransfer.effectAllowed = 'move'; |
||||
|
} |
||||
|
|
||||
|
// 销毁计时器
|
||||
|
private destroyTimer() { |
||||
|
if (this.timer) { |
||||
|
clearInterval(this.timer); |
||||
|
this.timer = undefined; |
||||
|
} |
||||
|
this.iconAndNameDwellTime.value = 0; |
||||
|
this.dwellRow = undefined; |
||||
|
} |
||||
|
|
||||
|
// 拖拽结束
|
||||
|
onDragEnd(e: any, scope: any) { |
||||
|
this.destroyTimer(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 拖拽至图标与名字区域触发 |
||||
|
* @param e |
||||
|
* @param scope |
||||
|
*/ |
||||
|
iconAndNameDragOver(e: any, row: any) { |
||||
|
if (!this.iconAndNameCanDrag(this.table?.store.dragRecords, row[this.props.primaryKey])) { |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP); |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM); |
||||
|
this.setDragIcon(this.table?.rows); |
||||
|
e.dataTransfer.dropEffect = 'none'; |
||||
|
} else { |
||||
|
if (!this.timer) { |
||||
|
this.timer = setInterval(() => { |
||||
|
this.iconAndNameDwellTime.value++; |
||||
|
}, 500); |
||||
|
this.dwellRow = row; |
||||
|
} |
||||
|
row['_dragIcon_'] = DragAndDrop.DRAG_ICON.name; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 拖拽过程触发
|
||||
|
onDragOver(e: any, scope: any, trMiddleHeight: number) { |
||||
|
e.preventDefault(); |
||||
|
if (!this.canDrag(this.table?.store.dragRecords, scope.row[this.props.foreignKey])) { |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP); |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM); |
||||
|
this.setDragIcon(this.table?.rows); |
||||
|
e.dataTransfer.dropEffect = 'none'; |
||||
|
} else { |
||||
|
if (e.offsetY <= trMiddleHeight && !this.dragRecordsContains(scope.row)) { |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM); |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_ADD, DragAndDrop.POSITION_TOP); |
||||
|
} else if (e.offsetY > trMiddleHeight && !this.dragRecordsContains(scope.row)) { |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP); |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_ADD, DragAndDrop.POSITION_BOTTOM); |
||||
|
} |
||||
|
if (!this.dropTargetIsIconAndName(e)) { |
||||
|
this.destroyTimer(); |
||||
|
if (!Tools.isUndefinedOrNull(scope.row[this.props.foreignKey])) { |
||||
|
this.setDragIcon(this.table?.rows, scope.row[this.props.foreignKey]); |
||||
|
} else { |
||||
|
this.setDragIcon(this.table?.rows); |
||||
|
} |
||||
|
} |
||||
|
e.dataTransfer.dropEffect = 'move'; |
||||
|
} |
||||
|
e.stopPropagation(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判定放置目标是否为图标与名字div |
||||
|
* @param e |
||||
|
* @returns |
||||
|
*/ |
||||
|
private dropTargetIsIconAndName(e) { |
||||
|
return typeof e.target.className === 'string' && e.target.className.split(' ').includes('treeGridIconAndName'); |
||||
|
} |
||||
|
|
||||
|
// 拖拽至不可放置区域触发
|
||||
|
onDragLeave(e) { |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP); |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM); |
||||
|
this.destroyTimer(); |
||||
|
} |
||||
|
|
||||
|
// 拖拽放置时触发
|
||||
|
onDrop(e: any, scope: any, trMiddleHeight: number) { |
||||
|
e.preventDefault(); |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP); |
||||
|
this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM); |
||||
|
this.setDragIcon(this.table?.rows); |
||||
|
if (!this.table) { |
||||
|
return false; |
||||
|
} |
||||
|
if ( |
||||
|
this.table.store.dragRecords.findIndex((item) => { |
||||
|
return item[Constant.FIELD_NAMES.ROW_KEY] === scope.row[Constant.FIELD_NAMES.ROW_KEY]; |
||||
|
}) > -1 |
||||
|
) { |
||||
|
return; |
||||
|
} |
||||
|
// 每次放置时清空待更新集合
|
||||
|
this.updateOrderData.splice(0, this.updateOrderData.length); |
||||
|
|
||||
|
if (this.dropTargetIsIconAndName(e)) { |
||||
|
this.table.store.dragRecords.forEach((item) => { |
||||
|
// 从表格数据中将拖拽数据删除
|
||||
|
this.removeRecord(this.table?.rows, item); |
||||
|
|
||||
|
// 重新设置父
|
||||
|
item[this.props.foreignKey] = scope.row[this.props.primaryKey]; |
||||
|
// 将拖动的数据全部加入到父的children中
|
||||
|
if (scope.row.children) { |
||||
|
scope.row.children.push(item); |
||||
|
} else { |
||||
|
scope.row.children = [item]; |
||||
|
} |
||||
|
this.updateOrderData.push(item); |
||||
|
}); |
||||
|
} else { |
||||
|
// 将拖动的数据及其子节点放到目标数据位置,并将目标数据父节点下的子节点重新排序。
|
||||
|
this.resetOrder(e, this.table.store.dragRecords, this.table.rows, scope.row, trMiddleHeight); |
||||
|
} |
||||
|
|
||||
|
// 请求后端更新排序
|
||||
|
if (this.props.dndMode === Constant.DND_MODE.SERVER && this.updateOrderData.length > 0) { |
||||
|
this.tools?.reqApiFM.updates(this.updateOrderData); |
||||
|
} |
||||
|
|
||||
|
this.tools?.em.afterDragAndDrop(this.updateOrderData); |
||||
|
} |
||||
|
|
||||
|
// 重新设置树的排序
|
||||
|
private resetOrder(e, dragRecords, arr, targetData, trMiddleHeight: number) { |
||||
|
for (let i = 0; i < arr.length; i++) { |
||||
|
if (arr[i][Constant.FIELD_NAMES.ROW_KEY] === targetData[Constant.FIELD_NAMES.ROW_KEY]) { |
||||
|
this.resetOrderDataHandler(e, dragRecords, targetData, i, trMiddleHeight); |
||||
|
break; |
||||
|
} else if (arr[i].children && arr[i].children.length > 0) { |
||||
|
this.resetOrder(e, dragRecords, arr[i].children, targetData, trMiddleHeight); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 数据处理
|
||||
|
private resetOrderDataHandler(e, dragRecords, targetData, targetIndex, trMiddleHeight: number) { |
||||
|
dragRecords.forEach((item) => { |
||||
|
// 从表格数据中将拖拽数据删除
|
||||
|
const itemIndex = this.removeRecord(this.table?.rows, item); |
||||
|
if (Tools.isEmpty(targetData[this.props.foreignKey])) { |
||||
|
this.nodeSplice(e, itemIndex, targetIndex, item, targetData, this.table?.rows, trMiddleHeight); |
||||
|
item[this.props.foreignKey] = null; |
||||
|
this.setOrder(this.table?.rows); |
||||
|
} else { |
||||
|
this.childrenResetOrder(e, this.table?.rows, item, targetData, itemIndex, targetIndex, trMiddleHeight); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 删除表格中的数据
|
||||
|
private removeRecord(arr, removeData) { |
||||
|
let index = 0; |
||||
|
for (let i = 0; i < arr.length; i++) { |
||||
|
if (arr[i][Constant.FIELD_NAMES.ROW_KEY] === removeData[Constant.FIELD_NAMES.ROW_KEY]) { |
||||
|
arr.splice(i, 1); |
||||
|
index = i; |
||||
|
break; |
||||
|
} else if (arr[i].children && arr[i].children.length > 0) { |
||||
|
const a = this.removeRecord(arr[i].children, removeData); |
||||
|
if (a > 0) { |
||||
|
index = a; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return index; |
||||
|
} |
||||
|
|
||||
|
// 处理树节点的增加
|
||||
|
private nodeSplice(e, dragIndex, targetIndex, drag, target, arr, trMiddleHeight: number) { |
||||
|
if ( |
||||
|
(Tools.isEmpty(drag[this.props.foreignKey]) && Tools.isEmpty(target[this.props.foreignKey])) || |
||||
|
drag[this.props.foreignKey] === target[this.props.foreignKey] |
||||
|
) { |
||||
|
const lessThanResult = this.dragLessThanTarget(dragIndex, targetIndex); |
||||
|
// 父节点相同的同级处理
|
||||
|
if (e.offsetY <= trMiddleHeight && lessThanResult) { |
||||
|
arr.splice(targetIndex - 1, 0, drag); |
||||
|
} else if (e.offsetY > trMiddleHeight && !lessThanResult) { |
||||
|
arr.splice(targetIndex + 1, 0, drag); |
||||
|
} else { |
||||
|
arr.splice(targetIndex, 0, drag); |
||||
|
} |
||||
|
} else { |
||||
|
// 拖拽节点与目标节点不同级处理
|
||||
|
if (e.offsetY <= trMiddleHeight) { |
||||
|
arr.splice(targetIndex, 0, drag); |
||||
|
} else if (e.offsetY > trMiddleHeight) { |
||||
|
arr.splice(targetIndex + 1, 0, drag); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private dragLessThanTarget(dragIndex, targetIndex) { |
||||
|
if (dragIndex < targetIndex) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 设置排序号
|
||||
|
private setOrder(arr) { |
||||
|
arr.forEach((item, index) => { |
||||
|
item[this.props.dndOrderBy] = index + 1; |
||||
|
// 添加至待更新集合中
|
||||
|
this.updateOrderData.push({ ...toRaw(item), children: null }); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 增加并重新设置父属性
|
||||
|
private childrenResetOrder(e, arr, addData, targetData, dragIndex, targetIndex, trMiddleHeight: number) { |
||||
|
for (let i = 0; i < arr.length; i++) { |
||||
|
if (arr[i][this.props.primaryKey] === targetData[this.props.foreignKey]) { |
||||
|
this.nodeSplice(e, dragIndex, targetIndex, addData, targetData, arr[i].children, trMiddleHeight); |
||||
|
// 修改父
|
||||
|
addData[this.props.foreignKey] = arr[i][this.props.primaryKey]; |
||||
|
// 重新设置排序号
|
||||
|
this.setOrder(arr[i].children); |
||||
|
break; |
||||
|
} else if (arr[i].children && arr[i].children.length > 0) { |
||||
|
this.childrenResetOrder(e, arr[i].children, addData, targetData, dragIndex, targetIndex, trMiddleHeight); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 设置拖拽目标的父节点icon图标
|
||||
|
setDragIcon(arr: any, key: string = '') { |
||||
|
for (let i = 0; i < arr.length; i++) { |
||||
|
if (arr[i].children && arr[i].children.length > 0) { |
||||
|
if (!Tools.isEmpty(key) && arr[i][this.props.primaryKey] === key) { |
||||
|
if (Tools.isEmpty(arr[i]['_dragIcon_']) || arr[i]['_dragIcon_'] !== DragAndDrop.DRAG_ICON.name) { |
||||
|
arr[i]['_dragIcon_'] = DragAndDrop.DRAG_ICON.name; |
||||
|
} |
||||
|
} else { |
||||
|
arr[i]['_dragIcon_'] = ''; |
||||
|
} |
||||
|
this.setDragIcon(arr[i].children, key); |
||||
|
} else { |
||||
|
arr[i]['_dragIcon_'] = ''; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断是否可以拖拽 |
||||
|
* @param dragRecords 拖拽的记录集 |
||||
|
* @param targetParentKey 当前鼠标所在的目标行的父节点key |
||||
|
*/ |
||||
|
private canDrag(dragRecords, targetParentKey) { |
||||
|
if (Tools.isEmpty(targetParentKey)) { |
||||
|
return true; |
||||
|
} |
||||
|
// 判断拖拽的行是否为当前鼠标所在的目标行的父节点(只要存在父,一直往上找)
|
||||
|
let result = true; |
||||
|
let parentKey = targetParentKey; |
||||
|
while (!Tools.isEmpty(parentKey)) { |
||||
|
if ( |
||||
|
dragRecords.findIndex((item) => { |
||||
|
return item[this.props.primaryKey] === parentKey; |
||||
|
}) > -1 |
||||
|
) { |
||||
|
result = false; |
||||
|
parentKey = null; |
||||
|
} else { |
||||
|
parentKey = this.tools?.dataFM.getRow(this.table.rows, parentKey, true)[this.props.foreignKey]; |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断图标与名字区域是否可以拖拽 |
||||
|
* @param dragRecords 拖拽的记录集 |
||||
|
* @param targetPrimaryKey 当前鼠标所在的目标行的主键 |
||||
|
*/ |
||||
|
private iconAndNameCanDrag(dragRecords: any, targetPrimaryKey: any) { |
||||
|
let result = true; |
||||
|
for (let i = 0; i < dragRecords.length; i++) { |
||||
|
const record = dragRecords[i]; |
||||
|
if (record[this.props.primaryKey] === targetPrimaryKey) { |
||||
|
result = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
// 根据传入的数据行判断是否在当前拖拽的记录集合中
|
||||
|
private dragRecordsContains(dataRow) { |
||||
|
if (!this.table) { |
||||
|
return false; |
||||
|
} |
||||
|
return ( |
||||
|
this.table.store.dragRecords.findIndex((item) => { |
||||
|
return item[Constant.FIELD_NAMES.ROW_KEY] === dataRow[Constant.FIELD_NAMES.ROW_KEY]; |
||||
|
}) > -1 |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private setDndStyle(e: any, operator: string, position: string) { |
||||
|
let targetChildrenNodes: any = []; |
||||
|
if (e.target.nodeName === 'TD') { |
||||
|
targetChildrenNodes = e.target?.parentNode?.children; |
||||
|
} else if (e.target.nodeName === 'DIV' && e.target.className.split(' ').includes('treeGridFirstTd')) { |
||||
|
targetChildrenNodes = e.target?.parentNode?.parentNode?.children; |
||||
|
} |
||||
|
if (targetChildrenNodes) { |
||||
|
for (let i = 0; i < targetChildrenNodes.length; i++) { |
||||
|
targetChildrenNodes[i].style[position + 'Width'] = operator === DragAndDrop.OPERATOR_ADD ? DragAndDrop.DRAG_LINE_STYLE.width : ''; |
||||
|
targetChildrenNodes[i].style[position + 'Style'] = operator === DragAndDrop.OPERATOR_ADD ? DragAndDrop.DRAG_LINE_STYLE.style : ''; |
||||
|
targetChildrenNodes[i].style[position + 'Color'] = operator === DragAndDrop.OPERATOR_ADD ? DragAndDrop.DRAG_LINE_STYLE.color : ''; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,112 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { Base } from '../Base'; |
||||
|
import { Constant, PropsType, TableType } from '../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 内联编辑函数 |
||||
|
*/ |
||||
|
export class InlineEdit extends Base { |
||||
|
/** |
||||
|
* 内联编辑时所有组件的 ref 对象 |
||||
|
*/ |
||||
|
componentRef = {}; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.setComponentRef = this.setComponentRef.bind(this); |
||||
|
this.getComponentRef = this.getComponentRef.bind(this); |
||||
|
this.getComponentRefs = this.getComponentRefs.bind(this); |
||||
|
this.exitInlineEdit = this.exitInlineEdit.bind(this); |
||||
|
this.getEditorFieldByName = this.getEditorFieldByName.bind(this); |
||||
|
this.isShowInlineEditor = this.isShowInlineEditor.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置内联编辑时组件的 ref 对象 |
||||
|
* @param el html 元素 |
||||
|
* @param row 行数据 |
||||
|
* @param col 列对象 |
||||
|
*/ |
||||
|
public setComponentRef(el: any, row: any, col: any) { |
||||
|
if (el && !Tools.isEmpty(col.name)) { |
||||
|
this.componentRef[row[Constant.FIELD_NAMES.ROW_KEY] + '_' + col.name] = el; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取内联编辑时组件的 ref 对象 |
||||
|
* @param rowKey 行主键 |
||||
|
* @param colName 列名 |
||||
|
*/ |
||||
|
public getComponentRef(rowKey: string, colName: string) { |
||||
|
return this.componentRef[rowKey + '_' + colName]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取所有内联编辑时组件的 ref 对象,传入空数组获取全部 |
||||
|
* @param rowKey 行主键或者数组 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getComponentRefs(rowKey: string | Array<string>) { |
||||
|
const refs = <any>[]; |
||||
|
if (!Tools.isEmpty(this.componentRef)) { |
||||
|
const filterResult = Object.keys(this.componentRef).filter((item) => { |
||||
|
if (typeof rowKey === 'string') { |
||||
|
return item.startsWith(rowKey + '_'); |
||||
|
} else { |
||||
|
return true; |
||||
|
} |
||||
|
}); |
||||
|
if (filterResult.length > 0) { |
||||
|
filterResult.forEach((key) => { |
||||
|
refs.push(this.componentRef[key]); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
return refs; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 退出内联编辑状态 |
||||
|
*/ |
||||
|
public exitInlineEdit() { |
||||
|
if (this.table) { |
||||
|
this.table.store.inlineEditStatus = Constant.EDIT_STATUS.NONE; |
||||
|
this.table.store.cellSelected = undefined; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据传入的字段名获取表格 editor 中的配置。 |
||||
|
* @param name |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getEditorFieldByName(name: string) { |
||||
|
if (this.table && this.props?.editor?.form?.fields) { |
||||
|
const field = this.props.editor.form.fields.find((item) => item['name'] === name); |
||||
|
return field; |
||||
|
} |
||||
|
return undefined; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否显示内联编辑界面 |
||||
|
* @param col |
||||
|
* @param rowKey |
||||
|
* @returns |
||||
|
*/ |
||||
|
public isShowInlineEditor(col: object, rowKey: string) { |
||||
|
if (Tools.hasOwnProperty(col, 'edit') && !col['edit']) { |
||||
|
return false; |
||||
|
} |
||||
|
if ( |
||||
|
this.getEditorFieldByName(col['name']) && |
||||
|
((this.tools?.dataFM.isSelectedRow(rowKey) && this.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROW) || |
||||
|
this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.ROWS || |
||||
|
(this.tools?.dataFM.isSelectedCell(rowKey, col['name']) && this.table.store.inlineEditStatus === Constant.EDIT_STATUS.CELL)) |
||||
|
) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
@ -0,0 +1,272 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { nextTick } from 'vue'; |
||||
|
import { Base } from '../Base'; |
||||
|
import { Constant, PropsType, TableType } from '../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 表格操作函数 |
||||
|
*/ |
||||
|
export class Operator extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.tableFullscreen = this.tableFullscreen.bind(this); |
||||
|
this.resetHeaderCheckbox = this.resetHeaderCheckbox.bind(this); |
||||
|
this.resetTreeGridExpand = this.resetTreeGridExpand.bind(this); |
||||
|
this.propsValidate = this.propsValidate.bind(this); |
||||
|
this.resize = this.resize.bind(this); |
||||
|
this.resetStyleVariableValue = this.resetStyleVariableValue.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 表格全屏触发函数 |
||||
|
* @param value true: 进入全屏, false: 退出全屏 |
||||
|
*/ |
||||
|
tableFullscreen(value) { |
||||
|
this.table.configStore.isFullscreen = !this.table.configStore.isFullscreen; |
||||
|
if (value) { |
||||
|
this.table.store.location.tableInFullscreenY = this.table.store.location.yLocation; |
||||
|
this.table.store.location.noDataTrInFullscreenY = this.table.store.location.noDataTrYLocation; |
||||
|
} else { |
||||
|
this.table.store.location.yLocation = this.table.store.location.tableInFullscreenY; |
||||
|
this.table.store.location.noDataTrYLocation = this.table.store.location.noDataTrInFullscreenY; |
||||
|
this.table.store.location.tableInFullscreenY = 0; |
||||
|
this.table.store.location.noDataTrInFullscreenY = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重新设置表头勾选框状态 |
||||
|
*/ |
||||
|
public resetHeaderCheckbox() { |
||||
|
this.tableError(); |
||||
|
if (this.table) { |
||||
|
if (this.props.checkboxSelection) { |
||||
|
const ticked_ = <any>[]; |
||||
|
const rows = this.table?.rows || []; |
||||
|
rows.forEach((item) => { |
||||
|
if (item[this.props.tickedField]) { |
||||
|
ticked_.push(item); |
||||
|
} |
||||
|
}); |
||||
|
if (ticked_.length === rows.length && rows.length > 0) { |
||||
|
// 全部勾选设置为 true
|
||||
|
this.table.store.headerTicked = true; |
||||
|
} else if (ticked_.length > 0) { |
||||
|
// 存在一条勾选的记录设置为 null
|
||||
|
this.table.store.headerTicked = null; |
||||
|
} else { |
||||
|
// 一条勾选的记录都没有设置为 false
|
||||
|
this.table.store.headerTicked = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重新设置树表格的展开状态 |
||||
|
* @param arr 需要设置的行数据集合 |
||||
|
* @param expandStatus 需要设置的状态 |
||||
|
*/ |
||||
|
public resetTreeGridExpand(arr: Array<any>, expandStatus: boolean) { |
||||
|
if (!this.props.tree) { |
||||
|
return; |
||||
|
} |
||||
|
arr.forEach((item) => { |
||||
|
item[Constant.FIELD_NAMES.EXPAND] = expandStatus; |
||||
|
if (item.children && item.children.length > 0) { |
||||
|
this.resetTreeGridExpand(item.children, expandStatus); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 表格属性校验 |
||||
|
*/ |
||||
|
propsValidate() { |
||||
|
if ( |
||||
|
!Tools.isEmpty(this.props.groupMode) && |
||||
|
((typeof this.props.groupByField === 'string' && Tools.isEmpty(this.props.groupByField)) || |
||||
|
(Array.isArray(this.props.groupByField) && this.props.groupByField.length === 0)) |
||||
|
) { |
||||
|
console.warn('[w-grid]Configured the `groupMode` property but did not configure the `groupByField` property.'); |
||||
|
} else if (!Tools.isEmpty(this.props.groupMode)) { |
||||
|
const groupByField = |
||||
|
typeof this.props.groupByField === 'string' ? [this.props.groupByField] : Array.isArray(this.props.groupByField) ? this.props.groupByField : undefined; |
||||
|
if (groupByField && this.table?.columns.findIndex((item) => groupByField.includes(item.name)) === -1) { |
||||
|
throw new Error('[w-grid]`groupByField` configuration cannot be found in the table columns.'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 表格大小变化触发函数 |
||||
|
* @returns |
||||
|
*/ |
||||
|
resize() { |
||||
|
if (!this || !this.table) { |
||||
|
return; |
||||
|
} |
||||
|
const tableElement = this.instance?.getHtmlElement(); |
||||
|
if (!tableElement) { |
||||
|
return; |
||||
|
} |
||||
|
this.table.store.resizeFlag = !this.table.store.resizeFlag; |
||||
|
this.table.componentRef.getTopRef()?.handleQueryFormShowField(); |
||||
|
this.table.componentRef.getTopRef()?.handleToolbarActions(); |
||||
|
if (tableElement) { |
||||
|
this.table.store.location.yLocation = tableElement.getBoundingClientRect().y; |
||||
|
if (tableElement.getElementsByClassName('noDataTr').length > 0) { |
||||
|
this.table.store.location.noDataTrYLocation = tableElement.getElementsByClassName('noDataTr')[0].getBoundingClientRect().y; |
||||
|
} |
||||
|
this.table.store.location.topHeight = tableElement.getElementsByClassName('q-table__top')[0]?.clientHeight; |
||||
|
this.table.store.location.middleWidth = tableElement.getElementsByClassName('q-table__middle')[0]?.clientWidth; |
||||
|
this.table.store.location.middleScrollWidth = tableElement.getElementsByClassName('q-table__middle')[0]?.scrollWidth; |
||||
|
this.table.store.location.columnHeadHeight = tableElement.getElementsByTagName('thead')[0]?.clientHeight; |
||||
|
this.table.store.location.hideHeaderNoDataHeight = tableElement.getElementsByClassName('q-table__middle')[0]?.clientHeight; |
||||
|
// 判断是否有数据,没数据修改 middleHeight
|
||||
|
if ((this.table.rows && this.table.rows.length > 0) || this.props.hideBottom) { |
||||
|
this.table.store.location.middleHeight = tableElement.getElementsByClassName('q-table__middle')[0]?.clientHeight; |
||||
|
} else { |
||||
|
let scrollHeight = 0; |
||||
|
if (this.table.store.location.middleScrollWidth - this.table.store.location.middleWidth > 0) { |
||||
|
scrollHeight = 15; |
||||
|
} |
||||
|
this.table.store.location.middleHeight = this.table.store.location.columnHeadHeight + scrollHeight; |
||||
|
} |
||||
|
let titleTotalHeight = tableElement.getElementsByTagName('thead')[0]?.offsetHeight; |
||||
|
// 无数据时列头会增加一行,多表头的top距离计算会出错,需减掉多出来的提示行。
|
||||
|
if (this.table.rows.length === 0) { |
||||
|
const noDataTrHeight = tableElement.getElementsByClassName('noDataTr')[0]?.offsetHeight; |
||||
|
if (titleTotalHeight && titleTotalHeight > 0) { |
||||
|
titleTotalHeight = titleTotalHeight - noDataTrHeight; |
||||
|
} else { |
||||
|
titleTotalHeight = noDataTrHeight; |
||||
|
} |
||||
|
} |
||||
|
this.table.store.location.titleTotalHeight = titleTotalHeight; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重新设置样式变量值 |
||||
|
* @param time 延迟时间 |
||||
|
*/ |
||||
|
public resetStyleVariableValue(time: number = 500) { |
||||
|
const tableElement = this.instance.getHtmlElement(); |
||||
|
setTimeout(() => { |
||||
|
if (!this.table) { |
||||
|
this.tableError(); |
||||
|
return; |
||||
|
} |
||||
|
if (tableElement) { |
||||
|
this.table.store.location.yLocation = tableElement.getBoundingClientRect()?.y; |
||||
|
if (tableElement.getElementsByClassName('noDataTr').length > 0) { |
||||
|
this.table.store.location.noDataTrYLocation = tableElement.getElementsByClassName('noDataTr')[0]?.getBoundingClientRect().y; |
||||
|
} |
||||
|
this.table.store.location.topHeight = tableElement.getElementsByClassName('q-table__top')[0]?.clientHeight; |
||||
|
this.table.store.location.bottomHeight = tableElement.getElementsByClassName('q-table__bottom')[0]?.clientHeight; |
||||
|
this.table.store.location.middleWidth = tableElement.getElementsByClassName('q-table__middle')[0]?.clientWidth; |
||||
|
this.table.store.location.middleScrollWidth = tableElement.getElementsByClassName('q-table__middle')[0]?.scrollWidth; |
||||
|
this.table.store.location.columnHeadHeight = tableElement.getElementsByTagName('thead')[0]?.clientHeight; |
||||
|
this.table.store.location.hideHeaderNoDataHeight = tableElement.getElementsByClassName('q-table__middle')[0]?.clientHeight; |
||||
|
// 判断是否有数据,没数据修改 middleHeight
|
||||
|
if (this.table?.rows?.length > 0 || this.props.hideBottom) { |
||||
|
this.table.store.location.middleHeight = tableElement.getElementsByClassName('q-table__middle')[0]?.clientHeight; |
||||
|
} else { |
||||
|
let scrollHeight = 0; |
||||
|
if (this.table.store.location.middleScrollWidth - this.table.store.location.middleWidth > 0) { |
||||
|
scrollHeight = 15; |
||||
|
} |
||||
|
this.table.store.location.middleHeight = this.table.store.location.columnHeadHeight + scrollHeight; |
||||
|
} |
||||
|
|
||||
|
const tableDom = tableElement.getElementsByTagName('table')[0]; |
||||
|
if (this.table.configStore.stickyNum > 0) { |
||||
|
if (this.table.componentRef.getHeaderRef()?.getColumnTitleState()?.columnTitleRowNum > 1) { |
||||
|
const theadDom = tableDom.getElementsByTagName('thead')[0]; |
||||
|
const theadTrDom = theadDom.getElementsByTagName('tr'); |
||||
|
tableDom.style.setProperty('--columnWidth' + '-' + 0 + '-' + 0, 0 + 'px'); |
||||
|
for (let i = 0; i < theadDom.getElementsByTagName('tr').length; i++) { |
||||
|
const theadThDom = theadTrDom[i].getElementsByTagName('th'); |
||||
|
for (let k = 0; k < theadThDom.length; k++) { |
||||
|
tableDom.style.setProperty('--columnWidth' + '-' + (i + 1) + '-' + (k + 1), theadThDom[k].offsetWidth + 'px'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const column1Width = tableDom.getElementsByTagName('td')[0]?.offsetWidth; |
||||
|
const column2Width = tableDom.getElementsByTagName('td')[1]?.offsetWidth; |
||||
|
const column3Width = tableDom.getElementsByTagName('td')[2]?.offsetWidth; |
||||
|
const column4Width = tableDom.getElementsByTagName('td')[3]?.offsetWidth; |
||||
|
const column5Width = tableDom.getElementsByTagName('td')[4]?.offsetWidth; |
||||
|
const column6Width = tableDom.getElementsByTagName('td')[5]?.offsetWidth; |
||||
|
const column7Width = tableDom.getElementsByTagName('td')[6]?.offsetWidth; |
||||
|
const column8Width = tableDom.getElementsByTagName('td')[7]?.offsetWidth; |
||||
|
const column9Width = tableDom.getElementsByTagName('td')[8]?.offsetWidth; |
||||
|
|
||||
|
tableDom.style.setProperty('--column1Width', column1Width + 'px'); |
||||
|
tableDom.style.setProperty('--column2Width', column1Width + column2Width + 'px'); |
||||
|
tableDom.style.setProperty('--column3Width', column1Width + column2Width + column3Width + 'px'); |
||||
|
tableDom.style.setProperty('--column4Width', column1Width + column2Width + column3Width + column4Width + 'px'); |
||||
|
tableDom.style.setProperty('--column5Width', column1Width + column2Width + column3Width + column4Width + column5Width + 'px'); |
||||
|
tableDom.style.setProperty('--column6Width', column1Width + column2Width + column3Width + column4Width + column5Width + column6Width + 'px'); |
||||
|
tableDom.style.setProperty( |
||||
|
'--column7Width', |
||||
|
column1Width + column2Width + column3Width + column4Width + column5Width + column6Width + column7Width + 'px', |
||||
|
); |
||||
|
tableDom.style.setProperty( |
||||
|
'--column8Width', |
||||
|
column1Width + column2Width + column3Width + column4Width + column5Width + column6Width + column7Width + column8Width + 'px', |
||||
|
); |
||||
|
tableDom.style.setProperty( |
||||
|
'--column9Width', |
||||
|
column1Width + column2Width + column3Width + column4Width + column5Width + column6Width + column7Width + column8Width + column9Width + 'px', |
||||
|
); |
||||
|
} |
||||
|
let titleTotalHeight = tableDom.getElementsByTagName('thead')[0]?.offsetHeight; |
||||
|
// 无数据时列头会增加一行,多表头的top距离计算会出错,需减掉多出来的提示行。
|
||||
|
if (this.table.rows.length === 0) { |
||||
|
const noDataTrHeight = tableElement.getElementsByClassName('noDataTr')[0]?.offsetHeight; |
||||
|
if (titleTotalHeight && titleTotalHeight > 0) { |
||||
|
titleTotalHeight = titleTotalHeight - noDataTrHeight; |
||||
|
} else { |
||||
|
titleTotalHeight = noDataTrHeight; |
||||
|
} |
||||
|
} |
||||
|
this.table.store.location.titleTotalHeight = titleTotalHeight; |
||||
|
} |
||||
|
}, time); |
||||
|
|
||||
|
tableElement.getElementsByTagName('table')[0].style.setProperty('--tableHeadBgColor', this.table?.color.headBgColor); |
||||
|
tableElement.getElementsByTagName('table')[0].style.setProperty('--stickyBgColor', this.table?.color.stickyBgColor); |
||||
|
tableElement.getElementsByTagName('table')[0].style.setProperty('--tableBorderColor', this.table?.color.borderColor); |
||||
|
tableElement.getElementsByTagName('table')[0].style.setProperty('--tableColumnTitleHeight', (this.tools?.cm.denseHeader.value ? 28 : 48) + 'px'); |
||||
|
let headerPadding = '8px'; |
||||
|
if (this.tools?.cm.denseHeader) { |
||||
|
headerPadding = '4px'; |
||||
|
} |
||||
|
tableElement.getElementsByTagName('table')[0].style.setProperty('--tableHeaderPadding', headerPadding); |
||||
|
let bodyPadding = '8px'; |
||||
|
if (this.tools?.cm.denseBody) { |
||||
|
bodyPadding = '4px'; |
||||
|
} |
||||
|
tableElement.getElementsByTagName('table')[0].style.setProperty('--tableBodyPadding', bodyPadding); |
||||
|
tableElement.getElementsByTagName('table')[0].style.setProperty('--tableBodyHeight', (this.tools?.cm.denseBody.value ? 24 : 48) + 'px'); |
||||
|
nextTick(() => { |
||||
|
if (this.tools?.cm.denseBody.value && !this.props.hideBottom) { |
||||
|
if (tableElement.getElementsByClassName('q-table__bottom').length > 0) { |
||||
|
tableElement.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomHeight', 33 + 'px'); |
||||
|
tableElement.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomButtonHeight', 24 + 'px'); |
||||
|
} |
||||
|
} else if (!this.props.hideBottom && tableElement.getElementsByClassName('q-table__bottom').length > 0) { |
||||
|
tableElement.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomHeight', 50 + 'px'); |
||||
|
tableElement.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomButtonHeight', 40 + 'px'); |
||||
|
} |
||||
|
}); |
||||
|
if (this.props.title || this.props.toolbarActions.length > 0 || this.props.configButton) { |
||||
|
tableElement.getElementsByClassName('q-table__top')[0].style.setProperty('--tableTopPadding', '8px'); |
||||
|
} else { |
||||
|
tableElement.getElementsByClassName('q-table__top')[0].style.setProperty('--tableTopPadding', '0px'); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,339 @@ |
|||||
|
import { $t, axios, noErrorAxios, NotifyManager, Tools, TreeBuilder, ServerExceptionHandler } from '@/platform'; |
||||
|
import { toRaw } from 'vue'; |
||||
|
import { Base } from '../Base'; |
||||
|
import { Constant, PropsType, TableType } from '../index'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 请求后端API函数 |
||||
|
*/ |
||||
|
export class RequestApi extends Base { |
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.fetchData = this.fetchData.bind(this); |
||||
|
this.treeFetchDataByForeignKey = this.treeFetchDataByForeignKey.bind(this); |
||||
|
this.save = this.save.bind(this); |
||||
|
this.updates = this.updates.bind(this); |
||||
|
this.remove = this.remove.bind(this); |
||||
|
|
||||
|
this.requestHandle = this.requestHandle.bind(this); |
||||
|
this.responseHandle = this.responseHandle.bind(this); |
||||
|
this.setExpandDatas = this.setExpandDatas.bind(this); |
||||
|
this.setTreeExpandDatas = this.setTreeExpandDatas.bind(this); |
||||
|
this.setAlongGroupExpandDatas = this.setAlongGroupExpandDatas.bind(this); |
||||
|
this.getFetchUrl = this.getFetchUrl.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 请求数据 |
||||
|
* @param ops 分页排序信息 |
||||
|
*/ |
||||
|
async fetchData(ops: any) { |
||||
|
this.tools?.editFM.exitInlineEdit(); |
||||
|
|
||||
|
this.table.store.loading = true; |
||||
|
const resp: any = await this.requestHandle(ops); |
||||
|
this.table.store.loading = false; |
||||
|
|
||||
|
// 处理返回的结果
|
||||
|
this.responseHandle(resp, ops); |
||||
|
|
||||
|
this.tools?.dataFM.setExtraProperty(this.table.rows, ops); |
||||
|
this.tools?.opFM.resetHeaderCheckbox(); |
||||
|
this.tools?.opFM.resetStyleVariableValue(100); |
||||
|
// 判断数据加载后是否展开树形表格
|
||||
|
if (this.table.store.expand) { |
||||
|
if (this.props.treeLazyLoad) { |
||||
|
this.table.store.alreadyLoadAllData = true; |
||||
|
this.table.store.loading = true; |
||||
|
const datas = await this.tools?.reqApiFM.treeFetchDataByForeignKey(); |
||||
|
this.tools?.apiFM.localMode.setLocalData(datas); |
||||
|
this.table.store.loading = false; |
||||
|
this.tools?.apiFM.operator.expand(undefined, true); |
||||
|
} else { |
||||
|
this.tools?.apiFM.operator.expand(); |
||||
|
} |
||||
|
} else if (this.props.tree && this.props.treeLazyLoad && this.table.store.expandDatas.length > 0) { |
||||
|
for (let i = 0; i < this.table.store.expandDatas.length; i++) { |
||||
|
const primaryKey = this.table.store.expandDatas[i]; |
||||
|
if (!this.table.store.lazyloadNoChildrenDatas.includes(primaryKey)) { |
||||
|
const childrenData = await this.tools?.reqApiFM.treeFetchDataByForeignKey(primaryKey); |
||||
|
if (childrenData.length > 0) { |
||||
|
this.tools?.apiFM.localMode.addLocalData(childrenData); |
||||
|
} else { |
||||
|
const row = this.tools?.dataFM.getRow(this.table.rows, primaryKey, false); |
||||
|
if (row) { |
||||
|
row[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] = true; |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
const row = this.tools?.dataFM.getRow(this.table.rows, primaryKey, false); |
||||
|
if (row) { |
||||
|
row[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// 触发请求完成后事件
|
||||
|
this.tools?.em.afterRequestData(toRaw(this.table.rows)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 树表格根据关系字段查询 |
||||
|
* @param primaryKey 加载哪个主键下面的子节点,不传入加载所有 |
||||
|
*/ |
||||
|
async treeFetchDataByForeignKey(primaryKey?: string) { |
||||
|
const url = this.getFetchUrl(); |
||||
|
const urlSearchParams = new URLSearchParams({ pageable: false }); |
||||
|
if (primaryKey) { |
||||
|
const criteria = { |
||||
|
fieldName: this.props.foreignKey, |
||||
|
operator: Constant.CRITERIA_OPERATOR.equals, |
||||
|
value: primaryKey, |
||||
|
}; |
||||
|
urlSearchParams.append('criteria', JSON.stringify(criteria)); |
||||
|
} |
||||
|
const resp = await axios.get(url, { params: urlSearchParams }).catch((error) => { |
||||
|
console.error('[w-grid]fetch data error:', error); |
||||
|
this.table.store.loading = false; |
||||
|
}); |
||||
|
let resultData: any = []; |
||||
|
if (resp && resp.data) { |
||||
|
const responseData = resp.data; |
||||
|
if (Array.isArray(responseData)) { |
||||
|
resultData = responseData; |
||||
|
} else if (typeof responseData === 'object' && responseData.content) { |
||||
|
resultData = responseData.content; |
||||
|
} |
||||
|
} |
||||
|
return resultData; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 新增、修改、复制、新增顶级节点、新增子节点保存数据 |
||||
|
* @param method 请求方式 |
||||
|
* @param data 数据 |
||||
|
* @param url url |
||||
|
* @param formRef form对象 |
||||
|
* @param callback 请求成功回调 |
||||
|
* @param errorCallback 请求错误回调 |
||||
|
*/ |
||||
|
save(method: string, data: any, url: string, formRef: any, callback: any, errorCallback: any) { |
||||
|
const requestParams = { |
||||
|
method: method, |
||||
|
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
||||
|
data: data, |
||||
|
url: url, |
||||
|
}; |
||||
|
noErrorAxios(requestParams) |
||||
|
.then((resp) => { |
||||
|
if (callback) { |
||||
|
callback(resp); |
||||
|
} |
||||
|
NotifyManager.info($t('tip.operationSuccess') || ''); |
||||
|
this.tools?.em.afterEditorDataSubmit({ grid: this.instance, data: resp.data }); |
||||
|
}) |
||||
|
.catch((error) => { |
||||
|
if (error?.code === 1001) { |
||||
|
// 验证错误
|
||||
|
formRef?.setValidationErrors(error.data); |
||||
|
} else { |
||||
|
ServerExceptionHandler.handle(error); |
||||
|
} |
||||
|
if (errorCallback) { |
||||
|
errorCallback(error); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 批量更新数据 |
||||
|
* @param data 数据集合 |
||||
|
* @param callback 回调函数 |
||||
|
*/ |
||||
|
updates(data: Array<any>, callback: any = undefined) { |
||||
|
const requestParams = { |
||||
|
method: 'PUT', |
||||
|
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
||||
|
data: data, |
||||
|
url: this.table?.url.dataUrl + '/updates', |
||||
|
}; |
||||
|
noErrorAxios(requestParams) |
||||
|
.then((resp) => { |
||||
|
if (!Tools.isEmpty(callback)) { |
||||
|
callback(resp?.data); |
||||
|
} |
||||
|
}) |
||||
|
.catch((error) => { |
||||
|
if (error?.code === 1001) { |
||||
|
// 验证错误
|
||||
|
NotifyManager.error('服务器验证未通过'); |
||||
|
} else { |
||||
|
ServerExceptionHandler.handle(error); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除数据 |
||||
|
* @param ids 删除的主键集合 |
||||
|
*/ |
||||
|
remove(ids: Array<any>) { |
||||
|
const requestParams: any = { |
||||
|
method: 'DELETE', |
||||
|
url: this.table.url.removeDataUrl || this.table.url.dataUrl, |
||||
|
data: ids, |
||||
|
}; |
||||
|
axios(requestParams) |
||||
|
.then((resp) => { |
||||
|
// 触发请求完成后事件
|
||||
|
this.tools?.em.afterRemove(resp?.data); |
||||
|
NotifyManager.info($t('tip.operationSuccess') || ''); |
||||
|
if (this.props.refreshData || !this.props.tree) { |
||||
|
this.tools?.apiFM.operator.refreshGrid(); |
||||
|
} else { |
||||
|
this.tools?.apiFM.localMode.removeLocalData(resp?.data); |
||||
|
} |
||||
|
}) |
||||
|
.catch((error) => { |
||||
|
console.error('[w-grid]Remove error:', error); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private async requestHandle(ops: any) { |
||||
|
const reqParams: any = {}; |
||||
|
if (this.props.pageable && !this.props.tree && !this.props.localMode) { |
||||
|
reqParams.page = ops.pagination.page; |
||||
|
reqParams.size = ops.pagination.rowsPerPage; |
||||
|
} |
||||
|
reqParams.pageable = this.props.tree || this.props.localMode ? false : this.props.pageable; |
||||
|
if (ops.pagination.sortBy && ops.pagination.sortBy !== '') { |
||||
|
if (ops.pagination.descending) { |
||||
|
reqParams.sortBy = '-' + ops.pagination.sortBy; |
||||
|
reqParams.descending = ops.pagination.descending; |
||||
|
} else { |
||||
|
reqParams.sortBy = ops.pagination.sortBy; |
||||
|
} |
||||
|
} else if (this.props.sortBy && this.props.sortBy.length > 0) { |
||||
|
reqParams.sortBy = this.props.sortBy; |
||||
|
} |
||||
|
|
||||
|
// 后台 RestCrudController 查询
|
||||
|
let urlSearchParams = this.tools?.criteriaFM.buildURLSearchParams(reqParams); |
||||
|
|
||||
|
// 请求前事件触发
|
||||
|
const _requestParams = await this.tools?.em.beforeRequestData(urlSearchParams); |
||||
|
if (_requestParams) { |
||||
|
urlSearchParams = _requestParams; |
||||
|
} |
||||
|
const url = this.getFetchUrl(); |
||||
|
|
||||
|
// 树型表格懒加载
|
||||
|
if (this.props.tree && this.props.treeLazyLoad && urlSearchParams) { |
||||
|
const criteria = { |
||||
|
fieldName: this.props.foreignKey, |
||||
|
operator: Constant.CRITERIA_OPERATOR.isNull, |
||||
|
}; |
||||
|
urlSearchParams.append('criteria', JSON.stringify(criteria)); |
||||
|
} |
||||
|
const resp = await axios.get(url, { params: urlSearchParams }).catch((error) => { |
||||
|
console.error('[w-grid]fetch data error:', error); |
||||
|
this.table.store.loading = false; |
||||
|
}); |
||||
|
return resp; |
||||
|
} |
||||
|
|
||||
|
private responseHandle(resp: any, ops: any) { |
||||
|
if (resp && resp.data) { |
||||
|
if (!this.table.store.expand) { |
||||
|
// 记录当前已展开记录
|
||||
|
this.setExpandDatas(); |
||||
|
} |
||||
|
const responseData = resp.data; |
||||
|
if (Array.isArray(responseData)) { |
||||
|
this.table.rows = responseData; |
||||
|
this.table.store.pagination.rowsNumber = responseData.length; |
||||
|
} else if (typeof responseData === 'object' && responseData.content) { |
||||
|
if (this.props.pageable) { |
||||
|
this.table.store.pagination.page = this.table.store.pagination.reqPageStart === 0 ? responseData.number + 1 : responseData.number; |
||||
|
this.table.store.pagination.rowsPerPage = responseData.size || this.table.store.pagination.rowsPerPage; |
||||
|
} |
||||
|
this.table.store.pagination.rowsNumber = responseData.totalElements; |
||||
|
this.table.rows = responseData.content; |
||||
|
} else { |
||||
|
this.table.rows = []; |
||||
|
} |
||||
|
|
||||
|
if (this.props.tree) { |
||||
|
if (Array.isArray(responseData)) { |
||||
|
this.table.store.pagination.rowsNumber = responseData.length; |
||||
|
this.table.store.pagination.rowsPerPage = 0; |
||||
|
this.table.rows = |
||||
|
this.props.treeRelationship === 'parent' ? TreeBuilder.build(responseData, this.props.foreignKey, this.props.primaryKey) : responseData; |
||||
|
} else if (typeof responseData === 'object' && responseData.content) { |
||||
|
this.table.store.pagination.rowsNumber = responseData.content.length; |
||||
|
this.table.store.pagination.rowsPerPage = 0; |
||||
|
this.table.rows = |
||||
|
this.props.treeRelationship === 'parent' |
||||
|
? TreeBuilder.build(responseData.content, this.props.foreignKey, this.props.primaryKey) |
||||
|
: responseData.content; |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
this.table.rows = []; |
||||
|
} |
||||
|
this.table.store.pagination.sortBy = ops.pagination.sortBy; |
||||
|
this.table.store.pagination.descending = ops.pagination.descending; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置 store 中记录的当前已展开记录 |
||||
|
* @param |
||||
|
*/ |
||||
|
private setExpandDatas() { |
||||
|
if (this.props.tree) { |
||||
|
this.table.store.expandDatas = []; |
||||
|
this.table.store.lazyloadNoChildrenDatas = []; |
||||
|
this.setTreeExpandDatas(toRaw(this.table.rows)); |
||||
|
} else if (this.props.groupMode === Constant.GROUP_MODE.ALONE) { |
||||
|
this.setAlongGroupExpandDatas(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private setTreeExpandDatas(array: Array<any>) { |
||||
|
array.forEach((item) => { |
||||
|
if (item[Constant.FIELD_NAMES.EXPAND]) { |
||||
|
if (this.props.treeLazyLoad) { |
||||
|
const parents = this.tools?.apiFM.getData.getCascadeParentsData(item, null); |
||||
|
if (parents.length === 0 || parents.findIndex((parent) => !parent[Constant.FIELD_NAMES.EXPAND]) === -1) { |
||||
|
this.table.store.expandDatas.push(item[this.props.primaryKey]); |
||||
|
} |
||||
|
} else { |
||||
|
this.table.store.expandDatas.push(item[this.props.primaryKey]); |
||||
|
} |
||||
|
} |
||||
|
if (item[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] && (!item.children || item.children.length === 0)) { |
||||
|
this.table.store.lazyloadNoChildrenDatas.push(item[this.props.primaryKey]); |
||||
|
} |
||||
|
if (item.children?.length > 0) { |
||||
|
this.setTreeExpandDatas(item.children); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private setAlongGroupExpandDatas() { |
||||
|
this.table.configStore.aloneGroupRecords.forEach((item) => { |
||||
|
if (item['expand'] && !this.table.store.expandDatas.includes(item['groupName'])) { |
||||
|
this.table.store.expandDatas.push(item['groupName']); |
||||
|
} else if (!item['expand']) { |
||||
|
const index = this.table.store.expandDatas.findIndex((tmp) => tmp === item['groupName']); |
||||
|
if (index > -1) { |
||||
|
this.table.store.expandDatas.splice(index, 1); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private getFetchUrl() { |
||||
|
return this.table?.url.fetchDataUrl || this.table?.url.dataUrl || ''; |
||||
|
} |
||||
|
} |
@ -0,0 +1,440 @@ |
|||||
|
import { toRaw } from 'vue'; |
||||
|
import sortArray from 'sort-array'; |
||||
|
import { Tools } from '@/platform'; |
||||
|
import { Base } from '../Base'; |
||||
|
import { Constant, GridTools, PropsType, TableType } from '../index'; |
||||
|
|
||||
|
/** |
||||
|
* 行数据相关处理函数 |
||||
|
*/ |
||||
|
export class RowData extends Base { |
||||
|
private aloneGroupRowIndex: number = 0; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.getRow = this.getRow.bind(this); |
||||
|
this.getRowsByTarget = this.getRowsByTarget.bind(this); |
||||
|
this.isSelectedRow = this.isSelectedRow.bind(this); |
||||
|
this.isSelectedCell = this.isSelectedCell.bind(this); |
||||
|
this.setOldValue = this.setOldValue.bind(this); |
||||
|
this.isLastRow = this.isLastRow.bind(this); |
||||
|
this.checkLastRow = this.checkLastRow.bind(this); |
||||
|
this.setExtraProperty = this.setExtraProperty.bind(this); |
||||
|
this.arrayOrderBy = this.arrayOrderBy.bind(this); |
||||
|
this.mergeSortFields = this.mergeSortFields.bind(this); |
||||
|
this.mergeDefaultSortBy = this.mergeDefaultSortBy.bind(this); |
||||
|
this.rowDataExtraPropertyHandle = this.rowDataExtraPropertyHandle.bind(this); |
||||
|
this.mergeGroupBy = this.mergeGroupBy.bind(this); |
||||
|
this.aloneGroupBy = this.aloneGroupBy.bind(this); |
||||
|
this.setRowIndex = this.setRowIndex.bind(this); |
||||
|
this.initRowDataExtraProperty = this.initRowDataExtraProperty.bind(this); |
||||
|
this.getInitRowTicked = this.getInitRowTicked.bind(this); |
||||
|
this.getInitRowSelected = this.getInitRowSelected.bind(this); |
||||
|
this.pushRow = this.pushRow.bind(this); |
||||
|
this.rowsKeyIsEmptyError = this.rowsKeyIsEmptyError.bind(this); |
||||
|
this.rowsKeyNotExistError = this.rowsKeyNotExistError.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 从传入的行记录集合中查找符合条件的行数据(树型表格也适用) |
||||
|
* @param array 集合。 |
||||
|
* @param key 行数据主键或行数据前端主键。 |
||||
|
* @param parentFlag key为父ID标识,当key为父ID时,通过行数据主键进行匹配,否则根据传入的 fieldName 进行匹配。 |
||||
|
* @param fieldName 字段名称,默认为行数据前端主键名。 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getRow(array: Array<any>, key: string, parentFlag: boolean, fieldName: string = Constant.FIELD_NAMES.ROW_KEY): any { |
||||
|
let result = undefined; |
||||
|
for (let i = 0; i < array.length; i++) { |
||||
|
const item = array[i]; |
||||
|
if (parentFlag ? item[this.props.primaryKey] === key : item[fieldName] === key || item[this.props.primaryKey] === key) { |
||||
|
result = item; |
||||
|
break; |
||||
|
} else if (this.props.tree && item.children && item.children.length > 0) { |
||||
|
const temp = this.getRow(item.children, key, parentFlag); |
||||
|
if (temp) { |
||||
|
result = temp; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据传入的数据集合及目标对象查找表格行记录 |
||||
|
* @param rows 表格数据集合 |
||||
|
* @param target 目标对象,可以是行对象、行对象数组、行主键、前端主键。 |
||||
|
* @returns 行数据数组集合 |
||||
|
*/ |
||||
|
public getRowsByTarget(rows: Array<any>, target: any) { |
||||
|
const result = []; |
||||
|
if (target && Array.isArray(target) && target.length > 0) { |
||||
|
target.forEach((item) => { |
||||
|
this.rowsKeyIsEmptyError(item); |
||||
|
this.pushRow(rows, result, item); |
||||
|
}); |
||||
|
} else if (!Tools.isEmpty(target)) { |
||||
|
this.pushRow(rows, result, target); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判定传入的主键是否处于行选中状态 |
||||
|
* @param rowKey 前端主键 |
||||
|
*/ |
||||
|
public isSelectedRow(rowKey: string) { |
||||
|
if (this.table && !Tools.isEmpty(rowKey)) { |
||||
|
const selected = this.tools?.apiFM.getData.getSelectedRow(); |
||||
|
if (selected) { |
||||
|
return rowKey === selected[Constant.FIELD_NAMES.ROW_KEY]; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判定传入的行主键及列名是否处于单元格选中状态 |
||||
|
* @param rowKey 前端主键 |
||||
|
* @param colName 列名 |
||||
|
*/ |
||||
|
public isSelectedCell(rowKey: string, colName: string) { |
||||
|
if (this.table && !Tools.isEmpty(rowKey) && !Tools.isEmpty(colName)) { |
||||
|
const selected = this.tools?.apiFM.getData.getSelectedCell(); |
||||
|
if (selected?.colName && rowKey === selected['row'][Constant.FIELD_NAMES.ROW_KEY] && colName === selected.colName) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置内联编辑修改前的行数据值 |
||||
|
* @param row 行数据 |
||||
|
*/ |
||||
|
public setOldValue(row) { |
||||
|
const values = Object.values(Constant.FIELD_NAMES.getExtraFields(this.props)); |
||||
|
Object.keys(row).forEach((key) => { |
||||
|
if ( |
||||
|
values.findIndex((item) => { |
||||
|
return item === key; |
||||
|
}) < 0 |
||||
|
) { |
||||
|
row[Constant.FIELD_NAMES.ROW_OLD_VALUE][key] = toRaw(row[key]); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置表格附加属性及分组处理 |
||||
|
* @param rows |
||||
|
* @returns |
||||
|
*/ |
||||
|
public async setExtraProperty(rows: Array<any>, ops?: any, lazyloadNoChildren: boolean = false) { |
||||
|
// 清空分组记录
|
||||
|
this.table.configStore.aloneGroupRecords = []; |
||||
|
// 重置分组行下标
|
||||
|
this.aloneGroupRowIndex = 0; |
||||
|
if (this.props.groupMode === Constant.GROUP_MODE.MERGE && !this.props.tree) { |
||||
|
// 如果存在需要合并的字段,并且不是树表格,重新对数据进行排序
|
||||
|
this.table.store.mergeGroupRecords = {}; |
||||
|
const groupByFields = this.tools?.cm.mergeGroupByField.value; |
||||
|
this.arrayOrderBy(rows, groupByFields, ops); |
||||
|
} |
||||
|
this.rowDataExtraPropertyHandle(rows, lazyloadNoChildren); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断传入的行是否为数据的最后一行 |
||||
|
* @param row 校验的行 |
||||
|
*/ |
||||
|
isLastRow(row: any) { |
||||
|
if (this.tools?.table?.rows?.length === 0) { |
||||
|
return true; |
||||
|
} |
||||
|
const lastRow = this.tools?.table.rows[this.tools.table.rows.length - 1]; |
||||
|
return this.checkLastRow(lastRow, row); |
||||
|
} |
||||
|
|
||||
|
private checkLastRow(lastRow: any, row: any) { |
||||
|
if (this.tools?.props.tree && row[Constant.FIELD_NAMES.EXPAND] && !Tools.isEmpty(row.children) && row.children.length > 0) { |
||||
|
const childrenLastRow = row.children[row.children.length - 1]; |
||||
|
if (childrenLastRow[Constant.FIELD_NAMES.EXPAND] && !Tools.isEmpty(childrenLastRow.children) && childrenLastRow.children.length > 0) { |
||||
|
return this.checkLastRow(lastRow, childrenLastRow); |
||||
|
} else { |
||||
|
return childrenLastRow[Constant.FIELD_NAMES.ROW_KEY] === row[Constant.FIELD_NAMES.ROW_KEY]; |
||||
|
} |
||||
|
} else { |
||||
|
return lastRow[Constant.FIELD_NAMES.ROW_KEY] === row[Constant.FIELD_NAMES.ROW_KEY]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 对象数组根据依次根据属性名进行排序 |
||||
|
* @param array 对象数组 |
||||
|
* @param sortFields 属性名 |
||||
|
*/ |
||||
|
private arrayOrderBy(array: Array<any>, sortFields: any, ops: any) { |
||||
|
let fields = [...sortFields]; |
||||
|
// 将分组字段与排序字段进行合并
|
||||
|
fields = this.mergeSortFields(fields, ops); |
||||
|
const customOrders = {}; |
||||
|
// 对每一个字段进行排序
|
||||
|
for (const field of fields) { |
||||
|
const desc = field.startsWith('-'); |
||||
|
const _field = desc ? field.substring(1) : field; |
||||
|
const fieldSortArr = array.map((item) => item[_field]); |
||||
|
sortArray(fieldSortArr, { order: desc ? 'desc' : 'asc' }); |
||||
|
customOrders[_field] = fieldSortArr; |
||||
|
} |
||||
|
const byFields = fields.map((item) => (item.startsWith('-') ? item.substring(1) : item)); |
||||
|
sortArray(array, { |
||||
|
by: byFields, |
||||
|
order: byFields, |
||||
|
customOrders: customOrders, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private mergeSortFields(fields: Array<any>, ops: any) { |
||||
|
const sortBy = ops?.pagination?.sortBy; |
||||
|
if (sortBy) { |
||||
|
const prefix = ops.pagination.descending ? '-' : ''; |
||||
|
if (fields.findIndex((tmp) => tmp === sortBy) === -1) { |
||||
|
fields.push(prefix + sortBy); |
||||
|
} else { |
||||
|
fields = fields.map((tmp) => { |
||||
|
if (tmp === sortBy) { |
||||
|
return prefix + sortBy; |
||||
|
} |
||||
|
return tmp; |
||||
|
}); |
||||
|
} |
||||
|
return fields; |
||||
|
} else { |
||||
|
return this.mergeDefaultSortBy(fields); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private mergeDefaultSortBy(fields: Array<any>) { |
||||
|
if (Array.isArray(this.tools?.props.sortBy) && this.tools?.props.sortBy.length > 0) { |
||||
|
this.tools.props.sortBy.forEach((item) => { |
||||
|
if (item.startsWith('-')) { |
||||
|
if (fields.findIndex((tmp) => tmp === item.substring(1)) === -1) { |
||||
|
fields.push(item); |
||||
|
} else { |
||||
|
fields = fields.map((tmp) => { |
||||
|
if (tmp === item.substring(1)) { |
||||
|
return item; |
||||
|
} |
||||
|
return tmp; |
||||
|
}); |
||||
|
} |
||||
|
} else { |
||||
|
if (fields.findIndex((tmp) => tmp === item) === -1) { |
||||
|
fields.push(item); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
return fields; |
||||
|
} |
||||
|
|
||||
|
private rowDataExtraPropertyHandle(rows: Array<any>, lazyloadNoChildren: boolean = false) { |
||||
|
if (rows && rows.length > 0) { |
||||
|
rows.forEach((item: any, index) => { |
||||
|
this.initRowDataExtraProperty(item, lazyloadNoChildren); |
||||
|
this.mergeGroupBy(item); |
||||
|
this.aloneGroupBy(item); |
||||
|
if (item[this.props.tickedField] === true) { |
||||
|
item[Constant.FIELD_NAMES.TICKED_COUNT] = 1; |
||||
|
} else if (item[Constant.FIELD_NAMES.TICKED_COUNT] === false) { |
||||
|
item[Constant.FIELD_NAMES.TICKED_COUNT] = 0; |
||||
|
} else { |
||||
|
item[Constant.FIELD_NAMES.TICKED_COUNT] = 0; |
||||
|
} |
||||
|
if (this.props.tree && item.children && item.children.length > 0) { |
||||
|
this.rowDataExtraPropertyHandle(item.children, lazyloadNoChildren); |
||||
|
item[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT] = 0; |
||||
|
item.children.forEach((child) => { |
||||
|
item[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT] += child[Constant.FIELD_NAMES.TICKED_COUNT]; |
||||
|
}); |
||||
|
if (item[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT] === 0) { |
||||
|
item[this.props.tickedField] = false; |
||||
|
item[Constant.FIELD_NAMES.TICKED_COUNT] = 0; |
||||
|
} else if (item[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT] === item.children.length) { |
||||
|
item[this.props.tickedField] = true; |
||||
|
item[Constant.FIELD_NAMES.TICKED_COUNT] = 1; |
||||
|
} else { |
||||
|
item[this.props.tickedField] = null; |
||||
|
item[Constant.FIELD_NAMES.TICKED_COUNT] = 0; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 合并模式分组 |
||||
|
* @param rowData |
||||
|
*/ |
||||
|
private mergeGroupBy(rowData: any) { |
||||
|
if (this.props.groupMode === Constant.GROUP_MODE.MERGE && !this.props.tree) { |
||||
|
const groupByFields = this.tools?.cm.mergeGroupByField.value; |
||||
|
if (groupByFields && Array.isArray(groupByFields)) { |
||||
|
groupByFields.forEach((columnName: any) => { |
||||
|
const tmpArr = [rowData[Constant.FIELD_NAMES.ROW_KEY]]; |
||||
|
if (this.table.store.mergeGroupRecords[columnName]) { |
||||
|
if (this.table.store.mergeGroupRecords[columnName][rowData[columnName]]) { |
||||
|
this.table.store.mergeGroupRecords[columnName][rowData[columnName]].push(rowData[Constant.FIELD_NAMES.ROW_KEY]); |
||||
|
} else { |
||||
|
this.table.store.mergeGroupRecords[columnName][rowData[columnName]] = tmpArr; |
||||
|
} |
||||
|
} else { |
||||
|
this.table.store.mergeGroupRecords[columnName] = { [rowData[columnName]]: tmpArr }; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 单独模式分组 |
||||
|
*/ |
||||
|
private aloneGroupBy(rowData: any) { |
||||
|
if (this.props.groupMode === Constant.GROUP_MODE.ALONE) { |
||||
|
const groupByField = this.tools?.table.configStore.aloneGroupByField; |
||||
|
if (groupByField) { |
||||
|
const expand = this.getAloneGroupDefaultExpand(rowData, groupByField); |
||||
|
const findResult = this.table.configStore.aloneGroupRecords.find((tmp) => tmp['groupName'] === rowData[groupByField]); |
||||
|
if (!findResult) { |
||||
|
this.table.configStore.aloneGroupRecords.push({ groupName: rowData[groupByField], expand: expand, rows: [rowData] }); |
||||
|
} else { |
||||
|
findResult.rows.push(rowData); |
||||
|
} |
||||
|
this.setRowIndex(rowData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获得独立分组默认展开状态 |
||||
|
* @param rowData |
||||
|
* @param groupByField |
||||
|
* @returns |
||||
|
*/ |
||||
|
private getAloneGroupDefaultExpand(rowData: any, groupByField: string) { |
||||
|
let expand = false; |
||||
|
if (typeof this.props.groupStartOpen === 'string' && this.props.groupStartOpen === Constant.GROUP_START_OPEN.ALL) { |
||||
|
expand = true; |
||||
|
} else if ( |
||||
|
typeof this.props.groupStartOpen === 'string' && |
||||
|
this.props.groupStartOpen === Constant.GROUP_START_OPEN.FIRST && |
||||
|
this.aloneGroupRowIndex === 0 |
||||
|
) { |
||||
|
expand = true; |
||||
|
} else if (Array.isArray(this.props.groupStartOpen) && this.props.groupStartOpen.includes(rowData[groupByField])) { |
||||
|
expand = true; |
||||
|
} else if (typeof this.props.groupStartOpen === 'function') { |
||||
|
expand = this.props.groupStartOpen({ |
||||
|
groupByField: rowData[groupByField], |
||||
|
row: rowData, |
||||
|
}); |
||||
|
} |
||||
|
// 当前展开记录中已经有,强制改为展开状态
|
||||
|
if (this.table.store.expandDatas.length > 0 && this.table.store.expandDatas.includes(rowData[groupByField])) { |
||||
|
expand = true; |
||||
|
} |
||||
|
return expand; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置行下标 |
||||
|
* @param rowData |
||||
|
*/ |
||||
|
private setRowIndex(rowData: any) { |
||||
|
rowData[Constant.FIELD_NAMES.ROW_INDEX] = this.aloneGroupRowIndex; |
||||
|
this.aloneGroupRowIndex++; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化行数据附加属性 |
||||
|
* @param rowData 行数据对象 |
||||
|
*/ |
||||
|
private async initRowDataExtraProperty(rowData: any, lazyloadNoChildren: boolean = false) { |
||||
|
rowData[Constant.FIELD_NAMES.ROW_KEY] = Tools.uuid(); |
||||
|
rowData[this.props.tickedField] = this.getInitRowTicked(rowData); |
||||
|
rowData[this.props.selectedField] = this.getInitRowSelected(rowData); |
||||
|
rowData[Constant.FIELD_NAMES.ROW_OLD_VALUE] = {}; |
||||
|
this.setOldValue(rowData); |
||||
|
if (this.props.tree) { |
||||
|
if (this.table.store.expandDatas.length > 0 && this.table.store.expandDatas.includes(rowData[this.props.primaryKey])) { |
||||
|
rowData[Constant.FIELD_NAMES.EXPAND] = true; |
||||
|
} else if (Tools.isEmpty(rowData[Constant.FIELD_NAMES.EXPAND])) { |
||||
|
rowData[Constant.FIELD_NAMES.EXPAND] = false; |
||||
|
} |
||||
|
if (lazyloadNoChildren && (!rowData.children || rowData.children.length === 0)) { |
||||
|
rowData[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取初始化行的ticked状态 |
||||
|
* @param rowData |
||||
|
* @returns |
||||
|
*/ |
||||
|
private getInitRowTicked(rowData: any) { |
||||
|
if (rowData[this.props.tickedField]) { |
||||
|
return true; |
||||
|
} else if (Object.keys(this.props.tickedRecord).length > 0 && this.props.tickedRecord['data']?.length > 0) { |
||||
|
const columnName = this.props.tickedRecord['columnName'] || this.props.primaryKey; |
||||
|
const data = this.props.tickedRecord['data']; |
||||
|
if ( |
||||
|
data.findIndex((item) => { |
||||
|
return item === rowData[columnName]; |
||||
|
}) > -1 |
||||
|
) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
/** |
||||
|
* 获取初始化行的selected状态 |
||||
|
* @param rowData |
||||
|
* @returns |
||||
|
*/ |
||||
|
private getInitRowSelected(rowData: any) { |
||||
|
if (rowData[this.props.selectedField]) { |
||||
|
return true; |
||||
|
} else if (!this.props.tree && rowData[this.props.tickedField]) { |
||||
|
return true; |
||||
|
} else if (this.props.tree && !this.props.checkboxSelection && rowData[this.props.tickedField]) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private pushRow(rows: Array<any>, result: Array<any>, item: any) { |
||||
|
const row = this.getRow( |
||||
|
rows, |
||||
|
typeof item === 'object' ? item[this.props.primaryKey] || item[Constant.FIELD_NAMES.ROW_KEY] : item, |
||||
|
false, |
||||
|
item[this.props.primaryKey] ? this.props.primaryKey : Constant.FIELD_NAMES.ROW_KEY, |
||||
|
); |
||||
|
if (row) { |
||||
|
result.push(row); |
||||
|
} else { |
||||
|
this.rowsKeyNotExistError(typeof item === 'object' ? item[this.props.primaryKey] || item[Constant.FIELD_NAMES.ROW_KEY] : item); |
||||
|
} |
||||
|
} |
||||
|
private rowsKeyIsEmptyError(data: any) { |
||||
|
if (typeof data === 'object' && Tools.isEmpty(data[this.props.primaryKey]) && Tools.isEmpty(data[Constant.FIELD_NAMES.ROW_KEY])) { |
||||
|
throw new Error('[w-grid]Data does not contain `primaryKey` or `rowKey`.'); |
||||
|
} |
||||
|
} |
||||
|
private rowsKeyNotExistError(data: any) { |
||||
|
throw new Error('[w-grid]`' + data + '` Not Found in Data Row.'); |
||||
|
} |
||||
|
} |
@ -1,105 +0,0 @@ |
|||||
import { Tools, t } from '@/platform'; |
|
||||
|
|
||||
// 拖拽排序模式
|
|
||||
export const dndMode = { |
|
||||
local: 'local', // 本地排序
|
|
||||
server: 'server', // 服务端排序
|
|
||||
}; |
|
||||
export const dndImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E`; |
|
||||
|
|
||||
// 选择模式
|
|
||||
export const selectMode = { |
|
||||
none: 'none', // 不允许选择
|
|
||||
row: 'row', // 行选择
|
|
||||
cell: 'cell', // 单元格选择
|
|
||||
}; |
|
||||
|
|
||||
// 分组模式
|
|
||||
export const groupMode = { |
|
||||
alone: 'alone', // 单独行进行分组
|
|
||||
merge: 'merge', // 合并行进行分组
|
|
||||
}; |
|
||||
|
|
||||
// 表格内容区域快速编辑状态
|
|
||||
export const editStatus = { |
|
||||
none: 'none', // 不在编辑状态
|
|
||||
cell: 'cellEdit', // 单元格编辑状态
|
|
||||
row: 'rowEdit', // 行编辑状态
|
|
||||
rows: 'rowsEdit', // 所有行编辑状态
|
|
||||
}; |
|
||||
|
|
||||
// 内置编辑窗口中form的状态
|
|
||||
export const formStatus = { |
|
||||
add: 'add', // 新增
|
|
||||
edit: 'edit', // 编辑
|
|
||||
clone: 'clone', // 复制
|
|
||||
addTop: 'addTop', // 新增顶级节点
|
|
||||
addChild: 'addChild', // 新增子节点
|
|
||||
}; |
|
||||
|
|
||||
// 列样式处理
|
|
||||
const columnStyle = (item: any) => { |
|
||||
let style = ''; |
|
||||
if (Tools.hasOwnProperty(item, 'style')) { |
|
||||
style = item.style; |
|
||||
} |
|
||||
if (Tools.hasOwnProperty(item, 'width')) { |
|
||||
if (typeof item.width === 'number') { |
|
||||
item.style = `min-width: ` + item.width + `px; width: ` + item.width + `px;max-width: ` + item.width + `px;` + style; |
|
||||
} else { |
|
||||
item.style = `min-width: ` + item.width + `; width: ` + item.width + `;max-width: ` + item.width + `;` + style; |
|
||||
} |
|
||||
delete item.width; |
|
||||
|
|
||||
if (Tools.hasOwnProperty(item, 'classes')) { |
|
||||
item.classes = item.classes + ' truncate'; |
|
||||
} else { |
|
||||
item.classes = 'truncate'; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 孩子列处理
|
|
||||
const childrenHandler = (item: any, gridColumns: any) => { |
|
||||
if (item.columns && item.columns.length > 0) { |
|
||||
item.columns.forEach((column) => { |
|
||||
childrenHandler(column, gridColumns); |
|
||||
}); |
|
||||
} else { |
|
||||
columnStyle(item); |
|
||||
const col = { |
|
||||
...{ align: 'left', label: item.name, field: item.name, name: item.name, sortable: true, showIf: true }, |
|
||||
...item, |
|
||||
}; |
|
||||
if (Tools.isEmpty(col.name)) { |
|
||||
col.name = Tools.uuid(); |
|
||||
} |
|
||||
gridColumns.push(col); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 列的默认属性
|
|
||||
export const columnDefaultProps = (columns: any, sortNo: boolean) => { |
|
||||
const gridColumns = <any>[]; |
|
||||
if (columns && columns.length > 0) { |
|
||||
gridColumns.push({ name: '_sortNo_', align: 'center', label: t('rownum'), field: '_sortNo_', showIf: sortNo }); |
|
||||
columns.forEach((item: any) => { |
|
||||
childrenHandler(item, gridColumns); |
|
||||
}); |
|
||||
return gridColumns; |
|
||||
} |
|
||||
return []; |
|
||||
}; |
|
||||
|
|
||||
export const sortByProperties = (arr, properties) => { |
|
||||
for (const prop of properties) { |
|
||||
arr.sort((a, b) => { |
|
||||
if (a[prop] < b[prop]) { |
|
||||
return -1; |
|
||||
} else if (a[prop] > b[prop]) { |
|
||||
return 1; |
|
||||
} |
|
||||
return 0; |
|
||||
}); |
|
||||
} |
|
||||
}; |
|
@ -0,0 +1,14 @@ |
|||||
|
import { Constant } from './constant/Constant'; |
||||
|
|
||||
|
import { EventManager } from './event/EventManager'; |
||||
|
import { ExposeApiManager } from './expose-api/ExposeApiManager'; |
||||
|
import { Init } from './Init'; |
||||
|
|
||||
|
import type { TableType } from './types/TableType'; |
||||
|
import type { EventType } from './types/EventType'; |
||||
|
import type { PropsType } from './types/PropsType'; |
||||
|
import type { UrlType } from './types/table/UrlType'; |
||||
|
|
||||
|
import { GridTools } from './GridTools'; |
||||
|
|
||||
|
export { Constant, EventManager, ExposeApiManager, Init, EventType, PropsType, UrlType, TableType, GridTools }; |
@ -0,0 +1,55 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { Loading } from 'quasar'; |
||||
|
import { nextTick } from 'vue'; |
||||
|
import { Base } from '../Base'; |
||||
|
/** |
||||
|
* w-grid 内置按钮超类 |
||||
|
*/ |
||||
|
export abstract class Button extends Base { |
||||
|
/** |
||||
|
* 按钮名称 |
||||
|
*/ |
||||
|
abstract name: string; |
||||
|
|
||||
|
/** |
||||
|
* 获取按钮配置到toolbar中的配置属性 |
||||
|
*/ |
||||
|
abstract getButtonConfig(args: any): object | string; |
||||
|
|
||||
|
/** |
||||
|
* 打开editor窗口 |
||||
|
* @param grid 表格实例对象 |
||||
|
* @param title 窗口标题 |
||||
|
* @param status 窗口中Form的状态 |
||||
|
* @param data 编辑的数据,新增为 undefined |
||||
|
*/ |
||||
|
openEditor(grid: any, title: string, status: string, data: any) { |
||||
|
grid.getEditorDialog().show(); |
||||
|
nextTick(() => { |
||||
|
grid.getEditorDialog().setTitle(title); |
||||
|
grid.getEditorForm().setStatus(status); |
||||
|
if (!Tools.isEmpty(data)) { |
||||
|
grid.getEditorForm().setData(data); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 显示处理中/加载中遮罩 |
||||
|
* @param msg |
||||
|
*/ |
||||
|
showLoading(msg: string = '正在处理,请稍等...') { |
||||
|
Loading.show({ |
||||
|
message: msg, |
||||
|
boxClass: 'bg-grey-2 text-grey-9', |
||||
|
spinnerColor: 'primary', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 隐藏处理中/加载中遮罩 |
||||
|
*/ |
||||
|
hideLoading() { |
||||
|
Loading.hide(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,227 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
import { GridTools } from '../GridTools'; |
||||
|
import { PropsType } from '../types/PropsType'; |
||||
|
|
||||
|
import { TableType } from '../types/TableType'; |
||||
|
import { Add } from './buttons/Add'; |
||||
|
import { AddChild } from './buttons/AddChild'; |
||||
|
import { AddTop } from './buttons/AddTop'; |
||||
|
import { CellEdit } from './buttons/CellEdit'; |
||||
|
import { Clone } from './buttons/Clone'; |
||||
|
import { Edit } from './buttons/Edit'; |
||||
|
import { Expand } from './buttons/Expand'; |
||||
|
import { Export } from './buttons/Export'; |
||||
|
import { InlineCellEdit } from './buttons/InlineCellEdit'; |
||||
|
import { InlineRowEdit } from './buttons/InlineRowEdit'; |
||||
|
import { InlineRowsEdit } from './buttons/InlineRowsEdit'; |
||||
|
import { MoreQuery } from './buttons/MoreQuery'; |
||||
|
import { Query } from './buttons/Query'; |
||||
|
import { AdvancedQuery } from './buttons/AdvancedQuery'; |
||||
|
import { Refresh } from './buttons/Refresh'; |
||||
|
import { Remove } from './buttons/Remove'; |
||||
|
import { Reset } from './buttons/Reset'; |
||||
|
import { ResetDefaultValues } from './buttons/ResetDefaultValues'; |
||||
|
import { Separator } from './buttons/Separator'; |
||||
|
import { View } from './buttons/view'; |
||||
|
import { FullScreen } from './buttons/FullScreen'; |
||||
|
|
||||
|
/** |
||||
|
* w-grid 内置按钮管理器 |
||||
|
*/ |
||||
|
export class ButtonManager { |
||||
|
// 查询
|
||||
|
query: Query; |
||||
|
moreQuery: MoreQuery; |
||||
|
advancedQuery: AdvancedQuery; |
||||
|
reset: Reset; |
||||
|
refresh: Refresh; |
||||
|
|
||||
|
// 新增
|
||||
|
add: Add; |
||||
|
addTop: AddTop; |
||||
|
addChild: AddChild; |
||||
|
|
||||
|
// 编辑
|
||||
|
edit: Edit; |
||||
|
cellEdit: CellEdit; |
||||
|
inlineCellEdit: InlineCellEdit; |
||||
|
inlineRowEdit: InlineRowEdit; |
||||
|
inlineRowsEdit: InlineRowsEdit; |
||||
|
clone: Clone; |
||||
|
|
||||
|
// 删除
|
||||
|
remove: Remove; |
||||
|
|
||||
|
// 其他
|
||||
|
fullScreen: FullScreen; |
||||
|
separator: Separator; |
||||
|
view: View; |
||||
|
export: Export; |
||||
|
expand: Expand; |
||||
|
resetDefaultValues: ResetDefaultValues; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
this.query = new Query(props, table); |
||||
|
this.moreQuery = new MoreQuery(props, table); |
||||
|
this.advancedQuery = new AdvancedQuery(props, table); |
||||
|
this.reset = new Reset(props, table); |
||||
|
this.refresh = new Refresh(props, table); |
||||
|
|
||||
|
this.add = new Add(props, table); |
||||
|
this.addTop = new AddTop(props, table); |
||||
|
this.addChild = new AddChild(props, table); |
||||
|
|
||||
|
this.edit = new Edit(props, table); |
||||
|
this.cellEdit = new CellEdit(props, table); |
||||
|
this.inlineCellEdit = new InlineCellEdit(props, table); |
||||
|
this.inlineRowEdit = new InlineRowEdit(props, table); |
||||
|
this.inlineRowsEdit = new InlineRowsEdit(props, table); |
||||
|
this.clone = new Clone(props, table); |
||||
|
|
||||
|
this.remove = new Remove(props, table); |
||||
|
|
||||
|
this.fullScreen = new FullScreen(props, table); |
||||
|
this.separator = new Separator(props, table); |
||||
|
this.view = new View(props, table); |
||||
|
this.export = new Export(props, table); |
||||
|
this.expand = new Expand(props, table); |
||||
|
this.resetDefaultValues = new ResetDefaultValues(props, table); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取按钮配置 |
||||
|
* @param name 按钮名称,不传入则以`{ 按钮名称:按钮配置 }`对象的形式返回所有按钮配置。 |
||||
|
* @returns |
||||
|
*/ |
||||
|
getButtonConfig(name?: string) { |
||||
|
let config = {}; |
||||
|
if (Tools.isEmpty(name)) { |
||||
|
config = { |
||||
|
[this.query.name]: this.query.getButtonConfig(), |
||||
|
[this.moreQuery.name]: this.moreQuery.getButtonConfig(), |
||||
|
[this.advancedQuery.name]: this.advancedQuery.getButtonConfig(), |
||||
|
[this.reset.name]: this.reset.getButtonConfig(), |
||||
|
[this.refresh.name]: this.refresh.getButtonConfig(), |
||||
|
|
||||
|
[this.add.name]: this.add.getButtonConfig(), |
||||
|
[this.addTop.name]: this.addTop.getButtonConfig(), |
||||
|
[this.addChild.name]: this.addChild.getButtonConfig(), |
||||
|
|
||||
|
[this.edit.name]: this.edit.getButtonConfig(), |
||||
|
[this.cellEdit.name]: this.cellEdit.getButtonConfig(), |
||||
|
[this.inlineCellEdit.name]: this.inlineCellEdit.getButtonConfig(), |
||||
|
[this.inlineRowEdit.name]: this.inlineRowEdit.getButtonConfig(), |
||||
|
[this.inlineRowsEdit.name]: this.inlineRowsEdit.getButtonConfig(), |
||||
|
[this.clone.name]: this.clone.getButtonConfig(), |
||||
|
|
||||
|
[this.remove.name]: this.remove.getButtonConfig(), |
||||
|
|
||||
|
[this.fullScreen.name]: this.fullScreen.getButtonConfig(), |
||||
|
[this.separator.name]: this.separator.getButtonConfig(), |
||||
|
[this.view.name]: this.view.getButtonConfig(), |
||||
|
[this.export.name]: this.export.getButtonConfig(), |
||||
|
[this.expand.name]: this.expand.getButtonConfig(), |
||||
|
[this.resetDefaultValues.name]: this.resetDefaultValues.getButtonConfig(), |
||||
|
}; |
||||
|
} else { |
||||
|
switch (name) { |
||||
|
case this.query.name: |
||||
|
config = this.query.getButtonConfig(); |
||||
|
break; |
||||
|
case this.moreQuery.name: |
||||
|
config = this.moreQuery.getButtonConfig(); |
||||
|
break; |
||||
|
case this.advancedQuery.name: |
||||
|
config = this.advancedQuery.getButtonConfig(); |
||||
|
break; |
||||
|
case this.reset.name: |
||||
|
config = this.reset.getButtonConfig(); |
||||
|
break; |
||||
|
case this.refresh.name: |
||||
|
config = this.refresh.getButtonConfig(); |
||||
|
break; |
||||
|
|
||||
|
case this.add.name: |
||||
|
config = this.add.getButtonConfig(); |
||||
|
break; |
||||
|
case this.addTop.name: |
||||
|
config = this.addTop.getButtonConfig(); |
||||
|
break; |
||||
|
case this.addChild.name: |
||||
|
config = this.addChild.getButtonConfig(); |
||||
|
break; |
||||
|
|
||||
|
case this.edit.name: |
||||
|
config = this.edit.getButtonConfig(); |
||||
|
break; |
||||
|
case this.cellEdit.name: |
||||
|
config = this.cellEdit.getButtonConfig(); |
||||
|
break; |
||||
|
case this.inlineCellEdit.name: |
||||
|
config = this.inlineCellEdit.getButtonConfig(); |
||||
|
break; |
||||
|
case this.inlineRowEdit.name: |
||||
|
config = this.inlineRowEdit.getButtonConfig(); |
||||
|
break; |
||||
|
case this.inlineRowsEdit.name: |
||||
|
config = this.inlineRowsEdit.getButtonConfig(); |
||||
|
break; |
||||
|
case this.clone.name: |
||||
|
config = this.clone.getButtonConfig(); |
||||
|
break; |
||||
|
|
||||
|
case this.remove.name: |
||||
|
config = this.remove.getButtonConfig(); |
||||
|
break; |
||||
|
|
||||
|
case this.fullScreen.name: |
||||
|
config = this.fullScreen.getButtonConfig(); |
||||
|
break; |
||||
|
case this.separator.name: |
||||
|
config = this.separator.getButtonConfig(); |
||||
|
break; |
||||
|
case this.view.name: |
||||
|
config = this.view.getButtonConfig(); |
||||
|
break; |
||||
|
case this.export.name: |
||||
|
config = this.export.getButtonConfig(); |
||||
|
break; |
||||
|
case this.expand.name: |
||||
|
config = this.expand.getButtonConfig(); |
||||
|
break; |
||||
|
case this.resetDefaultValues.name: |
||||
|
config = this.resetDefaultValues.getButtonConfig(); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
return config; |
||||
|
} |
||||
|
|
||||
|
setInstance(instance: any, tools: GridTools) { |
||||
|
this.query.setInstance(instance, tools); |
||||
|
this.moreQuery.setInstance(instance, tools); |
||||
|
this.advancedQuery.setInstance(instance, tools); |
||||
|
this.reset.setInstance(instance, tools); |
||||
|
this.refresh.setInstance(instance, tools); |
||||
|
|
||||
|
this.add.setInstance(instance, tools); |
||||
|
this.addTop.setInstance(instance, tools); |
||||
|
this.addChild.setInstance(instance, tools); |
||||
|
|
||||
|
this.edit.setInstance(instance, tools); |
||||
|
this.cellEdit.setInstance(instance, tools); |
||||
|
this.inlineCellEdit.setInstance(instance, tools); |
||||
|
this.inlineRowEdit.setInstance(instance, tools); |
||||
|
this.inlineRowsEdit.setInstance(instance, tools); |
||||
|
this.clone.setInstance(instance, tools); |
||||
|
|
||||
|
this.remove.setInstance(instance, tools); |
||||
|
|
||||
|
this.fullScreen.setInstance(instance, tools); |
||||
|
this.separator.setInstance(instance, tools); |
||||
|
this.view.setInstance(instance, tools); |
||||
|
this.export.setInstance(instance, tools); |
||||
|
this.expand.setInstance(instance, tools); |
||||
|
this.resetDefaultValues.setInstance(instance, tools); |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class Add extends Button { |
||||
|
name = 'add'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
this.openEditor(args.grid, $t('action.addNew'), Constant.FORM_STATUS.ADD, undefined); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'add', |
||||
|
labelI18nKey: 'action.addNew', |
||||
|
label: $t('action.addNew'), |
||||
|
click: this.click, |
||||
|
afterEditorOpen: (args) => { |
||||
|
this.tools?.em.afterEditorOpen(undefined); |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class AddChild extends Button { |
||||
|
name = 'addChild'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
this.openEditor(args.grid, $t('action.addChild'), Constant.FORM_STATUS.ADD_CHILD, undefined); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'playlist_add', |
||||
|
labelI18nKey: 'action.addChild', |
||||
|
label: $t('action.addChild'), |
||||
|
enableIf: (args) => { |
||||
|
if (args.selected) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}, |
||||
|
click: this.click, |
||||
|
afterEditorOpen: (args) => { |
||||
|
this.tools?.em.afterEditorOpen(undefined); |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class AddTop extends Button { |
||||
|
name = 'addTop'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
this.openEditor(args.grid, $t('action.addTop'), Constant.FORM_STATUS.ADD_TOP, undefined); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'add', |
||||
|
labelI18nKey: 'action.addTop', |
||||
|
label: $t('action.addTop'), |
||||
|
click: (args) => {}, |
||||
|
afterEditorOpen: (args) => { |
||||
|
this.tools?.em.afterEditorOpen(undefined); |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
import { Button } from '../Button'; |
||||
|
|
||||
|
export class AdvancedQuery extends Button { |
||||
|
name = 'advancedQuery'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
this.table.advancedQueryStatus = !this.table.advancedQueryStatus; |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'manage_search', |
||||
|
labelI18nKey: 'action.advancedQuery', |
||||
|
label: $t('action.advancedQuery'), |
||||
|
enableIf: (args) => { |
||||
|
if (this.props.queryFormFields.length > 0 && this.props.advancedQuery) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
}, |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,45 @@ |
|||||
|
import { $t, NotifyManager, Tools } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class CellEdit extends Button { |
||||
|
name = 'cellEdit'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
const selectedCell = args.grid.getSelectedCell(); |
||||
|
if (args.grid.props.selectMode !== Constant.SELECT_MODE.CELL) { |
||||
|
console.warn('[w-grid]`selectMode` property is not `cell`, Cannot use cell editing function.'); |
||||
|
return false; |
||||
|
} else if (Tools.isEmpty(selectedCell.colName)) { |
||||
|
NotifyManager.info('请选择要编辑的单元格'); |
||||
|
return false; |
||||
|
} else if (!this.tools?.editFM.getEditorFieldByName(selectedCell.colName)) { |
||||
|
console.warn('[w-grid]The column selected is not configured with a component type for editing.'); |
||||
|
return false; |
||||
|
} |
||||
|
// 弹出模态框编辑,但是只编辑该列的值
|
||||
|
args.grid.getCellEditorDialog().show(); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'border_color', |
||||
|
labelI18nKey: 'action.edit', |
||||
|
label: $t('action.edit'), |
||||
|
enableIf: (args) => { |
||||
|
if (!Tools.isEmpty(args.grid.getSelectedCell()['colName'])) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}, |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
import { $t, NotifyManager } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class Clone extends Button { |
||||
|
name = 'clone'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
if (!args.selected) { |
||||
|
NotifyManager.warn($t('action.copy.tip')); |
||||
|
} else { |
||||
|
args.selected[args.grid.props.primaryKey] = undefined; |
||||
|
this.openEditor(args.grid, $t('action.copy'), Constant.FORM_STATUS.CLONE, args.selected); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'content_copy', |
||||
|
labelI18nKey: 'action.copy', |
||||
|
label: $t('action.copy'), |
||||
|
enableIf: (args) => { |
||||
|
if (args.selected) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}, |
||||
|
click: this.click, |
||||
|
afterEditorOpen: (args) => { |
||||
|
this.tools?.em.afterEditorOpen(args.selected); |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
import { $t, NotifyManager } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class Edit extends Button { |
||||
|
name = 'edit'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
if (!args.selected) { |
||||
|
NotifyManager.warn($t('action.edit.tip')); |
||||
|
} else { |
||||
|
this.openEditor(args.grid, $t('action.edit'), Constant.FORM_STATUS.EDIT, args.selected); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'edit', |
||||
|
labelI18nKey: 'action.edit', |
||||
|
label: $t('action.edit'), |
||||
|
enableIf: (args) => { |
||||
|
if (args.selected) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}, |
||||
|
click: this.click, |
||||
|
afterEditorOpen: (args) => { |
||||
|
this.tools?.em.afterEditorOpen(args.selected); |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
import { Button } from '../Button'; |
||||
|
|
||||
|
export class Expand extends Button { |
||||
|
name = 'expand'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
async click(args) { |
||||
|
if (this.table.store.expand) { |
||||
|
this.tools?.apiFM.operator.collapse(undefined, true); |
||||
|
} else { |
||||
|
if (this.props.tree && this.props.treeLazyLoad && !this.table.store.alreadyLoadAllData) { |
||||
|
this.table.store.alreadyLoadAllData = true; |
||||
|
this.table.store.loading = true; |
||||
|
const datas = await this.tools?.reqApiFM.treeFetchDataByForeignKey(); |
||||
|
this.tools?.apiFM.localMode.setLocalData(datas); |
||||
|
this.table.store.loading = false; |
||||
|
} |
||||
|
this.tools?.apiFM.operator.expand(undefined, true); |
||||
|
} |
||||
|
this.table.store.expand = !this.table.store.expand; |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: () => { |
||||
|
return this.table.store.expand ? 'expand_less' : 'expand_more'; |
||||
|
}, |
||||
|
label: () => { |
||||
|
return this.table.store.expand ? $t('action.collapseAll') : $t('action.expandAll'); |
||||
|
}, |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,89 @@ |
|||||
|
import { $t, axios, NotifyManager, Tools } from '@/platform'; |
||||
|
import { exportFile } from 'quasar'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
import { Button } from '../Button'; |
||||
|
|
||||
|
export class Export extends Button { |
||||
|
name = 'export'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getExportData = this.getExportData.bind(this); |
||||
|
this.wrapCsvValue = this.wrapCsvValue.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
async click(args) { |
||||
|
this.showLoading(); |
||||
|
let exportData = args.grid.getRows(); |
||||
|
// 判断是否配置了 url, 以不分页形式请求后端获取全部数据一把导出。
|
||||
|
if (!Tools.isEmpty(args.grid.props.fetchDataUrl) || !Tools.isEmpty(args.grid.props.dataUrl)) { |
||||
|
const fetchResult = await this.getExportData(); |
||||
|
if (fetchResult && fetchResult.length > 0) { |
||||
|
exportData = fetchResult; |
||||
|
} |
||||
|
} |
||||
|
const content = [args.grid.props.columns.map((col) => this.wrapCsvValue(col.label))] |
||||
|
.concat( |
||||
|
exportData.map((row) => |
||||
|
args.grid.props.columns |
||||
|
.map((col) => |
||||
|
this.wrapCsvValue(typeof col.field === 'function' ? col.field(row) : row[col.field === void 0 ? col.name : col.field], col.format, row), |
||||
|
) |
||||
|
.join(','), |
||||
|
), |
||||
|
) |
||||
|
.join('\r\n'); |
||||
|
|
||||
|
const status = exportFile('table-export.csv', content, { |
||||
|
encoding: 'utf-8', |
||||
|
mimeType: 'text/csv', |
||||
|
byteOrderMark: '\uFEFF', // 解决乱码问题
|
||||
|
}); |
||||
|
|
||||
|
if (status !== true) { |
||||
|
NotifyManager.error($t('action.export.failed')); |
||||
|
} |
||||
|
this.hideLoading(); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'file_download', |
||||
|
labelI18nKey: 'action.export', |
||||
|
label: $t('action.export'), |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private async getExportData() { |
||||
|
let resultData = <any>[]; |
||||
|
const reqParams: any = { pageable: false }; |
||||
|
const urlSearchParams = this.tools?.criteriaFM.buildURLSearchParams(reqParams); |
||||
|
const resp = await axios.get(this.table?.url.fetchDataUrl || this.table.url.dataUrl, { params: urlSearchParams }); |
||||
|
if (resp && resp.data) { |
||||
|
const responseData = resp.data; |
||||
|
if (Array.isArray(responseData)) { |
||||
|
resultData = responseData; |
||||
|
} else if (typeof responseData === 'object' && responseData.content) { |
||||
|
resultData = responseData.content; |
||||
|
} |
||||
|
} |
||||
|
return resultData; |
||||
|
} |
||||
|
|
||||
|
private wrapCsvValue(val, formatFn, row) { |
||||
|
let formatted = formatFn !== void 0 ? formatFn(val, row) : val; |
||||
|
formatted = formatted === void 0 || formatted === null ? '' : String(formatted); |
||||
|
formatted = formatted.split('"').join('""'); |
||||
|
/** |
||||
|
* Excel accepts \n and \r in strings, but some other CSV parsers do not |
||||
|
* Uncomment the next two lines to escape new lines |
||||
|
*/ |
||||
|
// .split('\n').join('\\n')
|
||||
|
// .split('\r').join('\\r')
|
||||
|
return `"${formatted}"`; |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
import { Button } from '../Button'; |
||||
|
|
||||
|
export class FullScreen extends Button { |
||||
|
name = 'fullScreen'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
this.table.componentRef.getTableRef().toggleFullscreen(); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: () => { |
||||
|
return this.table.configStore.isFullscreen ? 'fullscreen_exit' : 'fullscreen'; |
||||
|
}, |
||||
|
label: () => { |
||||
|
return this.table.configStore.isFullscreen ? $t('logout') : $t('fullScreen'); |
||||
|
}, |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
import { $t, Tools, NotifyManager } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class InlineCellEdit extends Button { |
||||
|
name = 'inlineCellEdit'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
const selectedCell = args.grid.getSelectedCell(); |
||||
|
if (args.grid.props.selectMode !== Constant.SELECT_MODE.CELL) { |
||||
|
console.warn('[w-grid]`selectMode` property is not `cell`, Cannot use cell editing function.'); |
||||
|
return false; |
||||
|
} else if (Tools.isEmpty(selectedCell.colName)) { |
||||
|
NotifyManager.info('请选择要编辑的单元格'); |
||||
|
return false; |
||||
|
} else if (!this.tools?.editFM.getEditorFieldByName(selectedCell.colName)) { |
||||
|
console.warn('[w-grid]The column selected is not configured with a component type for editing.'); |
||||
|
return false; |
||||
|
} |
||||
|
this.table.store.inlineEditStatus = Constant.EDIT_STATUS.CELL; |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'border_color', |
||||
|
labelI18nKey: 'action.edit', |
||||
|
label: $t('action.edit'), |
||||
|
enableIf: (args) => { |
||||
|
if (!Tools.isEmpty(args.grid.getSelectedCell()['colName'])) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}, |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class InlineRowEdit extends Button { |
||||
|
name = 'inlineRowEdit'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
if (this.props?.editor?.form?.fields?.length > 0) { |
||||
|
this.table.store.inlineEditStatus = Constant.EDIT_STATUS.ROW; |
||||
|
} else { |
||||
|
console.warn('[w-grid]Not configured with a component type for editing.'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'edit_note', |
||||
|
labelI18nKey: 'action.edit', |
||||
|
label: $t('action.edit'), |
||||
|
enableIf: (args) => { |
||||
|
if (args.selected) { |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
}, |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class InlineRowsEdit extends Button { |
||||
|
name = 'inlineRowsEdit'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
if (this.props.editor?.form?.fields?.length > 0) { |
||||
|
this.table.store.inlineEditStatus = Constant.EDIT_STATUS.ROWS; |
||||
|
// 清空勾选与选中
|
||||
|
args.grid.clearSelected(); |
||||
|
args.grid.clearTicked(); |
||||
|
this.table.store.headerTicked = false; |
||||
|
} else { |
||||
|
console.warn('[w-grid]Not configured with a component type for editing.'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'app_registration', |
||||
|
labelI18nKey: 'action.edit', |
||||
|
label: $t('action.edit'), |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { Button } from '../Button'; |
||||
|
import { Constant, PropsType, TableType } from '../../index'; |
||||
|
|
||||
|
export class MoreQuery extends Button { |
||||
|
name = 'moreQuery'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
this.table.moreQueryStatus = !this.table.moreQueryStatus; |
||||
|
this.table.componentRef.getTopRef()?.handleQueryFormShowField(); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'zoom_in', |
||||
|
labelI18nKey: 'action.moreQueryConditions', |
||||
|
label: $t('action.moreQueryConditions'), |
||||
|
enableIf: (args) => { |
||||
|
if (this.props.queryFormFields.length <= this.table.queryFormDisplayFields.length && !this.table.moreQueryStatus) { |
||||
|
return false; |
||||
|
} else { |
||||
|
return true; |
||||
|
} |
||||
|
}, |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
import { Button } from '../Button'; |
||||
|
|
||||
|
export class Query extends Button { |
||||
|
name = 'query'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
this.tools?.apiFM.operator.refreshGrid(); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'search', |
||||
|
labelI18nKey: 'action.query', |
||||
|
label: $t('action.query'), |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
import { $t } from '@/platform'; |
||||
|
import { PropsType, TableType } from '../../index'; |
||||
|
import { Button } from '../Button'; |
||||
|
|
||||
|
export class Refresh extends Button { |
||||
|
name = 'refresh'; |
||||
|
|
||||
|
constructor(props: PropsType, table: TableType) { |
||||
|
super(props, table); |
||||
|
this.click = this.click.bind(this); |
||||
|
this.getButtonConfig = this.getButtonConfig.bind(this); |
||||
|
} |
||||
|
|
||||
|
click(args) { |
||||
|
this.tools?.apiFM.operator.refreshGrid(); |
||||
|
} |
||||
|
|
||||
|
getButtonConfig() { |
||||
|
return { |
||||
|
name: this.name, |
||||
|
icon: 'loop', |
||||
|
labelI18nKey: 'action.refresh', |
||||
|
label: $t('action.refresh'), |
||||
|
click: this.click, |
||||
|
}; |
||||
|
} |
||||
|
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue