|
@@ -16,66 +16,66 @@ const {$gettext, interpolate} = gettext
|
|
|
const emit = defineEmits(['onSelected', 'onSelectedRecord', 'clickEdit', 'update:selectedRowKeys', 'clickBatchModify'])
|
|
|
|
|
|
const props = defineProps({
|
|
|
- api: Object,
|
|
|
- columns: Array,
|
|
|
- data_key: {
|
|
|
- type: String,
|
|
|
- default: 'data'
|
|
|
- },
|
|
|
- disable_search: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- disable_query_params: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- disable_add: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- edit_text: String,
|
|
|
- deletable: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- get_params: {
|
|
|
- type: Object,
|
|
|
- default() {
|
|
|
- return {}
|
|
|
- }
|
|
|
- },
|
|
|
- editable: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- selectionType: {
|
|
|
- type: String,
|
|
|
- validator: function (value: string) {
|
|
|
- return ['checkbox', 'radio'].indexOf(value) !== -1
|
|
|
- }
|
|
|
- },
|
|
|
- pithy: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- scrollX: {
|
|
|
- type: [Number, Boolean],
|
|
|
- default: true
|
|
|
- },
|
|
|
- rowKey: {
|
|
|
- type: String,
|
|
|
- default: 'id'
|
|
|
- },
|
|
|
- exportCsv: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- size: String,
|
|
|
- selectedRowKeys: {
|
|
|
- type: Array
|
|
|
- },
|
|
|
- useSortable: Boolean
|
|
|
+ api: Object,
|
|
|
+ columns: Array,
|
|
|
+ data_key: {
|
|
|
+ type: String,
|
|
|
+ default: 'data'
|
|
|
+ },
|
|
|
+ disable_search: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ disable_query_params: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ disable_add: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ edit_text: String,
|
|
|
+ deletable: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ get_params: {
|
|
|
+ type: Object,
|
|
|
+ default() {
|
|
|
+ return {}
|
|
|
+ }
|
|
|
+ },
|
|
|
+ editable: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ selectionType: {
|
|
|
+ type: String,
|
|
|
+ validator: function (value: string) {
|
|
|
+ return ['checkbox', 'radio'].indexOf(value) !== -1
|
|
|
+ }
|
|
|
+ },
|
|
|
+ pithy: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ scrollX: {
|
|
|
+ type: [Number, Boolean],
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ rowKey: {
|
|
|
+ type: String,
|
|
|
+ default: 'id'
|
|
|
+ },
|
|
|
+ exportCsv: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ size: String,
|
|
|
+ selectedRowKeys: {
|
|
|
+ type: Array
|
|
|
+ },
|
|
|
+ useSortable: Boolean
|
|
|
})
|
|
|
|
|
|
const data_source: any = ref([])
|
|
@@ -84,27 +84,27 @@ const rows_key_index_map: any = ref({})
|
|
|
|
|
|
const loading = ref(true)
|
|
|
const pagination = reactive({
|
|
|
- total: 1,
|
|
|
- per_page: 10,
|
|
|
- current_page: 1,
|
|
|
- total_pages: 1
|
|
|
+ total: 1,
|
|
|
+ per_page: 10,
|
|
|
+ current_page: 1,
|
|
|
+ total_pages: 1
|
|
|
})
|
|
|
|
|
|
const route = useRoute()
|
|
|
const params = reactive({
|
|
|
- ...props.get_params
|
|
|
+ ...props.get_params
|
|
|
})
|
|
|
|
|
|
const selectedKeysLocalBuffer: any = ref([])
|
|
|
|
|
|
const selectedRowKeysBuffer = computed({
|
|
|
- get() {
|
|
|
- return props.selectedRowKeys || selectedKeysLocalBuffer.value
|
|
|
- },
|
|
|
- set(v) {
|
|
|
- selectedKeysLocalBuffer.value = v
|
|
|
- emit('update:selectedRowKeys', v)
|
|
|
- }
|
|
|
+ get() {
|
|
|
+ return props.selectedRowKeys || selectedKeysLocalBuffer.value
|
|
|
+ },
|
|
|
+ set(v) {
|
|
|
+ selectedKeysLocalBuffer.value = v
|
|
|
+ emit('update:selectedRowKeys', v)
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
const searchColumns = getSearchColumns()
|
|
@@ -112,472 +112,472 @@ const pithyColumns = getPithyColumns()
|
|
|
const batchColumns = getBatchEditColumns()
|
|
|
|
|
|
onMounted(() => {
|
|
|
- if (!props.disable_query_params) {
|
|
|
- Object.assign(params, route.query)
|
|
|
- }
|
|
|
- get_list()
|
|
|
-
|
|
|
- if (props.useSortable) {
|
|
|
- initSortable()
|
|
|
- }
|
|
|
+ if (!props.disable_query_params) {
|
|
|
+ Object.assign(params, route.query)
|
|
|
+ }
|
|
|
+ get_list()
|
|
|
+
|
|
|
+ if (props.useSortable) {
|
|
|
+ initSortable()
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
defineExpose({
|
|
|
- get_list
|
|
|
+ get_list
|
|
|
})
|
|
|
|
|
|
function destroy(id: any) {
|
|
|
- props.api!.destroy(id).then(() => {
|
|
|
- get_list()
|
|
|
- message.success(interpolate($gettext('Delete ID: %{id}'), {id: id}))
|
|
|
- }).catch((e: any) => {
|
|
|
- message.error($gettext(e?.message ?? 'Server error'))
|
|
|
- })
|
|
|
+ props.api!.destroy(id).then(() => {
|
|
|
+ get_list()
|
|
|
+ message.success(interpolate($gettext('Delete ID: %{id}'), {id: id}))
|
|
|
+ }).catch((e: any) => {
|
|
|
+ message.error($gettext(e?.message ?? 'Server error'))
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function get_list(page_num = null, page_size = 20) {
|
|
|
- loading.value = true
|
|
|
- if (page_num) {
|
|
|
- params['page'] = page_num
|
|
|
- params['page_size'] = page_size
|
|
|
- }
|
|
|
- props.api!.get_list(params).then(async (r: any) => {
|
|
|
- data_source.value = r.data
|
|
|
- rows_key_index_map.value = {}
|
|
|
- if (props.useSortable) {
|
|
|
- function buildIndexMap(data: any, level: number = 0, index: number = 0, total: number[] = []) {
|
|
|
- if (data && data.length > 0) {
|
|
|
- data.forEach((v: any) => {
|
|
|
- v.level = level
|
|
|
- let current_index = [...total, index++]
|
|
|
- rows_key_index_map.value[v.id] = current_index
|
|
|
- if (v.children) buildIndexMap(v.children, level + 1, 0, current_index)
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- buildIndexMap(r.data)
|
|
|
+ loading.value = true
|
|
|
+ if (page_num) {
|
|
|
+ params['page'] = page_num
|
|
|
+ params['page_size'] = page_size
|
|
|
+ }
|
|
|
+ props.api!.get_list(params).then(async (r: any) => {
|
|
|
+ data_source.value = r.data
|
|
|
+ rows_key_index_map.value = {}
|
|
|
+ if (props.useSortable) {
|
|
|
+ function buildIndexMap(data: any, level: number = 0, index: number = 0, total: number[] = []) {
|
|
|
+ if (data && data.length > 0) {
|
|
|
+ data.forEach((v: any) => {
|
|
|
+ v.level = level
|
|
|
+ let current_index = [...total, index++]
|
|
|
+ rows_key_index_map.value[v.id] = current_index
|
|
|
+ if (v.children) buildIndexMap(v.children, level + 1, 0, current_index)
|
|
|
+ })
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (r.pagination !== undefined) {
|
|
|
- Object.assign(pagination, r.pagination)
|
|
|
- }
|
|
|
+ buildIndexMap(r.data)
|
|
|
+ }
|
|
|
|
|
|
- loading.value = false
|
|
|
- }).catch((e: any) => {
|
|
|
- message.error(e?.message ?? $gettext('Server error'))
|
|
|
- })
|
|
|
+ if (r.pagination !== undefined) {
|
|
|
+ Object.assign(pagination, r.pagination)
|
|
|
+ }
|
|
|
+
|
|
|
+ loading.value = false
|
|
|
+ }).catch((e: any) => {
|
|
|
+ message.error(e?.message ?? $gettext('Server error'))
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function stdChange(pagination: any, filters: any, sorter: any) {
|
|
|
- if (sorter) {
|
|
|
- selectedRowKeysBuffer.value = []
|
|
|
- params['order_by'] = sorter.field
|
|
|
- params['sort'] = sorter.order === 'ascend' ? 'asc' : 'desc'
|
|
|
- switch (sorter.order) {
|
|
|
- case 'ascend':
|
|
|
- params['sort'] = 'asc'
|
|
|
- break
|
|
|
- case 'descend':
|
|
|
- params['sort'] = 'desc'
|
|
|
- break
|
|
|
- default:
|
|
|
- params['sort'] = null
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- if (pagination) {
|
|
|
- selectedRowKeysBuffer.value = []
|
|
|
+ if (sorter) {
|
|
|
+ selectedRowKeysBuffer.value = []
|
|
|
+ params['order_by'] = sorter.field
|
|
|
+ params['sort'] = sorter.order === 'ascend' ? 'asc' : 'desc'
|
|
|
+ switch (sorter.order) {
|
|
|
+ case 'ascend':
|
|
|
+ params['sort'] = 'asc'
|
|
|
+ break
|
|
|
+ case 'descend':
|
|
|
+ params['sort'] = 'desc'
|
|
|
+ break
|
|
|
+ default:
|
|
|
+ params['sort'] = null
|
|
|
+ break
|
|
|
}
|
|
|
+ }
|
|
|
+ if (pagination) {
|
|
|
+ selectedRowKeysBuffer.value = []
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function expandedTable(keys: any) {
|
|
|
- expand_keys_list.value = keys
|
|
|
+ expand_keys_list.value = keys
|
|
|
}
|
|
|
|
|
|
function getSearchColumns() {
|
|
|
- let searchColumns: any = []
|
|
|
- props.columns!.forEach((column: any) => {
|
|
|
- if (column.search) {
|
|
|
- searchColumns.push(column)
|
|
|
- }
|
|
|
- })
|
|
|
- return searchColumns
|
|
|
+ let searchColumns: any = []
|
|
|
+ props.columns!.forEach((column: any) => {
|
|
|
+ if (column.search) {
|
|
|
+ searchColumns.push(column)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return searchColumns
|
|
|
}
|
|
|
|
|
|
function getBatchEditColumns() {
|
|
|
- let batch: any = []
|
|
|
- props.columns!.forEach((column: any) => {
|
|
|
- if (column.batch) {
|
|
|
- batch.push(column)
|
|
|
- }
|
|
|
- })
|
|
|
- return batch
|
|
|
+ let batch: any = []
|
|
|
+ props.columns!.forEach((column: any) => {
|
|
|
+ if (column.batch) {
|
|
|
+ batch.push(column)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return batch
|
|
|
}
|
|
|
|
|
|
function getPithyColumns() {
|
|
|
- if (props.pithy) {
|
|
|
- return props.columns!.filter((c: any, index: any, columns: any) => {
|
|
|
- return c.pithy === true && c.display !== false
|
|
|
- })
|
|
|
- }
|
|
|
+ if (props.pithy) {
|
|
|
return props.columns!.filter((c: any, index: any, columns: any) => {
|
|
|
- return c.display !== false
|
|
|
+ return c.pithy === true && c.display !== false
|
|
|
})
|
|
|
+ }
|
|
|
+ return props.columns!.filter((c: any, index: any, columns: any) => {
|
|
|
+ return c.display !== false
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function checked(c: any) {
|
|
|
- params[c.target.value] = c.target.checked
|
|
|
+ params[c.target.value] = c.target.checked
|
|
|
}
|
|
|
|
|
|
const crossPageSelect: any = {}
|
|
|
|
|
|
async function onSelectChange(_selectedRowKeys: any) {
|
|
|
- const page = params.page || 1
|
|
|
-
|
|
|
- crossPageSelect[page] = await _selectedRowKeys
|
|
|
-
|
|
|
- let t: any = []
|
|
|
- Object.keys(crossPageSelect).forEach(v => {
|
|
|
- t.push(...crossPageSelect[v])
|
|
|
- })
|
|
|
- const n: any = [..._selectedRowKeys]
|
|
|
- t = await t.concat(n)
|
|
|
- // console.log(crossPageSelect)
|
|
|
- const set = new Set(t)
|
|
|
- selectedRowKeysBuffer.value = Array.from(set)
|
|
|
- emit('onSelected', selectedRowKeysBuffer.value)
|
|
|
+ const page = params.page || 1
|
|
|
+
|
|
|
+ crossPageSelect[page] = await _selectedRowKeys
|
|
|
+
|
|
|
+ let t: any = []
|
|
|
+ Object.keys(crossPageSelect).forEach(v => {
|
|
|
+ t.push(...crossPageSelect[v])
|
|
|
+ })
|
|
|
+ const n: any = [..._selectedRowKeys]
|
|
|
+ t = await t.concat(n)
|
|
|
+ // console.log(crossPageSelect)
|
|
|
+ const set = new Set(t)
|
|
|
+ selectedRowKeysBuffer.value = Array.from(set)
|
|
|
+ emit('onSelected', selectedRowKeysBuffer.value)
|
|
|
}
|
|
|
|
|
|
function onSelect(record: any) {
|
|
|
- emit('onSelectedRecord', record)
|
|
|
+ emit('onSelectedRecord', record)
|
|
|
}
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
const reset_search = async () => {
|
|
|
- Object.keys(params).forEach(v => {
|
|
|
- delete params[v]
|
|
|
- })
|
|
|
+ Object.keys(params).forEach(v => {
|
|
|
+ delete params[v]
|
|
|
+ })
|
|
|
|
|
|
- Object.assign(params, {
|
|
|
- ...props.get_params
|
|
|
- })
|
|
|
+ Object.assign(params, {
|
|
|
+ ...props.get_params
|
|
|
+ })
|
|
|
|
|
|
- router.push({query: {}}).catch(() => {
|
|
|
- })
|
|
|
+ router.push({query: {}}).catch(() => {
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
watch(params, () => {
|
|
|
- if (!props.disable_query_params) {
|
|
|
- router.push({query: params})
|
|
|
- }
|
|
|
- get_list()
|
|
|
+ if (!props.disable_query_params) {
|
|
|
+ router.push({query: params})
|
|
|
+ }
|
|
|
+ get_list()
|
|
|
})
|
|
|
|
|
|
const rowSelection = computed(() => {
|
|
|
- if (batchColumns.length > 0 || props.selectionType) {
|
|
|
- return {
|
|
|
- selectedRowKeys: selectedRowKeysBuffer.value, onChange: onSelectChange,
|
|
|
- onSelect: onSelect, type: batchColumns.length > 0 ? 'checkbox' : props.selectionType
|
|
|
- }
|
|
|
- } else {
|
|
|
- return null
|
|
|
+ if (batchColumns.length > 0 || props.selectionType) {
|
|
|
+ return {
|
|
|
+ selectedRowKeys: selectedRowKeysBuffer.value, onChange: onSelectChange,
|
|
|
+ onSelect: onSelect, type: batchColumns.length > 0 ? 'checkbox' : props.selectionType
|
|
|
}
|
|
|
+ } else {
|
|
|
+ return null
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
function fn(obj: Object, desc: string) {
|
|
|
- const arr: string[] = desc.split('.')
|
|
|
- while (arr.length) {
|
|
|
- // @ts-ignore
|
|
|
- const top = obj[arr.shift()]
|
|
|
- if (top === undefined) {
|
|
|
- return null
|
|
|
- }
|
|
|
- obj = top
|
|
|
+ const arr: string[] = desc.split('.')
|
|
|
+ while (arr.length) {
|
|
|
+ // @ts-ignore
|
|
|
+ const top = obj[arr.shift()]
|
|
|
+ if (top === undefined) {
|
|
|
+ return null
|
|
|
}
|
|
|
- return obj
|
|
|
+ obj = top
|
|
|
+ }
|
|
|
+ return obj
|
|
|
}
|
|
|
|
|
|
async function export_csv() {
|
|
|
- let header = []
|
|
|
- let headerKeys: any[] = []
|
|
|
- const showColumnsMap: any = {}
|
|
|
+ let header = []
|
|
|
+ let headerKeys: any[] = []
|
|
|
+ const showColumnsMap: any = {}
|
|
|
+ // @ts-ignore
|
|
|
+ for (let showColumnsKey in pithyColumns) {
|
|
|
// @ts-ignore
|
|
|
- for (let showColumnsKey in pithyColumns) {
|
|
|
- // @ts-ignore
|
|
|
- if (pithyColumns[showColumnsKey].dataIndex === 'action') continue
|
|
|
- // @ts-ignore
|
|
|
- let t = pithyColumns[showColumnsKey].title
|
|
|
-
|
|
|
- if (typeof t === 'function') {
|
|
|
- t = t()
|
|
|
- }
|
|
|
- header.push({
|
|
|
- title: t,
|
|
|
- // @ts-ignore
|
|
|
- key: pithyColumns[showColumnsKey].dataIndex
|
|
|
- })
|
|
|
- // @ts-ignore
|
|
|
- headerKeys.push(pithyColumns[showColumnsKey].dataIndex)
|
|
|
- // @ts-ignore
|
|
|
- showColumnsMap[pithyColumns[showColumnsKey].dataIndex] = pithyColumns[showColumnsKey]
|
|
|
- }
|
|
|
+ if (pithyColumns[showColumnsKey].dataIndex === 'action') continue
|
|
|
+ // @ts-ignore
|
|
|
+ let t = pithyColumns[showColumnsKey].title
|
|
|
|
|
|
- let dataSource: any = []
|
|
|
- let hasMore = true
|
|
|
- let page = 1
|
|
|
- while (hasMore) {
|
|
|
- // 准备 DataSource
|
|
|
- await props.api!.get_list({page}).then((response: any) => {
|
|
|
- if (response.data.length === 0) {
|
|
|
- hasMore = false
|
|
|
- return
|
|
|
- }
|
|
|
- if (response[props.data_key] === undefined) {
|
|
|
- dataSource = dataSource.concat(...response.data)
|
|
|
- } else {
|
|
|
- dataSource = dataSource.concat(...response[props.data_key])
|
|
|
- }
|
|
|
- }).catch((e: any) => {
|
|
|
- message.error(e.message ?? $gettext('Server error'))
|
|
|
- hasMore = false
|
|
|
- return
|
|
|
- })
|
|
|
- page += 1
|
|
|
+ if (typeof t === 'function') {
|
|
|
+ t = t()
|
|
|
}
|
|
|
- const data: any[] = []
|
|
|
- dataSource.forEach((row: Object) => {
|
|
|
- let obj: any = {}
|
|
|
- headerKeys.forEach(key => {
|
|
|
- let data = fn(row, key)
|
|
|
- const c = showColumnsMap[key]
|
|
|
- data = c?.customRender?.({text: data}) ?? data
|
|
|
- obj[c.dataIndex] = data
|
|
|
- })
|
|
|
- data.push(obj)
|
|
|
+ header.push({
|
|
|
+ title: t,
|
|
|
+ // @ts-ignore
|
|
|
+ key: pithyColumns[showColumnsKey].dataIndex
|
|
|
+ })
|
|
|
+ // @ts-ignore
|
|
|
+ headerKeys.push(pithyColumns[showColumnsKey].dataIndex)
|
|
|
+ // @ts-ignore
|
|
|
+ showColumnsMap[pithyColumns[showColumnsKey].dataIndex] = pithyColumns[showColumnsKey]
|
|
|
+ }
|
|
|
+
|
|
|
+ let dataSource: any = []
|
|
|
+ let hasMore = true
|
|
|
+ let page = 1
|
|
|
+ while (hasMore) {
|
|
|
+ // 准备 DataSource
|
|
|
+ await props.api!.get_list({page}).then((response: any) => {
|
|
|
+ if (response.data.length === 0) {
|
|
|
+ hasMore = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (response[props.data_key] === undefined) {
|
|
|
+ dataSource = dataSource.concat(...response.data)
|
|
|
+ } else {
|
|
|
+ dataSource = dataSource.concat(...response[props.data_key])
|
|
|
+ }
|
|
|
+ }).catch((e: any) => {
|
|
|
+ message.error(e.message ?? $gettext('Server error'))
|
|
|
+ hasMore = false
|
|
|
+ return
|
|
|
+ })
|
|
|
+ page += 1
|
|
|
+ }
|
|
|
+ const data: any[] = []
|
|
|
+ dataSource.forEach((row: Object) => {
|
|
|
+ let obj: any = {}
|
|
|
+ headerKeys.forEach(key => {
|
|
|
+ let data = fn(row, key)
|
|
|
+ const c = showColumnsMap[key]
|
|
|
+ data = c?.customRender?.({text: data}) ?? data
|
|
|
+ obj[c.dataIndex] = data
|
|
|
})
|
|
|
+ data.push(obj)
|
|
|
+ })
|
|
|
|
|
|
- downloadCsv(header, data,
|
|
|
- `${$gettext('Export')}-${dayjs().format('YYYYMMDDHHmmss')}.csv`)
|
|
|
+ downloadCsv(header, data,
|
|
|
+ `${$gettext('Export')}-${dayjs().format('YYYYMMDDHHmmss')}.csv`)
|
|
|
}
|
|
|
|
|
|
const hasSelectedRow = computed(() => {
|
|
|
- return batchColumns.length > 0 && selectedRowKeysBuffer.value.length > 0
|
|
|
+ return batchColumns.length > 0 && selectedRowKeysBuffer.value.length > 0
|
|
|
})
|
|
|
|
|
|
function click_batch_edit() {
|
|
|
- emit('clickBatchModify', batchColumns, selectedRowKeysBuffer.value)
|
|
|
+ emit('clickBatchModify', batchColumns, selectedRowKeysBuffer.value)
|
|
|
}
|
|
|
|
|
|
function getLeastIndex(index: number) {
|
|
|
- return index >= 1 ? index : 1
|
|
|
+ return index >= 1 ? index : 1
|
|
|
}
|
|
|
|
|
|
function getTargetData(data: any, indexList: number[]): any {
|
|
|
- let target: any = {children: data}
|
|
|
- indexList.forEach((index: number) => {
|
|
|
- target.children[index].parent = target
|
|
|
- target = target.children[index]
|
|
|
- })
|
|
|
- return target
|
|
|
+ let target: any = {children: data}
|
|
|
+ indexList.forEach((index: number) => {
|
|
|
+ target.children[index].parent = target
|
|
|
+ target = target.children[index]
|
|
|
+ })
|
|
|
+ return target
|
|
|
}
|
|
|
|
|
|
function initSortable() {
|
|
|
- const table: any = document.querySelector('#std-table tbody')
|
|
|
- new Sortable(table, {
|
|
|
- handle: '.ant-table-drag-icon',
|
|
|
- animation: 150,
|
|
|
- sort: true,
|
|
|
- forceFallback: true,
|
|
|
- setData: function (dataTransfer) {
|
|
|
- dataTransfer.setData('Text', '')
|
|
|
- },
|
|
|
- onStart({item}) {
|
|
|
- let targetRowKey = Number(item.dataset.rowKey)
|
|
|
- if (targetRowKey) {
|
|
|
- expand_keys_list.value = expand_keys_list.value.filter((item: number) => item !== targetRowKey)
|
|
|
- }
|
|
|
- },
|
|
|
- onMove({dragged, related}) {
|
|
|
- const oldRow: number[] = rows_key_index_map.value?.[Number(dragged.dataset.rowKey)]
|
|
|
- const newRow: number[] = rows_key_index_map.value?.[Number(related.dataset.rowKey)]
|
|
|
- if (oldRow.length !== newRow.length || oldRow[oldRow.length - 2] != newRow[newRow.length - 2]) {
|
|
|
- return false
|
|
|
- }
|
|
|
- },
|
|
|
- async onEnd({item, newIndex, oldIndex}) {
|
|
|
- if (newIndex === oldIndex) return
|
|
|
-
|
|
|
- const indexDelta: number = Number(oldIndex) - Number(newIndex)
|
|
|
- const direction: number = indexDelta > 0 ? +1 : -1
|
|
|
-
|
|
|
- let rowIndex: number[] = rows_key_index_map.value?.[Number(item.dataset.rowKey)]
|
|
|
- const newRow = getTargetData(data_source.value, rowIndex)
|
|
|
- const newRowParent = newRow.parent
|
|
|
- const level: number = newRow.level
|
|
|
-
|
|
|
- let currentRowIndex: number[] = [...rows_key_index_map.value?.
|
|
|
- [Number(table.children[Number(newIndex) + direction].dataset.rowKey)]]
|
|
|
- let currentRow: any = getTargetData(data_source.value, currentRowIndex)
|
|
|
- // Reset parent
|
|
|
- currentRow.parent = newRow.parent = null
|
|
|
- newRowParent.children.splice(rowIndex[level], 1)
|
|
|
- newRowParent.children.splice(currentRowIndex[level], 0, toRaw(newRow))
|
|
|
-
|
|
|
- let changeIds: number[] = []
|
|
|
-
|
|
|
- function processChanges(row: any, children: boolean = false, newIndex: number | undefined = undefined) {
|
|
|
- // Build changes ID list expect new row
|
|
|
- if (children || newIndex === undefined) changeIds.push(row.id)
|
|
|
-
|
|
|
- if (newIndex !== undefined)
|
|
|
- rows_key_index_map.value[row.id][level] = newIndex
|
|
|
- else if (children)
|
|
|
- rows_key_index_map.value[row.id][level] += direction
|
|
|
-
|
|
|
- row.parent = null
|
|
|
- if (row.children) {
|
|
|
- row.children.forEach((v: any) => processChanges(v, true, newIndex))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Replace row index for new row
|
|
|
- processChanges(newRow, false, currentRowIndex[level])
|
|
|
- // Rebuild row index maps for changes row
|
|
|
- for (let i = Number(oldIndex); i != newIndex; i -= direction) {
|
|
|
- let rowIndex: number[] = rows_key_index_map.value?.[table.children[i].dataset.rowKey]
|
|
|
- rowIndex[level] += direction
|
|
|
- processChanges(getTargetData(data_source.value, rowIndex))
|
|
|
- }
|
|
|
- console.log('Change row id', newRow.id, 'order', newRow.id, '=>', currentRow.id, ', direction: ', direction,
|
|
|
- ', changes IDs:', changeIds)
|
|
|
-
|
|
|
- props.api!.update_order({
|
|
|
- target_id: newRow.id,
|
|
|
- direction: direction,
|
|
|
- affected_ids: changeIds
|
|
|
- }).then(() => {
|
|
|
- message.success($gettext('Updated successfully'))
|
|
|
- }).catch((e: any) => {
|
|
|
- message.error(e?.message ?? $gettext('Server error'))
|
|
|
- })
|
|
|
+ const table: any = document.querySelector('#std-table tbody')
|
|
|
+ new Sortable(table, {
|
|
|
+ handle: '.ant-table-drag-icon',
|
|
|
+ animation: 150,
|
|
|
+ sort: true,
|
|
|
+ forceFallback: true,
|
|
|
+ setData: function (dataTransfer) {
|
|
|
+ dataTransfer.setData('Text', '')
|
|
|
+ },
|
|
|
+ onStart({item}) {
|
|
|
+ let targetRowKey = Number(item.dataset.rowKey)
|
|
|
+ if (targetRowKey) {
|
|
|
+ expand_keys_list.value = expand_keys_list.value.filter((item: number) => item !== targetRowKey)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onMove({dragged, related}) {
|
|
|
+ const oldRow: number[] = rows_key_index_map.value?.[Number(dragged.dataset.rowKey)]
|
|
|
+ const newRow: number[] = rows_key_index_map.value?.[Number(related.dataset.rowKey)]
|
|
|
+ if (oldRow.length !== newRow.length || oldRow[oldRow.length - 2] != newRow[newRow.length - 2]) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async onEnd({item, newIndex, oldIndex}) {
|
|
|
+ if (newIndex === oldIndex) return
|
|
|
+
|
|
|
+ const indexDelta: number = Number(oldIndex) - Number(newIndex)
|
|
|
+ const direction: number = indexDelta > 0 ? +1 : -1
|
|
|
+
|
|
|
+ let rowIndex: number[] = rows_key_index_map.value?.[Number(item.dataset.rowKey)]
|
|
|
+ const newRow = getTargetData(data_source.value, rowIndex)
|
|
|
+ const newRowParent = newRow.parent
|
|
|
+ const level: number = newRow.level
|
|
|
+
|
|
|
+ let currentRowIndex: number[] = [...rows_key_index_map.value?.
|
|
|
+ [Number(table.children[Number(newIndex) + direction].dataset.rowKey)]]
|
|
|
+ let currentRow: any = getTargetData(data_source.value, currentRowIndex)
|
|
|
+ // Reset parent
|
|
|
+ currentRow.parent = newRow.parent = null
|
|
|
+ newRowParent.children.splice(rowIndex[level], 1)
|
|
|
+ newRowParent.children.splice(currentRowIndex[level], 0, toRaw(newRow))
|
|
|
+
|
|
|
+ let changeIds: number[] = []
|
|
|
+
|
|
|
+ function processChanges(row: any, children: boolean = false, newIndex: number | undefined = undefined) {
|
|
|
+ // Build changes ID list expect new row
|
|
|
+ if (children || newIndex === undefined) changeIds.push(row.id)
|
|
|
+
|
|
|
+ if (newIndex !== undefined)
|
|
|
+ rows_key_index_map.value[row.id][level] = newIndex
|
|
|
+ else if (children)
|
|
|
+ rows_key_index_map.value[row.id][level] += direction
|
|
|
+
|
|
|
+ row.parent = null
|
|
|
+ if (row.children) {
|
|
|
+ row.children.forEach((v: any) => processChanges(v, true, newIndex))
|
|
|
}
|
|
|
- })
|
|
|
+ }
|
|
|
+
|
|
|
+ // Replace row index for new row
|
|
|
+ processChanges(newRow, false, currentRowIndex[level])
|
|
|
+ // Rebuild row index maps for changes row
|
|
|
+ for (let i = Number(oldIndex); i != newIndex; i -= direction) {
|
|
|
+ let rowIndex: number[] = rows_key_index_map.value?.[table.children[i].dataset.rowKey]
|
|
|
+ rowIndex[level] += direction
|
|
|
+ processChanges(getTargetData(data_source.value, rowIndex))
|
|
|
+ }
|
|
|
+ console.log('Change row id', newRow.id, 'order', newRow.id, '=>', currentRow.id, ', direction: ', direction,
|
|
|
+ ', changes IDs:', changeIds)
|
|
|
+
|
|
|
+ props.api!.update_order({
|
|
|
+ target_id: newRow.id,
|
|
|
+ direction: direction,
|
|
|
+ affected_ids: changeIds
|
|
|
+ }).then(() => {
|
|
|
+ message.success($gettext('Updated successfully'))
|
|
|
+ }).catch((e: any) => {
|
|
|
+ message.error(e?.message ?? $gettext('Server error'))
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <div class="std-table">
|
|
|
- <std-data-entry
|
|
|
- v-if="!disable_search && searchColumns.length"
|
|
|
- :data-list="searchColumns"
|
|
|
- :data-source="params"
|
|
|
- layout="inline"
|
|
|
- >
|
|
|
- <template #action>
|
|
|
- <a-space class="action-btn">
|
|
|
- <a-button v-if="exportCsv" @click="export_csv" type="primary" ghost>
|
|
|
- {{ $gettext('Export') }}
|
|
|
- </a-button>
|
|
|
- <a-button @click="reset_search">
|
|
|
- {{ $gettext('Reset') }}
|
|
|
- </a-button>
|
|
|
- <a-button v-if="hasSelectedRow" @click="click_batch_edit">
|
|
|
- {{ $gettext('Batch Modify') }}
|
|
|
- </a-button>
|
|
|
- </a-space>
|
|
|
- </template>
|
|
|
- </std-data-entry>
|
|
|
- <a-table
|
|
|
- :columns="pithyColumns"
|
|
|
- :data-source="data_source"
|
|
|
- :loading="loading"
|
|
|
- :pagination="false"
|
|
|
- :row-key="rowKey"
|
|
|
- :rowSelection="rowSelection"
|
|
|
- @change="stdChange"
|
|
|
- :scroll="{ x: scrollX }"
|
|
|
- :size="size"
|
|
|
- id="std-table"
|
|
|
- @expandedRowsChange="expandedTable"
|
|
|
- :expandedRowKeys="expand_keys_list"
|
|
|
- >
|
|
|
- <template
|
|
|
- v-slot:bodyCell="{text, record, index, column}"
|
|
|
- >
|
|
|
- <template v-if="column.handle === true">
|
|
|
- <span class="ant-table-drag-icon"><HolderOutlined/></span>
|
|
|
- {{ text }}
|
|
|
- </template>
|
|
|
- <template v-if="column.dataIndex === 'action'">
|
|
|
- <a-button type="link" size="small" v-if="props.editable"
|
|
|
- @click="$emit('clickEdit', record[props.rowKey], record)">
|
|
|
- {{ props.edit_text || $gettext('Modify') }}
|
|
|
- </a-button>
|
|
|
- <slot name="actions" :record="record"/>
|
|
|
- <template v-if="props.deletable">
|
|
|
- <a-divider type="vertical"/>
|
|
|
- <a-popconfirm
|
|
|
- :cancelText="$gettext('No')"
|
|
|
- :okText="$gettext('OK')"
|
|
|
- :title="$gettext('Are you sure you want to delete?')"
|
|
|
- @confirm="destroy(record[rowKey])">
|
|
|
- <a-button type="link" size="small">{{ $gettext('Delete') }}</a-button>
|
|
|
- </a-popconfirm>
|
|
|
- </template>
|
|
|
- </template>
|
|
|
- </template>
|
|
|
- </a-table>
|
|
|
- <std-pagination :size="size" :pagination="pagination" @change="get_list" @changePageSize="stdChange"/>
|
|
|
- </div>
|
|
|
+ <div class="std-table">
|
|
|
+ <std-data-entry
|
|
|
+ v-if="!disable_search && searchColumns.length"
|
|
|
+ :data-list="searchColumns"
|
|
|
+ :data-source="params"
|
|
|
+ layout="inline"
|
|
|
+ >
|
|
|
+ <template #action>
|
|
|
+ <a-space class="action-btn">
|
|
|
+ <a-button v-if="exportCsv" @click="export_csv" type="primary" ghost>
|
|
|
+ {{ $gettext('Export') }}
|
|
|
+ </a-button>
|
|
|
+ <a-button @click="reset_search">
|
|
|
+ {{ $gettext('Reset') }}
|
|
|
+ </a-button>
|
|
|
+ <a-button v-if="hasSelectedRow" @click="click_batch_edit">
|
|
|
+ {{ $gettext('Batch Modify') }}
|
|
|
+ </a-button>
|
|
|
+ </a-space>
|
|
|
+ </template>
|
|
|
+ </std-data-entry>
|
|
|
+ <a-table
|
|
|
+ :columns="pithyColumns"
|
|
|
+ :data-source="data_source"
|
|
|
+ :loading="loading"
|
|
|
+ :pagination="false"
|
|
|
+ :row-key="rowKey"
|
|
|
+ :rowSelection="rowSelection"
|
|
|
+ @change="stdChange"
|
|
|
+ :scroll="{ x: scrollX }"
|
|
|
+ :size="size"
|
|
|
+ id="std-table"
|
|
|
+ @expandedRowsChange="expandedTable"
|
|
|
+ :expandedRowKeys="expand_keys_list"
|
|
|
+ >
|
|
|
+ <template
|
|
|
+ v-slot:bodyCell="{text, record, index, column}"
|
|
|
+ >
|
|
|
+ <template v-if="column.handle === true">
|
|
|
+ <span class="ant-table-drag-icon"><HolderOutlined/></span>
|
|
|
+ {{ text }}
|
|
|
+ </template>
|
|
|
+ <template v-if="column.dataIndex === 'action'">
|
|
|
+ <a-button type="link" size="small" v-if="props.editable"
|
|
|
+ @click="$emit('clickEdit', record[props.rowKey], record)">
|
|
|
+ {{ props.edit_text || $gettext('Modify') }}
|
|
|
+ </a-button>
|
|
|
+ <slot name="actions" :record="record"/>
|
|
|
+ <template v-if="props.deletable">
|
|
|
+ <a-divider type="vertical"/>
|
|
|
+ <a-popconfirm
|
|
|
+ :cancelText="$gettext('No')"
|
|
|
+ :okText="$gettext('OK')"
|
|
|
+ :title="$gettext('Are you sure you want to delete?')"
|
|
|
+ @confirm="destroy(record[rowKey])">
|
|
|
+ <a-button type="link" size="small">{{ $gettext('Delete') }}</a-button>
|
|
|
+ </a-popconfirm>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </a-table>
|
|
|
+ <std-pagination :size="size" :pagination="pagination" @change="get_list" @changePageSize="stdChange"/>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<style lang="less">
|
|
|
.ant-table-scroll {
|
|
|
- .ant-table-body {
|
|
|
- overflow-x: auto !important;
|
|
|
- }
|
|
|
+ .ant-table-body {
|
|
|
+ overflow-x: auto !important;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
.ant-form {
|
|
|
- margin: 10px 0 20px 0;
|
|
|
+ margin: 10px 0 20px 0;
|
|
|
}
|
|
|
|
|
|
.ant-slider {
|
|
|
- min-width: 90px;
|
|
|
+ min-width: 90px;
|
|
|
}
|
|
|
|
|
|
.std-table {
|
|
|
- .ant-table-wrapper {
|
|
|
- // overflow-x: scroll;
|
|
|
- }
|
|
|
+ .ant-table-wrapper {
|
|
|
+ // overflow-x: scroll;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.action-btn {
|
|
|
- // min-height: 50px;
|
|
|
- height: 100%;
|
|
|
- display: flex;
|
|
|
- align-items: flex-start;
|
|
|
+ // min-height: 50px;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
}
|
|
|
|
|
|
:deep(.ant-form-inline .ant-form-item) {
|
|
|
- margin-bottom: 10px;
|
|
|
+ margin-bottom: 10px;
|
|
|
}
|
|
|
</style>
|
|
|
|
|
|
<style lang="less">
|
|
|
.ant-table-drag-icon {
|
|
|
- float: left;
|
|
|
- margin-right: 16px;
|
|
|
- cursor: grab;
|
|
|
+ float: left;
|
|
|
+ margin-right: 16px;
|
|
|
+ cursor: grab;
|
|
|
}
|
|
|
|
|
|
.sortable-ghost *, .sortable-chosen * {
|
|
|
- cursor: grabbing !important;
|
|
|
+ cursor: grabbing !important;
|
|
|
}
|
|
|
</style>
|