+}
+
+interface PageLayoutType {
+ /* columns: string[]
+ rows: string[] */
+ dataListHeight: string
+}
+
+export interface ATablePageType {
+ pageLayout: PageLayoutType
+ searchForm: FormPropsType
+ toolBar: ToolBarType
+ table: TablePropsType
+}
+
+function buildSearchForm(searchForm: Partial> = {}) {
+ if (searchForm.defaultData == null) {
+ searchForm.defaultData = {} as P
+ }
+ return searchForm
+}
+
+
+function buildPageLayout(pageLayout: Partial = {}) {
+ /* if (pageLayout.columns == null) {
+ pageLayout.columns = [ '1fr' ]
+ }
+
+ if (pageLayout.rows == null) {
+ pageLayout.rows = [ '1fr', '9fr' ]
+ } */
+ if (pageLayout.dataListHeight == null) {
+ pageLayout.dataListHeight = '9fr'
+ }
+
+ return pageLayout
+}
+
+function buildTable(table: Partial> = {}) {
+ if (table.actionColumn == null) {
+ table.actionColumn = {
+ tableActions: [],
+ }
+ }
+ if (table.actionColumn.tableActions == null) {
+ table.actionColumn.tableActions = []
+ }
+
+ return table
+}
+
+function buildToolBar(toolBar: Partial = {}) {
+ if (toolBar.leftTools == null) toolBar.leftTools = []
+ if (toolBar.rightTools == null) toolBar.rightTools = []
+
+ return toolBar
+}
+
+export interface PropsTplType {
+ pageLayout?: Partial
+ searchForm?: Partial>
+ toolBar?: Partial
+ table?: Partial>
+}
+
+export function buildProps({pageLayout, searchForm, toolBar, table}: PropsTplType
) {
+ return reactive({
+ pageLayout: buildPageLayout(pageLayout),
+ searchForm: buildSearchForm(searchForm),
+ toolBar: buildToolBar(toolBar),
+ table: buildTable(table),
+ })
+}
+
+interface Exposed extends Record {
+ doSearch: () => void
+}
+
+const component = defineComponent(
+ (props: ATablePageType
, {slots, expose}: SetupContext) => {
+ const formData = Utils.resetAble(reactive
({
+ ...(props.searchForm.defaultData),
+ current: 1,
+ size: 20,
+ })) as ResetAble
+ const tableData = Utils.resetAble(reactive([])) as ResetAble
+
+ const totalCount = ref(0)
+ const loading = ref(false)
+ const showHighForm = ref(true)
+
+ const doSearch = () => {
+ loading.value = true
+ if (props.searchForm.paging != null) {
+ props.searchForm.paging(formData.$clone() as P)
+ .then(res => {
+ totalCount.value = res.data?.total ?? 0
+ const records = res.data?.records ?? ([] as T[])
+ tableData.$reset(records)
+ })
+ .finally(() => {
+ loading.value = false
+ })
+ return
+ }
+ }
+ expose({doSearch})
+ const showHighFormHandle = () => {
+ showHighForm.value = !showHighForm.value
+ formData.$reset()
+ }
+ const doExport = () => {
+ if (props?.searchForm?.export == null) {
+ return
+ }
+ loading.value = true
+ saveFile(props?.searchForm?.export(formData.$clone() as P))
+ .finally(() => {
+ loading.value = false
+ })
+ }
+ const rowAction = (data: ColumnScopeType, action: TableActionType) => {
+ if (action.loading != null) {
+ action.loading = true
+ }
+ const result = action.action(data)
+ if (result instanceof Promise) {
+ result
+ .then(reload => {
+ if (reload) {
+ doSearch()
+ }
+ })
+ .finally(() => {
+ if (action.loading != null) {
+ action.loading = false
+ }
+ })
+ }
+ }
+
+ function doReset() {
+ formData.$reset()
+ doSearch()
+ }
+
+ const highFormCssParam = computed(() => {
+ return {
+ '--col-count': props.searchForm.highForm?.colCount ?? 3,
+ '--vgap': props.searchForm.highForm?.vgap ?? '0',
+ '--hgap': props.searchForm.highForm?.hgap ?? '0',
+ }
+ })
+
+ const pageCssParam = computed(() => {
+ return {
+ '--data-list-height': props.pageLayout.dataListHeight,
+ }
+ })
+ const actionColumnBtnRender = (scope: ColumnScopeType, tableAction: TableActionType) => {
+ const Btn =
+ {tableAction.label}
+
+ const tooltipTxt = tableAction.tooltip == null ? '' : (typeof tableAction.tooltip === 'function' ? tableAction.tooltip(scope) : tableAction.tooltip)
+ if (Strings.isBlank(tooltipTxt)) {
+ return (
+ {{
+ default: () => Btn,
+ }}
+ )
+ }
+ return Btn
+ }
+ const actionColumnRender = () => {
+ const actionColumn = props.table?.actionColumn
+ return Colls.isEmpty(actionColumn?.tableActions) ? <>>
+ : (
+
+ {{
+ default: (scope: ColumnScopeType) => (actionColumn?.tableActions!
+ .filter(it => (it.show == null ? true : it.show(scope)))
+ .map((tableAction, i) => (tableAction.confirm != null ?
+ ( rowAction(scope, tableAction)}>
+ {{
+ reference: () => (actionColumnBtnRender(scope, tableAction)),
+ }}
+ ) : actionColumnBtnRender(scope, tableAction)))
+ ),
+ }}
+
+ )
+ }
+ onMounted(doSearch)
+ return () => (
+ {/*@ts-ignore*/}
+
+
+ {/*@ts-ignore*/}
+
+ {
+ slots.highFormItem?.(formData)
+ }
+
+
+
+
+
+
+ {
+ withDirectives(
props.table?.treeLoad!(formData as P, row, expanded, resolve)) : undefined}
+ onExpand-change={props.table?.treeLoad ? ((row: any, expandedRows: any) => props.table?.treeLoad!(formData as P, row, expandedRows, undefined)) : undefined}
+ cell-class-name="table-cell"
+ header-row-class-name="table-header"
+ >
+ {
+ slots.columns?.()
+ }
+ {
+ actionColumnRender()
+ }
+ , [ [ ElLoading.directive, loading.value ] ])
+ }
+
(formData as G.PageParam).current = val}
+ onUpdate:page-size={(val) => (formData as G.PageParam).size = val}
+ hide-on-single-page={false}
+ page-sizes={[ 10, 20, 50, 100, 500 ]}
+ teleported={false}
+ total={totalCount.value}
+ background={true}
+ layout="total, prev, pager, next, sizes, jumper"
+ onChange={doSearch}/>
+ {
+ slots?.default?.()
+ }
+
+ )
+ },
+ {
+ name: 'ATablePage',
+ props: [ 'pageLayout', 'searchForm', 'toolBar', 'table' ],
+ },
+)
+
+export interface ATablePageInstance extends InstanceType, Exposed {
+}
+
+export default component
diff --git a/src/components/a-page/a-table-page/a-table-page.d.ts b/src/components/a-page/a-table-page/a-table-page.d.ts
new file mode 100644
index 0000000..bf5b637
--- /dev/null
+++ b/src/components/a-page/a-table-page/a-table-page.d.ts
@@ -0,0 +1,5 @@
+export {}
+declare global {
+ interface ATablePageInstance extends InstanceType, ATablePageExposed {
+ }
+}
diff --git a/src/components/a-page/a-table-page/a-table-page.module.styl b/src/components/a-page/a-table-page/a-table-page.module.styl
new file mode 100644
index 0000000..378c975
--- /dev/null
+++ b/src/components/a-page/a-table-page/a-table-page.module.styl
@@ -0,0 +1,197 @@
+.table-page {
+ grid-template-columns 1fr
+ grid-template-rows: minmax(0, 1fr) minmax(0, var(--data-list-height));
+ grid-auto-rows: minmax(0, auto);
+ gap 10px
+
+ &.folder {
+ grid-template-rows: 0 minmax(0, 3fr);
+ gap: 0;
+
+ .search-form {
+ padding 0
+ border-width 0
+ height 0
+ }
+ }
+
+ .search-form-wrapper {
+ border: 1px solid #EAEBF1;
+ padding: 20px;
+ border-radius: 8px;
+ background-color: white;
+ height 100%
+ width 100%;
+ box-sizing: border-box;
+
+ & {
+
+ }
+
+ & > div {
+ max-height 168px
+ width 100%;
+ }
+
+ .search-form {
+ display grid
+ grid-template-columns repeat(var(--col-count), 1fr)
+ row-gap var(--vgap);
+ column-gap var(--hgap);
+
+ :global(.el-form-item) {
+ margin 0
+ }
+
+ :global(.el-form-item).form-action-btn {
+ grid-column: 1 / -1;
+
+ .el-form-item__content {
+ justify-content end
+ }
+ }
+ }
+ }
+
+ .data-list {
+ box-sizing: border-box;
+ display grid
+ grid-template-columns 1fr
+ grid-template-rows 50px minmax(0, 1fr) 50px
+ border: 1px solid #EAEBF1;
+ padding: 15px 20px 20px 15px;
+ border-radius: 8px;
+ background-color: white;
+ height 100%
+ width 100%;
+ gap 20px
+
+ .tool-bar {
+ display flex
+ justify-content space-between
+ box-sizing: border-box;
+
+ .tool-bar-left {
+ flex 1
+ }
+
+ .tool-bar-right {
+ flex 1
+ display flex
+ gap 10px
+ justify-content end
+
+ :global(.el-form) {
+ display flex
+ gap 10px
+ flex-shrink 0
+
+ .el-form-item {
+ margin: 0;
+ flex 1
+ }
+ }
+
+ :global(.el-button--default) {
+ width 32px
+ color #4D5875
+ background-color #F1F4F4
+ border-color #F1F4F4
+ margin 0
+
+ &:hover {
+ color var(--main-color)
+ background-color #E6EAEB;
+ border-color #E6EAEB;
+ }
+ }
+
+
+ :global(.el-dropdown) {
+ flex-shrink 0
+ }
+
+ :global(.el-button--default).filter-btn-active {
+ color var(--main-color)
+ }
+ }
+ }
+
+ :global(.el-table) {
+ width 100%
+ height: 100%;
+
+ :global(.table-header) {
+ color #454C59
+
+ th {
+ background-color #EDF1F7
+ font-weight 500
+ position relative
+
+ & > div {
+ display flex
+ gap 5px
+ align-items center
+ }
+
+ &:not(:first-child) > div::before {
+ position: absolute;
+ top: 50%;
+ left: 1px;
+ width: 1px;
+ background-color: #D3D7DE;
+ transform: translateY(-50%);
+ content: "";
+ height 50%
+ }
+ }
+ }
+
+ :global(.table-cell) {
+ color #2F3540
+ }
+
+ .action-btn {
+ width 100%
+ display flex
+ flex-wrap wrap
+ gap: 10px;
+
+ .text-btn {
+ margin 0
+ }
+
+ .icon-btn {
+ margin 0
+ padding: 8px;
+ justify-content: center;
+ align-items: center;
+ //#ebf0ff
+ }
+
+ :global(.el-button--default).icon-btn {
+ color oklch(72% .19 231.6)
+ background-color oklch(0.96 0.03 224.26)
+ border-color oklch(0.96 0.03 224.26)
+ }
+
+ :global(.el-button--danger).icon-btn {
+ color oklch(73% .15 25.3)
+ background-color oklch(0.96 0.02 22.09)
+ border-color oklch(0.96 0.02 22.09)
+ }
+ }
+ }
+
+ :global(.el-pagination) {
+ justify-content center
+
+ :global(.btn-next),
+ :global(.btn-prev),
+ :global(.el-pager) li {
+ border-radius: 6px !important;
+ }
+ }
+ }
+}
diff --git a/src/components/page/FormPage.vue b/src/components/page/FormPage.vue
index cbffe75..bb07ab3 100644
--- a/src/components/page/FormPage.vue
+++ b/src/components/page/FormPage.vue
@@ -1,6 +1,6 @@
-
+
@@ -334,7 +334,7 @@ onMounted(doSearch)
@change="doSearch"/>
-
+