| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049 |
- <script setup lang="ts">
- import { VbUtil } from "@@/vb-dom"
- import apis from "@a"
- import symbolKeys from "@@@/table/symbol"
- import { getAssetPath } from "@@/utils"
- import Toolbar from "@@@/table/partials/toolbar/TableToolbar.vue"
- import Content from "@@@/table/partials/TableContent.vue"
- import Footer from "@@@/table/partials/footer/TableFooter.vue"
- import type { Sort, Header, Scroll, ToolBtn } from "@@@/table/models"
- import type { VbFormItem, VbFormRowItem } from "@@@/form/models"
- import type { AxiosRequestConfig } from "axios"
- import type { WritableComputedRef } from "vue"
- const formSlotSuffix = "tool-form_"
- const props = withDefaults(
- defineProps<{
- columns: Header[]
- columnsFun?: () => Header[]
- tableTitle?: string
- emptyTableText?: string
- keyField?: string
- initSearch?: boolean // 页面加载时自动查询
- autoSearch?: boolean // 参数变动时自动查询(深监听)
- data?: any
- selectedRows?: any[]
- /* remote */
- remoteFun?: (q: any) => Promise<any>
- url?: string
- method?: string
- configs?: AxiosRequestConfig
- queryParams?: any
- loading?: boolean
- loadingText?: string
- sortField?: string // 排序字段
- sortOrder?: "asc" | "desc"
- /* modal form */
- modal?: any
- formData?: any
- exportUrl?: string
- exportName?: string
- getEntityFun?: (v: any) => Promise<any>
- deleteEntityFun?: (v: any) => Promise<any>
- resetFormFun?: () => void
- /* toolbar */
- showToolbar?: boolean // 是否显示toolbar
- /* searchbar */
- showSearchBar?: boolean // 是否显示Searchbar
- searchFormRowItems?: VbFormRowItem[]
- searchFormItems?: VbFormItem[]
- searchFormSpan?: number
- customSearchFun?: (query: any) => void
- resetSearchFormFun?: (query: any) => void
- /* left toolbar */
- handleBtns?: ToolBtn[]
- handlePerm?: string
- handleFuns?: object
- handleDisabledFuns?: object
- useCustomBtns?: boolean // 不从远程获取,直接使用自定义的Btns
- customBtns?: ToolBtn[]
- btnIconPrefix?: string
- /* right toolbar */
- showRightToolbar?: boolean // 是否显示右边的toolbar
- showRightSearchBtn?: boolean // 初始是否显示搜索栏
- showRightColumnBtn?: boolean // 是否显示右边toolbar的选择列按钮
- rightToolbarBtnGutter?: number // 右边toolbar按钮间距
- /* checkbox */
- checkMultiple?: boolean // 是否多选
- checkPageMultiple?: boolean // 是否跨页多选
- hasCheckbox?: boolean // 是否有选择框 (checkMultiple 和 checkPageMultiple 为true 时不起作用)
- /* pagination */
- pagination?: boolean
- noPage?: boolean // 查询不带分页参数
- total?: number
- currentPage?: number
- maxPageBtnCount?: number
- pageSize?: number
- pageSizeArray?: number[]
- pageSizeChange?: boolean
- /* fixed */
- fixedHeader?: boolean // 固定列头
- fixedNumber?: number // 左边固定列数
- fixedRightNumber?: number // 右边固定列数
- scroll?: Scroll
- /* rowspan */
- rowSpanSuffix?: string // rowspan 的字段后缀 后台应该组成 `${字段}${后缀}`
- /* tree */
- isTree?: boolean
- expandDepth?: number
- intervalLeft?: number
- parentField?: string
- iconField?: string
- childrenField?: string
- /* tree lazy */
- isLazy?: boolean
- isLazySearch?: boolean
- rootId?: string
- leafField?: string
- /* class & style */
- tableBoxClass?: string
- tableBoxStyle?: any
- tableBoxHeight?: number | string
- tableBoxWidth?: number | string
- tableClass?: string
- headerClass?: string
- bodyClass?: string
- thTrClass?: string
- tdTrClass?: string
- rowClick?: (row: any) => void
- rowDbClick?: (row: any) => void
- }>(),
- {
- tableTitle: "",
- emptyTableText: "未查询到数据",
- loadingText: "正在加载中,请稍后...",
- initSearch: true,
- autoSearch: false,
- selectedRows: () => [],
- method: "get",
- sortOrder: "asc",
- showToolbar: true,
- showSearchBar: true,
- searchFormRowItems: () => [],
- searchFormItems: () => [],
- searchFormSpan: 4,
- showRightToolbar: true,
- showRightColumnBtn: true,
- showRightSearchBtn: true,
- rightToolbarBtnGutter: 10,
- pagination: true,
- noPage: false,
- total: 0,
- currentPage: 1,
- pageSizeArray: () => {
- return [15, 25, 50]
- },
- pageSizeChange: true,
- maxPageBtnCount: 5,
- hasCheckbox: false,
- checkMultiple: false,
- checkPageMultiple: false,
- fixedHeader: false,
- fixedNumber: 0,
- fixedRightNumber: 0,
- rowSpanSuffix: "_rowSpan",
- isTree: false,
- parentField: "parent_id",
- childrenField: "children",
- iconField: "name",
- leafField: "isLeaf",
- expandDepth: 0,
- intervalLeft: 10,
- btnIconPrefix: "me-1 bi bi-",
- tableBoxClass: "w-100 p-5",
- tableClass: "table-bordered table-rounded",
- headerClass: "",
- bodyClass: "fw-semibold text-gray-600",
- thTrClass: "text-center text-gray-800 fw-bold fs-7",
- tdTrClass: "text-center fs-7 "
- }
- )
- const emits = defineEmits<{
- (e: "update:columns", v: Header[]): void
- (e: "update:selectedRows", v: any[]): void
- (e: "update:currentPage", v: number): void
- (e: "update:pageSize", v: number): void
- (e: "update:loading", v: boolean): void
- (e: "update:queryParams", v: any): void
- (e: "page-change", v: number): void
- (e: "update:formData", v: any): void
- (e: "sort", v: Sort[]): void
- (e: "row-click", row: any): void
- (e: "row-db-click", row: any): void
- (e: "checkbox-change", isChecked: boolean, row: any): void
- (e: "select", row: any): void
- (e: "unSelect", row: any): void
- (e: "checkbox-all", isChecked: boolean, rows: any[]): void
- (e: "select-all", rows: any[]): void
- (e: "unSelect-all", rows: any[]): void
- }>()
- const tableBoxRef = ref()
- const tableContentRef = ref()
- const id = ref("")
- const { data, searchFormRowItems, searchFormItems } = toRefs(props)
- const leftFixedRef = ref()
- const tableResponsiveRef = ref()
- const rightFixedRef = ref()
- const emptyQueryParams = JSON.stringify(props.queryParams ? props.queryParams : {})
- let treeData: any = []
- let treeAllData: any = []
- const remoteTotal = ref<number>(0)
- const remoteData = ref<any[]>([])
- const checkAll = ref<boolean>(false)
- const toolbarHandleBtns = ref<ToolBtn[]>([])
- const sortParams = ref<Sort[]>([
- {
- label: props.sortField ?? "",
- order: props.sortOrder == "asc" ? "asc" : "desc"
- }
- ])
- const _columns = ref<Header[]>([])
- const columns: WritableComputedRef<Header[]> = computed({
- get(): Header[] {
- if (_columns.value && _columns.value.length > 0) {
- if (props.columnsFun) {
- const cols = props.columnsFun() ?? []
- const newCols: Header[] = []
- cols.forEach((v: Header) => {
- const col = _columns.value.find((vv: Header) => vv.field == v.field)
- if (col) {
- v.visible = col.visible
- }
- newCols.push(v)
- })
- _columns.value = newCols
- }
- } else {
- _columns.value = props.columnsFun ? props.columnsFun() : props.columns ?? []
- }
- return _columns.value
- },
- set(value: Header[]): void {
- _columns.value = value
- emits("update:columns", value)
- }
- })
- const _selectedRows = ref<any[]>()
- const selectedRows: WritableComputedRef<any[]> = computed({
- get(): any[] {
- return _selectedRows.value ?? props.selectedRows ?? []
- },
- set(value: any[]): void {
- _selectedRows.value = value
- emits("update:selectedRows", value)
- }
- })
- const _currentPage = ref<number>()
- const currentPage: WritableComputedRef<number> = computed({
- get(): number {
- return _currentPage.value ?? props.currentPage
- },
- set(value: number): void {
- if (_currentPage.value != value) {
- _currentPage.value = value
- emits("update:currentPage", value)
- emits("page-change", value)
- }
- }
- })
- const _pageSize = ref<number>()
- const pageSize: WritableComputedRef<number> = computed({
- get(): number {
- return _pageSize.value ?? props.pageSize ?? props.pageSizeArray[0]
- },
- set(value: number): void {
- _pageSize.value = value
- emits("update:pageSize", value)
- }
- })
- const _loading = ref<boolean>()
- const loading: WritableComputedRef<boolean> = computed({
- get(): boolean {
- return _loading.value ?? props.loading
- },
- set(value: boolean): void {
- _loading.value = value
- emits("update:loading", value)
- }
- })
- const _innerQueryParams = ref<any>()
- const innerQueryParams: WritableComputedRef<any> = computed({
- get(): boolean {
- return _innerQueryParams.value ?? props.queryParams
- },
- set(value: any): void {
- _innerQueryParams.value = value
- }
- })
- const rowColumns = computed(() => {
- const rowCols: Header[] = []
- function calcRowColumns(list: Header[], isVisible: boolean) {
- list.forEach((v) => {
- const visible = isVisible && (v.visible === undefined || v.visible)
- if (v.children && v.children.length > 0) {
- calcRowColumns(v.children, visible)
- } else {
- rowCols.push(Object.assign({}, v, { visible }))
- }
- })
- }
- calcRowColumns(columns.value, true)
- return rowCols
- })
- const keyField = ref(props.keyField ? props.keyField : rowColumns.value[0].field)
- const displayData = computed(() => {
- let tableData: any = []
- if (!!props.remoteFun || !!props.url) {
- tableData = remoteData.value || []
- } else if (data?.value) {
- tableData = data.value
- }
- if (tableData.length <= pageSize.value) {
- return tableData
- } else {
- const sliceFrom = (currentPage.value - 1) * pageSize.value
- return tableData.slice(sliceFrom, sliceFrom + pageSize.value)
- }
- })
- const isMultipleCheck = computed(() => {
- return props.checkMultiple || props.checkPageMultiple
- })
- const dataTotalCount = computed(() => {
- return !!props.remoteFun || !!props.url ? remoteTotal.value : props.total
- })
- const selectedIds = computed(() => {
- return selectedRows.value.map((v) => v[keyField.value])
- })
- const selectedRowCount = computed(() => {
- return selectedRows.value.length
- })
- const tableBoxClass = computed(() => {
- let classStr = props.tableBoxClass
- if (props.isTree) {
- classStr += " vb-tree-table"
- }
- return classStr
- })
- const _tableBoxStyle = ref(calcTableBoxStyle())
- const fixedColumn = computed(() => props.fixedNumber > 0 || props.fixedRightNumber > 0)
- const tableStyle = computed(() => {
- let style = ""
- let x = props.scroll?.x
- const y = props.scroll?.y
- if (fixedColumn.value) {
- style += "overflow:auto;"
- x = x ?? 1920
- }
- if (x) {
- style += `width:${Number(x) ? x + "px" : x};`
- }
- if (y) {
- style += `height:${Number(y) ? y + "px" : y};`
- }
- return style
- })
- const tableRef = computed(() => {
- return tableContentRef.value.tableRef
- })
- const defaultHandleFuns = {
- handleCreate: () => {
- props.resetFormFun && props.resetFormFun()
- if (props.modal) {
- props.modal.show()
- props.modal.changePrefixTitle("添加")
- }
- },
- handleUpdate: (e?: any, row?: any) => {
- props.resetFormFun && props.resetFormFun()
- row = row || getSelected()
- if (!row) {
- return
- }
- if (props.getEntityFun) {
- props.getEntityFun(row[keyField.value]).then((res) => {
- emits("update:formData", res.data)
- if (props.modal) {
- props.modal.show()
- props.modal.changePrefixTitle("修改")
- }
- })
- } else {
- const _row = Object.assign({}, row)
- emits("update:formData", _row)
- if (props.modal) {
- props.modal.show()
- props.modal.changePrefixTitle("修改")
- }
- }
- },
- handleDelete: (e?: any, rows?: any) => {
- rows = rows || getSelecteds()
- if (!rows) {
- return
- }
- if (!props.deleteEntityFun) {
- message.alertError("删除接口方法未配置,请联系管理员")
- return
- }
- const ids = rows.map((v: any) => v[keyField.value])
- message
- .confirm('是否确认删除编号为"' + ids.join(",") + '"的数据项?')
- .then(() => {
- props.deleteEntityFun &&
- props.deleteEntityFun(ids).then(() => {
- customSearch()
- })
- })
- .catch(() => {
- //
- })
- },
- handleExport: () => {
- console.log("TE")
- if (props.exportUrl) {
- download(
- props.exportUrl,
- `${props.exportName ? props.exportName + "_" : ""}${new Date().getTime()}.xlsx`,
- { method: "POST", params: { ...innerQueryParams.value } }
- )
- } else {
- message.alertError("导出接口方法未配置,请联系管理员")
- }
- }
- }
- const resetData = () => {
- currentPage.value = 1
- selectedRows.value = []
- }
- const remote = (id?: string) => {
- const params = props.noPage
- ? innerQueryParams.value
- : Object.assign(
- { pageNum: currentPage.value, pageSize: pageSize.value },
- innerQueryParams.value
- )
- if (sortParams.value.length) {
- params.orderByColumn = ""
- params.isAsc = ""
- sortParams.value.forEach((v: any) => {
- if (v.order && v.label) {
- params.orderByColumn += (params.orderByColumn ? "," : "") + v.label
- params.isAsc += (params.isAsc ? "," : "") + v.order
- }
- })
- }
- let curData: any = {}
- if (props.isLazy) {
- curData = treeAllData.find((v: any) => {
- return v[keyField.value] == id
- })
- if (!id && !curData) {
- throw new Error("懒加载模式,未获取到 parentId ,请检查")
- }
- if (curData?.children && curData?.children.length) {
- if (props.isLazySearch) {
- curData.children = []
- } else {
- return Promise.resolve(0)
- }
- }
- if (!props.keyField) {
- throw new Error("懒加载模式,请配置 keyField")
- }
- params[props.keyField] = id
- }
- loading.value = true
- return new Promise((resolve, reject) => {
- if (props.remoteFun) {
- props
- .remoteFun(params)
- .then((res: any) => {
- processData(res.rows || res.data, res.total)
- })
- .catch((err) => {
- loading.value = false
- reject(err)
- })
- } else if (props.url) {
- const qParam = props.method.toLocaleLowerCase() == "get" ? { params } : { data: params }
- const configs = Object.assign(
- { url: props.url, method: props.method, successAlert: false },
- props.configs,
- qParam
- )
- Rs.request(configs)
- .then((res: any) => {
- processData(res.rows || res.data, res.total)
- })
- .catch(() => {
- loading.value = false
- })
- }
- function processData(data: any, total: number) {
- remoteTotal.value = total || 0
- loading.value = false
- if (props.isLazy) {
- if (props.isLazySearch) {
- Object.keys(innerQueryParams.value).forEach((v) => {
- innerQueryParams.value[v] = undefined
- })
- if (!data.length) {
- const item = {}
- item[props.iconField ?? ""] = "没有查询到字节点"
- item[props.leafField ?? ""] = true
- item[props.parentField ?? ""] = id
- data.push(item)
- }
- resolve(1)
- }
- if (curData) {
- curData.children = []
- data.forEach((v: any) => {
- const index = treeAllData.findIndex((vv: any) => {
- return vv[keyField.value] == v[keyField.value]
- })
- if (index < 0) {
- treeAllData.push(v)
- curData.children.push(treeAllData[treeAllData.length - 1])
- } else {
- curData.children.push(treeAllData[index])
- }
- })
- } else {
- treeData.push(...data)
- treeAllData.push(...treeData)
- }
- remoteData.value = JSON.parse(JSON.stringify(treeData))
- } else {
- remoteData.value = data
- }
- }
- })
- }
- const isContentSlots = (name: string | number): boolean => {
- const str = name.toString()
- return (
- rowColumns.value.find((v) => {
- return v.field == str || v.field + "_header" == str
- }) != null
- )
- }
- const loadHandleBtns = () => {
- toolbarHandleBtns.value = []
- if (!props.useCustomBtns && props.handlePerm) {
- const _handleFuns = Object.assign({}, defaultHandleFuns, props.handleFuns ?? {})
- apis.system.menuApi.menuChildrenByPerms(props.handlePerm).then((res) => {
- if (res.data && res.data.length > 0) {
- res.data.forEach((v: any) => {
- if (v.btnScript && v.btnScript != "hide") {
- const scripts = v.btnScript.split("@")
- const _btn = props.handleBtns?.find((v) => {
- return v.key == scripts[0]
- })
- const btn: ToolBtn = {
- key: scripts[0],
- show: _btn?.show == undefined ? true : _btn?.show,
- name: _btn?.name ?? v.menuName,
- permission: _btn?.permission ?? v.perms,
- clickFun: _btn?.clickFun ?? _handleFuns[scripts[0]],
- icon: _btn?.icon ?? `${props.btnIconPrefix}${v.icon}`,
- iconType: _btn?.iconType ?? "class",
- btnClass: _btn?.btnClass ?? v.btnClass,
- disabledFun: _btn?.disabledFun
- }
- if (scripts.length > 1) {
- btn.disabledFun = (r: number) => {
- const temp = Number(scripts[1])
- if (temp == 0) {
- return r == 0
- }
- return r != Number(scripts[1])
- }
- }
- if (props.handleDisabledFuns && props.handleDisabledFuns[scripts[0]]) {
- btn.disabledFun = props.handleDisabledFuns[scripts[0]]
- }
- toolbarHandleBtns.value.push(btn)
- }
- })
- }
- toolbarHandleBtns.value.push(...(props.customBtns ?? []))
- })
- } else {
- toolbarHandleBtns.value = props.customBtns ?? []
- }
- }
- const customSearch = () => {
- if (props.customSearchFun) {
- props.customSearchFun(innerQueryParams.value)
- } else {
- search()
- }
- }
- function calcTableBoxStyle() {
- const style = props.tableBoxStyle ?? {}
- if (props.tableBoxHeight) {
- style.height = Number(props.tableBoxHeight) ? props.tableBoxHeight + "px" : props.tableBoxHeight
- } else {
- nextTick(() => {
- const tableBoxAutoHeight =
- document.documentElement.clientHeight -
- (document.querySelector(".app-header")?.clientHeight ?? 0) -
- (document.querySelector(".app-footer")?.clientHeight ?? 0) -
- 5
- style.height = tableBoxAutoHeight + "px"
- })
- }
- if (props.tableBoxWidth) {
- style.width = Number(props.tableBoxWidth) ? props.tableBoxWidth + "px" : props.tableBoxWidth
- } else {
- style.width = "100%"
- }
- return style
- }
- function query(query?: any) {
- if (query) {
- innerQueryParams.value = Object.assign({}, query)
- }
- search()
- }
- function search(isReset = true) {
- if (isReset) {
- resetData()
- }
- if (!!props.remoteFun || !!props.url) {
- remoteData.value = []
- treeData = []
- treeAllData = []
- remote(props.isLazy ? props.rootId : undefined)
- }
- }
- function getQueryParams() {
- return innerQueryParams.value
- }
- function getSelected() {
- return getSelecteds()[0]
- }
- function getSelecteds() {
- return selectedRows.value
- }
- function getSelectedIds() {
- return selectedIds.value
- }
- function getData() {
- return remoteData.value
- }
- function setSelecteds(rows: any[] | any, isSelected: boolean) {
- if (!Array.isArray(rows)) {
- rows = [rows]
- }
- if (isSelected) {
- selectedRows.value = [...new Set([...selectedRows.value, ...rows])]
- } else {
- selectedRows.value = selectedRows.value.filter((item: any) => !rows.includes(item))
- }
- }
- function clearSelecteds() {
- selectedRows.value = []
- }
- provide(symbolKeys.tableBox, tableBoxRef)
- provide(symbolKeys.searchFrom, {
- queryParams: innerQueryParams,
- searchFormRowItems,
- searchFormItems,
- searchFormSpan: props.searchFormSpan
- })
- provide(symbolKeys.toolBtns, toolbarHandleBtns)
- provide(symbolKeys.selectedRowCount, selectedRowCount)
- provide(symbolKeys.rightToolbar, {
- columns,
- showSearchBtn:
- props.showRightSearchBtn && (!!props.searchFormItems || !!props.searchFormRowItems),
- showColumnBtn: props.showRightColumnBtn,
- gutter: props.rightToolbarBtnGutter
- })
- provide(symbolKeys.content, {
- loading,
- tableClass: props.tableClass,
- tableStyle: tableStyle.value
- })
- provide(symbolKeys.checkAll, checkAll)
- provide(symbolKeys.header, {
- columns,
- hasCheckbox: isMultipleCheck.value || props.hasCheckbox,
- isMultipleCheck: isMultipleCheck.value,
- sortField: props.sortField ?? "",
- sortOrder: props.sortOrder,
- headerClass: props.fixedHeader ? props.headerClass + " fixed" : props.headerClass,
- thTrClass: props.thTrClass
- })
- provide(symbolKeys.body, {
- isTree: props.isTree,
- childrenField: props.childrenField,
- bodyClass: props.bodyClass
- })
- provide(symbolKeys.bodyTr, {
- selectedIds,
- isTree: props.isTree,
- keyField: keyField.value,
- hasCheckbox: isMultipleCheck.value || props.hasCheckbox,
- isMultipleCheck: isMultipleCheck.value,
- expandDepth: props.expandDepth,
- intervalLeft: props.intervalLeft,
- iconField: props.iconField,
- parentField: props.parentField,
- childrenField: props.childrenField,
- leafField: props.leafField,
- isLazy: props.isLazy,
- lazySearch: props.isLazySearch,
- tdTrClass: props.tdTrClass
- })
- provide(symbolKeys.bodyTds, {
- columns: rowColumns,
- rowSpanSuffix: props.rowSpanSuffix,
- expandDepth: props.expandDepth,
- intervalLeft: props.intervalLeft,
- iconField: props.iconField,
- parentField: props.parentField,
- childrenField: props.childrenField,
- leafField: props.leafField,
- isLazy: props.isLazy
- })
- provide(symbolKeys.displayData, displayData)
- provide(symbolKeys.pagination, {
- total: dataTotalCount,
- currentPage,
- pageSize,
- maxPageBtnCount: props.maxPageBtnCount
- })
- provide(symbolKeys.pagePerItems, {
- total: dataTotalCount,
- currentPage,
- pageSize,
- pageSizeArray: props.pageSizeArray,
- pageSizeChange: props.pageSizeChange
- })
- provide(symbolKeys.formSlotSuffix, formSlotSuffix)
- const onPageChange = (page: number) => {
- currentPage.value = page
- search(false)
- }
- const onPageSizeChange = (count: number) => {
- pageSize.value = count
- search()
- }
- const onColumnsChange = (headers: Header[]) => {
- columns.value = headers
- }
- const onSort = (v: Sort[]) => {
- const sorts = toValue(v)
- sortParams.value = sorts
- emits("sort", sorts)
- search(false)
- }
- const onSelectAll = (isChecked: boolean) => {
- let sRows: any = []
- if (isChecked) {
- sRows = [...new Set([...selectedRows.value, ...displayData.value])]
- selectedRows.value = sRows
- emits("select-all", sRows)
- emits("checkbox-all", true, sRows)
- } else {
- if (props.checkPageMultiple) {
- sRows = selectedRows.value.filter(
- (v: any) => !displayData.value.find((vv: any) => v[keyField.value] == vv[keyField.value])
- )
- } else {
- sRows = []
- }
- selectedRows.value = sRows
- emits("unSelect-all", sRows)
- emits("checkbox-all", false, displayData.value)
- }
- }
- const onSelect = (v: { isChecked: boolean; row: any }) => {
- let sRows: any = []
- const row = toValue(v.row)
- if (v.isChecked) {
- sRows = isMultipleCheck.value ? [...new Set([...selectedRows.value, row])] : [row]
- selectedRows.value = sRows
- emits("select", row)
- emits("checkbox-change", true, row)
- } else {
- sRows = isMultipleCheck.value
- ? selectedRows.value.filter((vv) => vv[keyField.value] != row[keyField.value])
- : []
- selectedRows.value = sRows
- emits("unSelect", row)
- emits("checkbox-change", false, row)
- }
- }
- const onQuery = () => {
- emits("update:queryParams", innerQueryParams.value)
- }
- const onRowClick = (v: any) => {
- const row = toValue(v)
- props.rowClick && props.rowClick(row)
- emits("row-click", row)
- }
- const onRowDbClick = (v: any) => {
- const row = toValue(v)
- props.rowDbClick && props.rowDbClick(row)
- emits("row-db-click", row)
- }
- const onReset = () => {
- innerQueryParams.value = JSON.parse(emptyQueryParams)
- props.resetSearchFormFun && props.resetSearchFormFun(innerQueryParams.value)
- emits("update:queryParams", innerQueryParams.value)
- }
- const onTreeToggle = (v: any) => {
- const row = toValue(v)
- const id = "tr_" + row[keyField.value]
- const tr = tableBoxRef.value?.querySelector(`#${id}`)
- const icon = tr?.querySelector(".icon")
- if (!tr || !icon) {
- return
- }
- if (props.isLazy) {
- // 已有children可展开的直接展开,不在调用懒加载
- if (tr.className.search("hasChildren") >= 0) {
- if (tr.className.search("tr-expand") >= 0 || !props.isLazySearch) {
- toggle(tr, icon, id)
- return
- }
- }
- remote(id).then((res) => {
- if (res == 1 && props.isLazySearch) {
- nextTick(() => {
- showChildren(id)
- })
- }
- })
- tr.className = tr.className.replace("tr-collapse", "tr-expand")
- icon.className = icon.className.replace("ki-add-folder", "ki-minus-folder")
- } else {
- toggle(tr, icon, id)
- }
- function toggle(_tr: HTMLElement, _icon: HTMLElement, id: string) {
- if (_tr.className.search("tr-expand") >= 0) {
- _tr.className = _tr.className.replace("tr-expand", "tr-collapse")
- _icon.className = _icon.className.replace("ki-minus-folder", "ki-add-folder")
- hideChildren(id)
- } else if (_tr.className.search("tr-collapse") >= 0) {
- _tr.className = _tr.className.replace("tr-collapse", "tr-expand")
- _icon.className = _icon.className.replace("ki-add-folder", "ki-minus-folder")
- showChildren(id)
- }
- }
- function hideChildren(parentId: string) {
- const children = document.querySelectorAll(`[data-parent="${parentId}"]`)
- if (children && children.length) {
- children.forEach((v) => {
- v.className = v.className.replace("show", "hide")
- v.className = v.className.replace("tr-expand", "tr-collapse")
- const iconEl = v.querySelector(".icon")
- if (iconEl) {
- iconEl.className = iconEl.className.replace("ki-minus-folder", "ki-add-folder")
- }
- hideChildren(v.id)
- })
- }
- }
- function showChildren(parentId: string) {
- const children = document.querySelectorAll(`[data-parent="${parentId}"]`)
- if (children && children.length) {
- children.forEach((v) => {
- v.className = v.className.replace("hide", "show")
- })
- }
- }
- }
- const onFixedScrollX = () => {
- leftFixedRef.value.className = leftFixedRef.value.className.replace("no-shadow", "")
- rightFixedRef.value.className = rightFixedRef.value.className.replace("no-shadow", "")
- if (tableResponsiveRef.value) {
- const left = tableResponsiveRef.value.scrollLeft
- if (left == 0) {
- leftFixedRef.value.className += " no-shadow"
- }
- if (left >= tableRef.value.clientWidth - tableResponsiveRef.value.clientWidth) {
- rightFixedRef.value.className += " no-shadow"
- }
- }
- }
- function BindInterEvent() {
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.row.click", onRowClick)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.row.dbclick", onRowDbClick)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.query", onQuery)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.reset", onReset)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.sort", onSort)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.select-all", onSelectAll)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.select", onSelect)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.page-size-change", onPageSizeChange)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.page-change", onPageChange)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.columns-change", onColumnsChange)
- VbUtil.EventHandlerUtil.on(tableBoxRef.value, "vbtable.row.tree-toggle", onTreeToggle)
- if (fixedColumn.value && tableResponsiveRef.value) {
- tableResponsiveRef.value.addEventListener("scroll", onFixedScrollX, true)
- }
- }
- function initFixedColumns() {
- if (fixedColumn.value) {
- const tr = tableRef.value?.querySelector("tr")
- if (tr && tr.children) {
- let width = 0
- if (leftFixedRef.value) {
- let num = props.fixedNumber
- if (props.hasCheckbox || props.checkMultiple || props.checkPageMultiple) {
- num += 1
- }
- for (let i = 0; i < num; i++) {
- width += tr.children[i]?.clientWidth ?? 0
- }
- leftFixedRef.value.style.width = width + "px"
- leftFixedRef.value.firstElementChild.style.width = tableRef.value.clientWidth + "px"
- }
- if (rightFixedRef.value) {
- width = 0
- const trLength = tr.children.length
- for (let i = trLength - 1; i >= trLength - props.fixedRightNumber; i--) {
- width += tr.children[i]?.clientWidth ?? 0
- }
- rightFixedRef.value.style.width = width + "px"
- rightFixedRef.value.firstElementChild.style.width = tableRef.value.clientWidth + "px"
- rightFixedRef.value.firstElementChild.style.transform = `translateX(-${
- tableRef.value.clientWidth - width
- }px)`
- if (tableRef.value.clientWidth <= tableResponsiveRef.value.clientWidth) {
- rightFixedRef.value.className += " no-shadow"
- }
- }
- }
- }
- }
- function init() {
- id.value = VbUtil.getUniqueIdWithPrefix("vb-table")
- if (props.initSearch) {
- customSearch()
- }
- loadHandleBtns()
- nextTick(() => {
- initFixedColumns()
- BindInterEvent()
- })
- window.addEventListener("resize", () => {
- _tableBoxStyle.value = calcTableBoxStyle()
- })
- }
- onMounted(init)
- onUnmounted(() => {
- tableResponsiveRef.value?.removeEventListener("scroll", onFixedScrollX, true)
- })
- watch(selectedRows, (val: any) => {
- if (val.length > 0 && val.length == displayData.value.length) {
- let flag = true
- displayData.value.forEach((item: any) => {
- if (
- selectedRows.value.find((row: any) => row[keyField.value] == item[keyField.value]) == null
- ) {
- flag = false
- return
- }
- })
- checkAll.value = flag
- } else {
- checkAll.value = false
- }
- })
- watch(
- () => props.queryParams,
- (val: any) => {
- innerQueryParams.value = val
- customSearch()
- },
- { deep: props.autoSearch }
- )
- defineExpose({
- tableRef: tableBoxRef,
- query,
- getSelected,
- getSelectedIds,
- getSelecteds,
- getData,
- getQueryParams,
- setSelecteds,
- clearSelecteds,
- defaultHandleFuns
- })
- </script>
- <template>
- <div ref="tableBoxRef" class="vb-table" :id="id" :class="tableBoxClass" :style="_tableBoxStyle">
- <Toolbar
- v-if="showToolbar"
- :tableTitle="tableTitle"
- :showSearchBar="showSearchBar"
- :showRightToolbar="showRightToolbar">
- <template v-for="(_, name) in $slots" #[name]>
- <slot v-if="name.toString().search(formSlotSuffix) == 0" :name="name" />
- </template>
- </Toolbar>
- <div class="table-box">
- <div
- v-if="props.fixedNumber > 0"
- ref="leftFixedRef"
- class="fixed-columns fixed-columns-left no-shadow">
- <Content>
- <template v-for="(_, name) in $slots" #[name]="{ row }">
- <slot v-if="isContentSlots(name)" :name="name" :row="row" />
- </template>
- </Content>
- </div>
- <div ref="tableResponsiveRef" class="table-responsive">
- <Content ref="tableContentRef">
- <template v-for="(_, name) in $slots" #[name]="{ row }">
- <slot v-if="isContentSlots(name)" :name="name" :row="row" />
- </template>
- </Content>
- <template v-if="loading">
- <div class="h-100px d-flex justify-content-center flex-column align-items-center">
- <div class="spinner-border text-gray-700 mb-5"></div>
- <span class="text-gray-700">{{ loadingText }}</span>
- </div>
- </template>
- <div
- v-else-if="displayData.length == 0 && !loading"
- class="h-100px d-flex justify-content-center flex-column align-items-center">
- <img class="mb-2" :src="getAssetPath('media/table/empty.svg')" />
- <span class="text-gray-700">{{ emptyTableText }}</span>
- </div>
- </div>
- <div v-if="props.fixedRightNumber > 0" ref="rightFixedRef" class="fixed-columns-right">
- <Content>
- <template v-for="(_, name) in $slots" #[name]="{ row }">
- <slot v-if="isContentSlots(name)" :name="name" :row="row" />
- </template>
- </Content>
- </div>
- </div>
- <Footer :loading="loading" :pagination="pagination && !noPage" />
- </div>
- </template>
|