Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
V
Vue-Dashboard
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
高源
Vue-Dashboard
Commits
8516388e
Commit
8516388e
authored
Jun 12, 2025
by
User
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
添加本地代码开发示例和table组件
parent
c560ab0d
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
4184 additions
and
16 deletions
+4184
-16
table.json
public/table.json
+197
-0
zh-cn.ts
src/locales/langs/zh-cn.ts
+1
-0
main.ts
src/main.ts
+8
-0
imports.ts
src/router/elegant/imports.ts
+1
-0
routes.ts
src/router/elegant/routes.ts
+9
-0
transform.ts
src/router/elegant/transform.ts
+2
-1
components.d.ts
src/typings/components.d.ts
+22
-0
elegant-router.d.ts
src/typings/elegant-router.d.ts
+3
-0
vueComponent.vue
src/views/_builtin/iframe-page/vueComponent.vue
+8
-15
AddFieldDrawer.vue
src/views/table/codet/components/AddFieldDrawer.vue
+0
-0
ColumnSettings.vue
src/views/table/codet/components/ColumnSettings.vue
+0
-0
CreateDialog.vue
src/views/table/codet/components/CreateDialog.vue
+0
-0
DataTable.vue
src/views/table/codet/components/DataTable.vue
+0
-0
DataTransform.vue
src/views/table/codet/components/DataTransform.vue
+0
-0
SearchFieldEditDrawer.vue
src/views/table/codet/components/SearchFieldEditDrawer.vue
+0
-0
SearchFieldSettings.vue
src/views/table/codet/components/SearchFieldSettings.vue
+0
-0
SearchForm.vue
src/views/table/codet/components/SearchForm.vue
+0
-0
TableBasicSettings.vue
src/views/table/codet/components/TableBasicSettings.vue
+0
-0
TableSettingsDrawer.vue
src/views/table/codet/components/TableSettingsDrawer.vue
+0
-0
AddFieldDrawer.vue
src/views/tableComponent/codet/components/AddFieldDrawer.vue
+79
-0
ColumnEditDrawer.vue
...iews/tableComponent/codet/components/ColumnEditDrawer.vue
+199
-0
ColumnSettings.vue
src/views/tableComponent/codet/components/ColumnSettings.vue
+197
-0
CreateDialog.vue
src/views/tableComponent/codet/components/CreateDialog.vue
+101
-0
DataTable.vue
src/views/tableComponent/codet/components/DataTable.vue
+367
-0
DataTransform.vue
src/views/tableComponent/codet/components/DataTransform.vue
+273
-0
SearchFieldEditDrawer.vue
...tableComponent/codet/components/SearchFieldEditDrawer.vue
+110
-0
SearchFieldSettings.vue
...s/tableComponent/codet/components/SearchFieldSettings.vue
+153
-0
SearchForm.vue
src/views/tableComponent/codet/components/SearchForm.vue
+278
-0
TableBasicSettings.vue
...ws/tableComponent/codet/components/TableBasicSettings.vue
+122
-0
TableSettingsDrawer.vue
...s/tableComponent/codet/components/TableSettingsDrawer.vue
+366
-0
index.vue
src/views/tableComponent/index.vue
+1491
-0
table.json
table.json
+197
-0
No files found.
public/table.json
0 → 100644
View file @
8516388e
{
"Offset"
:
0
,
"Total"
:
10
,
"Results"
:
[
{
"OwnerName"
:
"222"
,
"Name"
:
"333"
,
"SerialNumber"
:
"1111"
,
"Type"
:
"Pos"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"EF1D786D-06F7-43F5-A685-8E13AE0F33BF"
,
"CreateTime"
:
"2025-05-07T14:30:22.0000000+08:00"
,
"UpdateTime"
:
"2025-05-13T16:59:38.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"xxx"
,
"Name"
:
"xxx"
,
"SerialNumber"
:
"xxxx"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"USD"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"66B88D4E-A1B9-4E3C-922B-DD5211BA3FB5"
,
"CreateTime"
:
"2025-05-07T14:26:34.0000000+08:00"
,
"UpdateTime"
:
"2025-05-13T16:57:05.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"1111"
,
"Name"
:
"222"
,
"SerialNumber"
:
"333"
,
"Type"
:
"Pos"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"97F8DA3B-06AC-4C50-ABEB-430F58B3DD64"
,
"CreateTime"
:
"2025-05-07T14:30:08.0000000+08:00"
,
"UpdateTime"
:
"2025-05-07T14:30:08.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"11"
,
"Name"
:
"222"
,
"SerialNumber"
:
"3"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"USD"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"CC255F3E-9F80-4002-B68C-53C5FF86AE52"
,
"CreateTime"
:
"2025-05-07T14:29:49.0000000+08:00"
,
"UpdateTime"
:
"2025-05-07T14:29:49.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"22"
,
"Name"
:
"222"
,
"SerialNumber"
:
"33"
,
"Type"
:
"AliPay"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"99C366A3-5867-43D5-B22D-B71E970D9A3C"
,
"CreateTime"
:
"2025-05-07T14:29:33.0000000+08:00"
,
"UpdateTime"
:
"2025-05-07T14:29:33.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"xxx"
,
"Name"
:
"111"
,
"SerialNumber"
:
"2222"
,
"Type"
:
"Bank"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"F2475ECC-55BD-49FE-BC00-8822DC846739"
,
"CreateTime"
:
"2025-05-07T14:29:00.0000000+08:00"
,
"UpdateTime"
:
"2025-05-07T14:29:00.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"7275275"
,
"Name"
:
"4274272"
,
"SerialNumber"
:
"41042277"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"0C38277C-7BA6-4E9D-BEF5-D43DB49B5EA1"
,
"CreateTime"
:
"2025-03-27T14:47:54.0000000+08:00"
,
"UpdateTime"
:
"2025-03-27T14:47:54.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"12312322"
,
"Name"
:
"32131"
,
"SerialNumber"
:
"2231123"
,
"Type"
:
"Pos"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"2A42BDFE-CDED-4300-ADF3-020D1B3E0138"
,
"CreateTime"
:
"2025-03-27T14:33:13.0000000+08:00"
,
"UpdateTime"
:
"2025-03-27T14:33:13.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"123123"
,
"Name"
:
"321321"
,
"SerialNumber"
:
"123132312312"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"095872A0-513C-4834-A0C5-BA173F5189A4"
,
"CreateTime"
:
"2025-03-27T14:29:36.0000000+08:00"
,
"UpdateTime"
:
"2025-03-27T14:29:36.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"12"
,
"Name"
:
"1212"
,
"SerialNumber"
:
"12121211"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"40D1968B-70A1-49D5-89FD-39943A080D0A"
,
"CreateTime"
:
"2025-03-27T14:17:12.0000000+08:00"
,
"UpdateTime"
:
"2025-03-27T14:17:12.0000000+08:00"
,
"Status"
:
0
}
]
}
\ No newline at end of file
src/locales/langs/zh-cn.ts
View file @
8516388e
...
...
@@ -156,6 +156,7 @@ const local: App.I18n.Schema = {
'iframe-page'
:
'外链页面'
,
home
:
'首页'
,
chat
:
'AI助手'
,
tablecomponent
:
'表格组件'
,
exception
:
'异常页'
,
exception_403
:
'403'
,
exception_404
:
'404'
,
...
...
src/main.ts
View file @
8516388e
...
...
@@ -4,6 +4,9 @@ import '@wangeditor/editor/dist/css/style.css';
import
VueGridLayout
from
'vue-grid-layout'
;
import
MateChat
from
'@matechat/core'
;
import
'@devui-design/icons/icomoon/devui-icon.css'
;
import
axios
from
'axios'
;
import
*
as
echarts
from
'echarts/core'
;
import
WangEditor
from
'wangeditor'
;
import
'./plugins/assets'
;
import
{
localStg
}
from
'@/utils/storage'
;
// main.js or main.ts
...
...
@@ -62,6 +65,11 @@ async function setupApp() {
setupIconifyOffline
();
setupDayjs
();
// 全局注册工具库到 window 对象,供外部组件使用
(
window
as
any
).
$axios
=
axios
;
(
window
as
any
).
$echarts
=
echarts
;
(
window
as
any
).
$WangEditor
=
WangEditor
;
const
app
=
createApp
(
App
);
setupStore
(
app
);
await
setupRouter
(
app
);
...
...
src/router/elegant/imports.ts
View file @
8516388e
...
...
@@ -22,4 +22,5 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
login
:
()
=>
import
(
"@/views/_builtin/login/index.vue"
),
chat
:
()
=>
import
(
"@/views/chat/index.vue"
),
home
:
()
=>
import
(
"@/views/home/index.vue"
),
tablecomponent
:
()
=>
import
(
"@/views/tableComponent/index.vue"
),
};
src/router/elegant/routes.ts
View file @
8516388e
...
...
@@ -84,5 +84,14 @@ export const generatedRoutes: GeneratedRoute[] = [
constant
:
true
,
hideInMenu
:
true
}
},
{
name
:
'tablecomponent'
,
path
:
'/tablecomponent'
,
component
:
'layout.base$view.tablecomponent'
,
meta
:
{
title
:
'tablecomponent'
,
i18nKey
:
'route.tablecomponent'
}
}
];
src/router/elegant/transform.ts
View file @
8516388e
...
...
@@ -182,7 +182,8 @@ const routeMap: RouteMap = {
"chat"
:
"/chat"
,
"home"
:
"/home"
,
"iframe-page"
:
"/iframe-page/:url"
,
"login"
:
"/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"
"login"
:
"/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"
,
"tablecomponent"
:
"/tablecomponent"
};
/**
...
...
src/typings/components.d.ts
View file @
8516388e
...
...
@@ -7,11 +7,17 @@ export {}
/* prettier-ignore */
declare
module
'vue'
{
export
interface
GlobalComponents
{
AddFieldDrawer
:
typeof
import
(
'./../components/AddFieldDrawer.vue'
)[
'default'
]
AppProvider
:
typeof
import
(
'./../components/common/app-provider.vue'
)[
'default'
]
BetterScroll
:
typeof
import
(
'./../components/custom/better-scroll.vue'
)[
'default'
]
ButtonIcon
:
typeof
import
(
'./../components/custom/button-icon.vue'
)[
'default'
]
ColumnEditDrawer
:
typeof
import
(
'../views/table/codet/components/ColumnEditDrawer.vue'
)[
'default'
]
ColumnSettings
:
typeof
import
(
'../views/table/codet/components/ColumnSettings.vue'
)[
'default'
]
CountTo
:
typeof
import
(
'./../components/custom/count-to.vue'
)[
'default'
]
CreateDialog
:
typeof
import
(
'../views/table/codet/components/CreateDialog.vue'
)[
'default'
]
DarkModeContainer
:
typeof
import
(
'./../components/common/dark-mode-container.vue'
)[
'default'
]
DataTable
:
typeof
import
(
'../views/table/codet/components/DataTable.vue'
)[
'default'
]
DataTransform
:
typeof
import
(
'../views/table/codet/components/DataTransform.vue'
)[
'default'
]
ExceptionBase
:
typeof
import
(
'./../components/common/exception-base.vue'
)[
'default'
]
FullScreen
:
typeof
import
(
'./../components/common/full-screen.vue'
)[
'default'
]
IconAntDesignEnterOutlined
:
typeof
import
(
'~icons/ant-design/enter-outlined'
)[
'default'
]
...
...
@@ -27,12 +33,16 @@ declare module 'vue' {
LangSwitch
:
typeof
import
(
'./../components/common/lang-switch.vue'
)[
'default'
]
LookForward
:
typeof
import
(
'./../components/custom/look-forward.vue'
)[
'default'
]
MenuToggler
:
typeof
import
(
'./../components/common/menu-toggler.vue'
)[
'default'
]
NAlert
:
typeof
import
(
'naive-ui'
)[
'NAlert'
]
NBreadcrumb
:
typeof
import
(
'naive-ui'
)[
'NBreadcrumb'
]
NBreadcrumbItem
:
typeof
import
(
'naive-ui'
)[
'NBreadcrumbItem'
]
NButton
:
typeof
import
(
'naive-ui'
)[
'NButton'
]
NCard
:
typeof
import
(
'naive-ui'
)[
'NCard'
]
NCheckbox
:
typeof
import
(
'naive-ui'
)[
'NCheckbox'
]
NCheckboxGroup
:
typeof
import
(
'naive-ui'
)[
'NCheckboxGroup'
]
NColorPicker
:
typeof
import
(
'naive-ui'
)[
'NColorPicker'
]
NDataTable
:
typeof
import
(
'naive-ui'
)[
'NDataTable'
]
NDatePicker
:
typeof
import
(
'naive-ui'
)[
'NDatePicker'
]
NDialogProvider
:
typeof
import
(
'naive-ui'
)[
'NDialogProvider'
]
NDivider
:
typeof
import
(
'naive-ui'
)[
'NDivider'
]
NDrawer
:
typeof
import
(
'naive-ui'
)[
'NDrawer'
]
...
...
@@ -43,6 +53,8 @@ declare module 'vue' {
NFormItem
:
typeof
import
(
'naive-ui'
)[
'NFormItem'
]
NGi
:
typeof
import
(
'naive-ui'
)[
'NGi'
]
NGrid
:
typeof
import
(
'naive-ui'
)[
'NGrid'
]
NGridItem
:
typeof
import
(
'naive-ui'
)[
'NGridItem'
]
NIcon
:
typeof
import
(
'naive-ui'
)[
'NIcon'
]
NInput
:
typeof
import
(
'naive-ui'
)[
'NInput'
]
NInputGroup
:
typeof
import
(
'naive-ui'
)[
'NInputGroup'
]
NInputNumber
:
typeof
import
(
'naive-ui'
)[
'NInputNumber'
]
...
...
@@ -51,24 +63,34 @@ declare module 'vue' {
NMessageProvider
:
typeof
import
(
'naive-ui'
)[
'NMessageProvider'
]
NModal
:
typeof
import
(
'naive-ui'
)[
'NModal'
]
NNotificationProvider
:
typeof
import
(
'naive-ui'
)[
'NNotificationProvider'
]
NRadioButton
:
typeof
import
(
'naive-ui'
)[
'NRadioButton'
]
NRadioGroup
:
typeof
import
(
'naive-ui'
)[
'NRadioGroup'
]
NScrollbar
:
typeof
import
(
'naive-ui'
)[
'NScrollbar'
]
NSelect
:
typeof
import
(
'naive-ui'
)[
'NSelect'
]
NSpace
:
typeof
import
(
'naive-ui'
)[
'NSpace'
]
NSpin
:
typeof
import
(
'naive-ui'
)[
'NSpin'
]
NStatistic
:
typeof
import
(
'naive-ui'
)[
'NStatistic'
]
NSwitch
:
typeof
import
(
'naive-ui'
)[
'NSwitch'
]
NTab
:
typeof
import
(
'naive-ui'
)[
'NTab'
]
NTabPane
:
typeof
import
(
'naive-ui'
)[
'NTabPane'
]
NTabs
:
typeof
import
(
'naive-ui'
)[
'NTabs'
]
NText
:
typeof
import
(
'naive-ui'
)[
'NText'
]
NTooltip
:
typeof
import
(
'naive-ui'
)[
'NTooltip'
]
NWatermark
:
typeof
import
(
'naive-ui'
)[
'NWatermark'
]
PinToggler
:
typeof
import
(
'./../components/common/pin-toggler.vue'
)[
'default'
]
ReloadButton
:
typeof
import
(
'./../components/common/reload-button.vue'
)[
'default'
]
RouterLink
:
typeof
import
(
'vue-router'
)[
'RouterLink'
]
RouterView
:
typeof
import
(
'vue-router'
)[
'RouterView'
]
SearchFieldEditDrawer
:
typeof
import
(
'../views/table/codet/components/SearchFieldEditDrawer.vue'
)[
'default'
]
SearchFieldSettings
:
typeof
import
(
'../views/table/codet/components/SearchFieldSettings.vue'
)[
'default'
]
SearchForm
:
typeof
import
(
'../views/table/codet/components/SearchForm.vue'
)[
'default'
]
SoybeanAvatar
:
typeof
import
(
'./../components/custom/soybean-avatar.vue'
)[
'default'
]
SvgIcon
:
typeof
import
(
'./../components/custom/svg-icon.vue'
)[
'default'
]
SystemLogo
:
typeof
import
(
'./../components/common/system-logo.vue'
)[
'default'
]
TableBasicSettings
:
typeof
import
(
'../views/table/codet/components/TableBasicSettings.vue'
)[
'default'
]
TableColumnSetting
:
typeof
import
(
'./../components/advanced/table-column-setting.vue'
)[
'default'
]
TableHeaderOperation
:
typeof
import
(
'./../components/advanced/table-header-operation.vue'
)[
'default'
]
TableSettingsDrawer
:
typeof
import
(
'../views/table/codet/components/TableSettingsDrawer.vue'
)[
'default'
]
ThemeSchemaSwitch
:
typeof
import
(
'./../components/common/theme-schema-switch.vue'
)[
'default'
]
WaveBg
:
typeof
import
(
'./../components/custom/wave-bg.vue'
)[
'default'
]
}
...
...
src/typings/elegant-router.d.ts
View file @
8516388e
...
...
@@ -37,6 +37,7 @@ declare module "@elegant-router/types" {
"home"
:
"/home"
;
"iframe-page"
:
"/iframe-page/:url"
;
"login"
:
"/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"
;
"tablecomponent"
:
"/tablecomponent"
;
};
/**
...
...
@@ -88,6 +89,7 @@ declare module "@elegant-router/types" {
|
"home"
|
"iframe-page"
|
"login"
|
"tablecomponent"
>
;
/**
...
...
@@ -113,6 +115,7 @@ declare module "@elegant-router/types" {
|
"login"
|
"chat"
|
"home"
|
"tablecomponent"
>
;
/**
...
...
src/views/_builtin/iframe-page/vueComponent.vue
View file @
8516388e
...
...
@@ -11,9 +11,7 @@ import {
}
from
'vue'
;
import
{
NConfigProvider
,
darkTheme
}
from
'naive-ui'
;
import
*
as
naive
from
'naive-ui'
;
import
axios
from
'axios'
;
import
*
as
echarts
from
'echarts/core'
;
import
WangEditor
from
'wangeditor'
;
// 这些库已在 main.ts 中全局注册,这里不需要重复导入
import
{
useThemeStore
}
from
'@/store/modules/theme'
;
// 引入echarts相关
import
{
useEcharts
}
from
'@/hooks/common/echarts'
;
...
...
@@ -44,23 +42,18 @@ Object.keys(naive).forEach(key => {
// 添加类型声明
declare
global
{
interface
Window
{
$axios
:
typeof
axios
;
$echarts
:
typeof
echarts
;
$axios
:
any
;
$echarts
:
any
;
$useEcharts
:
typeof
useEcharts
;
$themeStore
:
typeof
themeStore
;
$WangEditor
:
typeof
WangEditor
;
$WangEditor
:
any
;
}
}
// 全局注入axios
(
window
as
any
).
$axios
=
axios
;
// 全局注入echarts和useEcharts
(
window
as
any
).
$echarts
=
echarts
;
// axios, echarts, WangEditor 已在 main.ts 中全局注册
// 这里只需要注册组件特有的工具
(
window
as
any
).
$useEcharts
=
useEcharts
;
// 全局注入主题存储
(
window
as
any
).
$themeStore
=
themeStore
;
// 全局注入WangEditor
(
window
as
any
).
$WangEditor
=
WangEditor
;
// 清理函数 - 完全销毁Vue组件
function
cleanup
()
{
...
...
@@ -75,8 +68,8 @@ function cleanup() {
}
// 清理编辑器实例
const
editor
=
(
window
as
any
).
$WangEditor
?.
getInstance
();
if
(
editor
)
{
const
editor
=
(
window
as
any
).
$WangEditor
?.
getInstance
?.
();
if
(
editor
&&
typeof
editor
.
destroy
===
'function'
)
{
editor
.
destroy
();
}
}
catch
(
e
)
{
...
...
src/views/table/codet/components/AddFieldDrawer.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/ColumnSettings.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/CreateDialog.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/DataTable.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/DataTransform.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/SearchFieldEditDrawer.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/SearchFieldSettings.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/SearchForm.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/TableBasicSettings.vue
0 → 100644
View file @
8516388e
src/views/table/codet/components/TableSettingsDrawer.vue
0 → 100644
View file @
8516388e
src/views/tableComponent/codet/components/AddFieldDrawer.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
;
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
const
{
showAddFieldDrawer
,
selectedColumns
,
availableColumns
}
=
tableState
;
const
{
confirmAddFields
}
=
tableMethods
;
defineOptions
({
name
:
'AddFieldDrawer'
});
</
script
>
<
template
>
<NDrawer
v-model:show=
"showAddFieldDrawer"
:width=
"400"
placement=
"right"
>
<NDrawerContent
title=
"添加搜索字段"
>
<div
class=
"add-field-content"
>
<NAlert
type=
"info"
:show-icon=
"true"
class=
"mb-4"
>
请选择要添加为搜索条件的字段
</NAlert>
<div
class=
"field-select-list"
>
<NScrollbar
style=
"max-height: calc(100vh - 250px)"
>
<NCheckboxGroup
v-model:value=
"selectedColumns"
>
<div
v-for=
"column in availableColumns"
:key=
"column.key"
class=
"field-select-item"
>
<NCheckbox
:value=
"column.key"
>
<span
class=
"field-select-label"
>
{{
column
.
title
}}
</span>
<span
class=
"field-select-key"
>
(
{{
column
.
key
}}
)
</span>
</NCheckbox>
</div>
</NCheckboxGroup>
</NScrollbar>
</div>
</div>
<template
#
footer
>
<NSpace
justify=
"end"
>
<NButton
@
click=
"showAddFieldDrawer = false"
>
取消
</NButton>
<NButton
type=
"primary"
:disabled=
"selectedColumns.length === 0"
@
click=
"confirmAddFields"
>
确定
</NButton>
</NSpace>
</
template
>
</NDrawerContent>
</NDrawer>
</template>
<
style
scoped
>
.add-field-content
{
padding
:
16px
;
}
.field-select-list
{
margin-top
:
16px
;
}
.field-select-item
{
padding
:
8px
0
;
border-bottom
:
1px
solid
var
(
--n-border-color
);
}
.field-select-item
:last-child
{
border-bottom
:
none
;
}
.field-select-label
{
font-size
:
14px
;
color
:
var
(
--n-text-color
);
}
.field-select-key
{
font-size
:
12px
;
color
:
var
(
--n-text-color-3
);
margin-left
:
8px
;
}
.mb-4
{
margin-bottom
:
16px
;
}
</
style
>
src/views/tableComponent/codet/components/ColumnEditDrawer.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
;
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
const
{
showEditDrawer
,
currentEditColumn
,
isIndexColumn
,
newFilterLabel
,
newFilterValue
}
=
tableState
;
const
{
saveColumnEdit
,
addFilterOption
,
removeFilterOption
,
generateFilterOptions
}
=
tableMethods
;
defineOptions
({
name
:
'ColumnEditDrawer'
});
</
script
>
<
template
>
<NDrawer
v-model:show=
"showEditDrawer"
:width=
"400"
placement=
"right"
>
<NDrawerContent
title=
"编辑列"
>
<NForm
label-placement=
"left"
label-width=
"100"
label-align=
"left"
>
<NFormItem
label=
"显示名称"
>
<NInput
:value=
"currentEditColumn?.title"
:disabled=
"!currentEditColumn"
@
update:value=
"val => currentEditColumn && (currentEditColumn.title = val)"
></NInput>
</NFormItem>
<NFormItem
label=
"字段名"
>
<NInput
:value=
"currentEditColumn?.key"
disabled
></NInput>
</NFormItem>
<NFormItem
label=
"表头对齐"
>
<NRadioGroup
:value=
"currentEditColumn?.titleAlign"
@
update:value=
"val => currentEditColumn && (currentEditColumn.titleAlign = val)"
>
<NRadioButton
value=
"left"
>
左对齐
</NRadioButton>
<NRadioButton
value=
"center"
>
居中
</NRadioButton>
<NRadioButton
value=
"right"
>
右对齐
</NRadioButton>
</NRadioGroup>
</NFormItem>
<NFormItem
label=
"内容对齐"
>
<NRadioGroup
:value=
"currentEditColumn?.align"
@
update:value=
"val => currentEditColumn && (currentEditColumn.align = val)"
>
<NRadioButton
value=
"left"
>
左对齐
</NRadioButton>
<NRadioButton
value=
"center"
>
居中
</NRadioButton>
<NRadioButton
value=
"right"
>
右对齐
</NRadioButton>
</NRadioGroup>
</NFormItem>
<!-- 只在非 index 字段时显示排序开关 -->
<NFormItem
v-if=
"!isIndexColumn"
label=
"开启排序"
>
<NSwitch
:value=
"currentEditColumn?.sortable"
@
update:value=
"val => currentEditColumn && (currentEditColumn.sortable = val)"
></NSwitch>
</NFormItem>
<!-- 添加筛选功能相关配置 -->
<NFormItem
v-if=
"!isIndexColumn"
label=
"开启筛选"
>
<NSwitch
:value=
"!!currentEditColumn?.filter"
@
update:value=
"
val =>
{
if (currentEditColumn) {
if (val) {
// 启用筛选时,初始化筛选相关属性
currentEditColumn.filter = true;
currentEditColumn.filterOptions = currentEditColumn.filterOptions || [];
} else {
// 禁用筛选时,清除筛选相关属性
delete currentEditColumn.filter;
delete currentEditColumn.filterOptions;
}
}
}
"
>
</NSwitch>
</NFormItem>
<!-- 筛选选项管理,仅在开启筛选时显示 -->
<template
v-if=
"currentEditColumn?.filter"
>
<NDivider>
筛选选项管理
</NDivider>
<NFormItem
label=
"自动获取选项"
>
<NButton
size=
"small"
@
click=
"generateFilterOptions(currentEditColumn)"
>
从当前数据生成选项
</NButton>
</NFormItem>
<!-- 添加新筛选选项 -->
<NSpace
vertical
>
<NInputGroup>
<NInput
v-model:value=
"newFilterLabel"
placeholder=
"显示文本"
></NInput>
<NInput
v-model:value=
"newFilterValue"
placeholder=
"筛选值"
></NInput>
<NButton
type=
"primary"
:disabled=
"!newFilterLabel || !newFilterValue"
@
click=
"addFilterOption"
>
添加
</NButton>
</NInputGroup>
</NSpace>
<!-- 筛选选项列表 -->
<NSpace
vertical
style=
"margin-top: 12px; max-height: 200px; overflow-y: auto"
>
<div
v-for=
"(option, index) in currentEditColumn.filterOptions"
:key=
"index"
style=
"display: flex; justify-content: space-between; align-items: center; padding: 4px 0"
>
<span>
{{
option
.
label
}}
</span>
<NButton
text
type=
"error"
@
click=
"removeFilterOption(index)"
>
<template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 24 24"
>
<path
d=
"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"
fill=
"currentColor"
/>
</svg>
</NIcon>
</
template
>
</NButton>
</div>
</NSpace>
</template>
<!-- 添加 ellipsis 开关 -->
<NFormItem
v-if=
"!isIndexColumn"
label=
"文本省略"
>
<NSwitch
:value=
"!!currentEditColumn?.ellipsis"
@
update:value=
"
val => {
if (currentEditColumn) {
if (val) {
currentEditColumn.ellipsis = { tooltip: true };
} else {
delete currentEditColumn.ellipsis;
}
}
}
"
></NSwitch>
</NFormItem>
<!-- 只在启用 ellipsis 时显示 tooltip 开关 -->
<NFormItem
v-if=
"currentEditColumn?.ellipsis"
label=
"显示tooltip"
>
<NSwitch
:value=
"currentEditColumn?.ellipsis?.tooltip"
@
update:value=
"
val => {
if (currentEditColumn?.ellipsis) {
currentEditColumn.ellipsis.tooltip = val;
}
}
"
></NSwitch>
</NFormItem>
</NForm>
<
template
#
footer
>
<NSpace
justify=
"end"
>
<NButton
@
click=
"showEditDrawer = false"
>
取消
</NButton>
<NButton
type=
"primary"
:disabled=
"!currentEditColumn"
@
click=
"saveColumnEdit"
>
保存
</NButton>
</NSpace>
</
template
>
</NDrawerContent>
</NDrawer>
</template>
<
style
scoped
>
:deep
(
.n-drawer
.n-form-item
)
{
margin-bottom
:
16px
;
}
:deep
(
.n-drawer
.n-radio-group
)
{
width
:
100%
;
display
:
flex
;
gap
:
8px
;
}
:deep
(
.n-drawer
.n-radio-button
)
{
flex
:
1
;
text-align
:
center
;
}
.n-input-group
{
display
:
flex
;
gap
:
8px
;
}
.n-input-group
.n-input
{
flex
:
1
;
}
.n-divider
{
margin
:
16px
0
;
}
</
style
>
src/views/tableComponent/codet/components/ColumnSettings.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
;
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
const
{
filteredColumns
,
visibleColumns
,
columnSearchText
}
=
tableState
;
const
{
handleColumnVisibleChange
,
clearColumnSearch
,
handleEditColumn
,
handleDragStart
,
handleDragOver
,
handleDrop
,
handleSetSettings
}
=
tableMethods
;
// 添加拖拽完成后的保存处理
const
handleDragEnd
=
()
=>
{
// 触发配置保存
if
(
handleSetSettings
)
{
handleSetSettings
();
}
};
defineOptions
({
name
:
'ColumnSettings'
});
</
script
>
<
template
>
<div>
<div
class=
"columns-header"
>
<span>
显示字段
</span>
<span>
显示
{{
visibleColumns
.
length
}}
列
</span>
</div>
<div
class=
"columns-search"
>
<NInput
v-model:value=
"columnSearchText"
placeholder=
"搜索列"
clearable
@
clear=
"clearColumnSearch"
>
<template
#
prefix
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 24 24"
>
<path
d=
"M15.5 14h-.79l-.28-.27a6.5 6.5 0 1 0-.7.7l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0A4.5 4.5 0 1 1 14 9.5A4.5 4.5 0 0 1 9.5 14z"
fill=
"currentColor"
/>
</svg>
</NIcon>
</
template
>
</NInput>
</div>
<div
class=
"columns-list"
>
<NScrollbar
style=
"max-height: calc(100vh - 200px)"
>
<div
class=
"columns-container"
>
<div
v-for=
"(column, index) in filteredColumns"
:key=
"column.key"
class=
"column-item"
draggable=
"true"
@
dragstart=
"handleDragStart($event, index)"
@
dragover=
"handleDragOver"
@
drop=
"handleDrop($event, index)"
@
dragend=
"handleDragEnd"
>
<div
class=
"column-item-left"
>
<NIcon
class=
"column-drag-handle"
>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 24 24"
>
<path
d=
"M8 18h8v-2H8v2zm0-4h8v-2H8v2zm0-4h8V8H8v2zm0-4h8V4H8v2z"
fill=
"currentColor"
/>
</svg>
</NIcon>
<NSwitch
:value=
"visibleColumns.includes(column.key)"
size=
"small"
@
update:value=
"checked => handleColumnVisibleChange(checked, column)"
></NSwitch>
</div>
<div
class=
"column-item-content"
>
<span
class=
"column-title"
>
{{ column.title }}
</span>
<span
class=
"column-key"
>
({{ column.key }})
</span>
</div>
<div
class=
"column-item-right"
>
<NButton
text
@
click=
"handleEditColumn(column)"
>
<
template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 24 24"
>
<g
fill=
"none"
>
<path
d=
"M3 17.75A3.25 3.25 0 0 0 6.25 21h4.915l.356-1.423l.02-.077H6.25a1.75 1.75 0 0 1-1.75-1.75V11h3.25l.184-.005A3.25 3.25 0 0 0 11 7.75V4.5h6.75c.966 0 1.75.784 1.75 1.75v4.982c.479-.19.994-.263 1.5-.22V6.25A3.25 3.25 0 0 0 17.75 3h-6.879a2.25 2.25 0 0 0-1.59.659L3.658 9.28A2.25 2.25 0 0 0 3 10.871v6.879zM7.75 9.5H5.561L9.5 5.561V7.75l-.006.144A1.75 1.75 0 0 1 7.75 9.5zm11.35 3.17l-5.903 5.902a2.686 2.686 0 0 0-.706 1.247l-.458 1.831a1.087 1.087 0 0 0 1.319 1.318l1.83-.457a2.685 2.685 0 0 0 1.248-.707l5.902-5.902A2.286 2.286 0 0 0 19.1 12.67z"
fill=
"currentColor"
></path>
</g>
</svg>
</NIcon>
</
template
>
</NButton>
</div>
</div>
</div>
</NScrollbar>
</div>
</div>
</template>
<
style
scoped
>
.columns-header
{
padding
:
0
16px
12px
;
margin
:
0
;
flex-shrink
:
0
;
display
:
flex
;
justify-content
:
space-between
;
}
.columns-search
{
padding
:
0
16px
12px
;
flex-shrink
:
0
;
}
.columns-list
{
flex
:
1
;
overflow
:
hidden
;
}
.columns-container
{
display
:
flex
;
flex-direction
:
column
;
}
.column-item
{
padding
:
8px
16px
;
min-height
:
40px
;
box-sizing
:
border-box
;
display
:
flex
;
border-bottom
:
1px
solid
var
(
--n-border-color
);
align-items
:
center
;
}
.column-item-left
{
display
:
flex
;
align-items
:
center
;
gap
:
12px
;
}
.column-drag-handle
{
cursor
:
move
;
color
:
var
(
--n-text-color-3
);
}
.column-item-content
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
flex
:
1
;
margin-left
:
12px
;
}
.column-title
{
font-size
:
12px
;
color
:
var
(
--n-text-color
);
}
.column-key
{
font-size
:
12px
;
color
:
var
(
--n-text-color-3
);
}
.column-item-right
{
opacity
:
0
;
transition
:
opacity
0.3s
;
margin-left
:
auto
;
}
.column-item
:hover
.column-item-right
{
opacity
:
1
;
}
.columns-search
:deep
(
.n-input
)
{
--n-height
:
32px
;
}
.columns-search
:deep
(
.n-input-wrapper
)
{
background-color
:
var
(
--n-card-color
);
}
.columns-search
:deep
(
.n-input__prefix
)
{
margin-right
:
8px
;
}
.columns-search
:deep
(
.n-icon
)
{
font-size
:
16px
;
color
:
var
(
--n-text-color-3
);
}
</
style
>
src/views/tableComponent/codet/components/CreateDialog.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
defineAsyncComponent
,
inject
,
ref
}
from
'vue'
;
// 从主组件注入状态
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
const
{
uiConfig
}
=
tableState
;
const
showDialog
=
ref
(
false
);
// 添加当前编辑的行数据
const
currentRow
=
ref
<
any
>
(
null
);
const
isEditMode
=
ref
(
false
);
// 动态加载组件
const
DynamicComponent
=
defineAsyncComponent
(()
=>
import
(
uiConfig
.
CreateVueUrl
));
// 处理子组件关闭事件
const
handleClose
=
()
=>
{
// console.log('关闭弹窗');
showDialog
.
value
=
false
;
// 重置状态
currentRow
.
value
=
null
;
isEditMode
.
value
=
false
;
// 刷新表格数据
tableMethods
.
fetchData
();
};
// 处理子组件提交事件
const
handleSubmit
=
()
=>
{
// console.log('提交表单');
showDialog
.
value
=
false
;
// 重置状态
currentRow
.
value
=
null
;
isEditMode
.
value
=
false
;
// 刷新表格数据
tableMethods
.
fetchData
();
};
// 打开弹窗的方法
const
open
=
(
row
?:
any
)
=>
{
// 如果传入了row参数,则表示是编辑模式
if
(
row
)
{
currentRow
.
value
=
row
;
isEditMode
.
value
=
true
;
// console.log('打开编辑模式', row);
}
else
{
currentRow
.
value
=
null
;
isEditMode
.
value
=
false
;
// console.log('打开新建模式');
}
showDialog
.
value
=
true
;
};
// 暴露方法给父组件
defineExpose
({
open
});
defineOptions
({
name
:
'CreateDialog'
});
</
script
>
<
template
>
<NModal
:show=
"showDialog"
preset=
"card"
style=
"width: 90%; max-width: 1200px"
:mask-closable=
"false"
@
update:show=
"showDialog = $event"
>
<template
#
header
>
<div
class=
"dialog-header"
>
<span>
{{
isEditMode
?
'编辑'
:
'新建'
}}
</span>
</div>
</
template
>
<div
class=
"dialog-content"
>
<DynamicComponent
v-if=
"showDialog"
:edit-data=
"currentRow"
:is-edit=
"isEditMode"
@
close=
"handleClose"
@
submit=
"handleSubmit"
/>
</div>
</NModal>
</template>
<
style
scoped
>
.dialog-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
width
:
100%
;
}
.dialog-content
{
width
:
100%
;
height
:
100%
;
}
</
style
>
src/views/tableComponent/codet/components/DataTable.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
computed
,
h
,
inject
,
onMounted
,
watch
}
from
'vue'
;
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
// 通过window对象使用axios和dialog
const
axios
=
(
window
as
any
).
$axios
;
const
dialog
=
(
window
as
any
).
$dialog
;
const
message
=
(
window
as
any
).
$message
;
// 获取主题状态
const
themeStore
=
(
window
as
any
).
$themeStore
;
const
isDarkMode
=
computed
(()
=>
themeStore
?.
darkMode
||
false
);
const
borderColor
=
computed
(()
=>
(
isDarkMode
.
value
?
'#303030'
:
'#ffffff'
));
const
{
tableData
,
columns
,
loading
,
pagination
,
tableSize
,
showBorder
,
showStripe
,
tableHeight
,
tableScrollWidth
,
uiConfig
}
=
tableState
;
const
{
handlePageChange
,
handlePageSizeChange
,
handleSorterChange
,
handleFiltersChange
,
fetchData
,
handleCreate
}
=
tableMethods
;
// const handleDeleteRefresh = tableMethods.handleDeleteRefresh;
// 监听数据变化
watch
(
()
=>
tableData
.
value
,
()
=>
{
// console.log('表格数据已更新');
},
{
deep
:
true
}
);
// 组件挂载后检查数据
onMounted
(()
=>
{
// 初始化完成
});
// 处理列配置,为表格列添加标签渲染支持
const
processedColumns
=
computed
(()
=>
{
// 处理原始列配置
const
baseColumns
=
columns
.
value
.
map
((
col
:
any
)
=>
{
// 如果已经有自定义render函数则保留
if
(
col
.
render
)
return
col
;
// 添加通用的render函数检查是否是标签类型
const
newCol
=
{
...
col
};
// 为列添加渲染函数
newCol
.
render
=
(
row
:
any
)
=>
{
const
value
=
row
[
col
.
key
];
// 检查是否是标签类型
// eslint-disable-next-line no-underscore-dangle
if
(
value
&&
typeof
value
===
'object'
&&
(
value
as
any
).
__isTag
)
{
// console.log(`渲染标签:列=${col.key}, 值=${value.value}, 标签=${value.label}, 颜色=${value.color}`);
return
h
(
'div'
,
{
class
:
'tag-container'
},
[
h
(
'div'
,
{
class
:
`table-tag
${
value
.
color
}
`
,
style
:
{
display
:
'inline-block'
,
padding
:
'0 7px'
,
fontSize
:
'12px'
,
lineHeight
:
'20px'
,
borderRadius
:
'3px'
,
margin
:
0
,
backgroundColor
:
getTagColor
(
value
.
color
),
color
:
'#fff'
}
},
value
.
label
)
]);
}
// 普通值直接返回
return
value
;
};
return
newCol
;
});
// 添加操作列
const
actionColumn
=
{
title
:
'操作'
,
key
:
'actions'
,
fixed
:
'right'
,
width
:
120
,
render
:
(
row
:
any
)
=>
{
return
h
(
'div'
,
{
style
:
`display: flex; align-items: center; justify-content: center; gap: 4px; background-color:
${
borderColor
.
value
}
; padding: 4px; flex-direction: row; flex-wrap: nowrap; width: 70px;`
},
[
// 编辑按钮 - 使用用户提供的 SVG 图标
h
(
'div'
,
{
style
:
'display: inline-flex; align-items: center; justify-content: center; cursor: pointer; margin-right: 8px; transition: transform 0.2s ease; flex-shrink: 0;'
,
onClick
:
()
=>
{
console
.
log
(
'编辑'
,
row
);
// 使用handleCreate方法处理编辑操作
if
(
handleCreate
)
{
handleCreate
(
row
);
}
else
{
console
.
warn
(
'handleCreate方法未定义'
);
}
}
},
h
(
'svg'
,
{
xmlns
:
'http://www.w3.org/2000/svg'
,
width
:
'16'
,
height
:
'16'
,
viewBox
:
'0 0 32 32'
,
fill
:
'none'
,
stroke
:
'currentColor'
},
[
h
(
'path'
,
{
d
:
'M2 26h28v2H2z'
,
fill
:
'currentColor'
}),
h
(
'path'
,
{
d
:
'M25.4 9c.8-.8.8-2 0-2.8l-3.6-3.6c-.8-.8-2-.8-2.8 0l-15 15V24h6.4l15-15zm-5-5L24 7.6l-3 3L17.4 7l3-3zM6 22v-3.6l10-10l3.6 3.6l-10 10H6z'
,
fill
:
'currentColor'
})
]
)
),
// 删除按钮 - 使用 SVG 图标
h
(
'div'
,
{
style
:
'display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: transform 0.2s ease; flex-shrink: 0;'
,
onClick
:
()
=>
{
// 创建确认对话框
dialog
?.
warning
({
title
:
'确认删除'
,
content
:
'确定要删除这条数据吗?'
,
positiveText
:
'确定'
,
negativeText
:
'取消'
,
onPositiveClick
:
async
()
=>
{
try
{
// 准备删除请求的参数
const
Kvids
=
[
row
.
Kvid
];
// 发送删除请求
const
response
=
await
axios
.
post
(
uiConfig
.
InitDelete
,
{
Kvids
});
if
(
response
.
data
)
{
message
?.
success
(
'删除成功'
);
// 重新加载数据
fetchData
();
}
else
{
message
?.
error
(
'删除失败'
);
}
}
catch
(
error
:
any
)
{
// console.error('删除失败:', error);
message
?.
error
(
`删除失败:
${
error
.
message
||
'未知错误'
}
`
);
}
}
});
}
},
h
(
'svg'
,
{
xmlns
:
'http://www.w3.org/2000/svg'
,
width
:
'16'
,
height
:
'16'
,
viewBox
:
'0 0 20 20'
,
fill
:
'none'
,
stroke
:
'currentColor'
},
[
h
(
'path'
,
{
d
:
'M11.5 4a1.5 1.5 0 0 0-3 0h-1a2.5 2.5 0 0 1 5 0H17a.5.5 0 0 1 0 1h-.554L15.15 16.23A2 2 0 0 1 13.163 18H6.837a2 2 0 0 1-1.987-1.77L3.553 5H3a.5.5 0 0 1-.492-.41L2.5 4.5A.5.5 0 0 1 3 4h8.5zm3.938 1H4.561l1.282 11.115a1 1 0 0 0 .994.885h6.326a1 1 0 0 0 .993-.885L15.438 5zM8.5 7.5c.245 0 .45.155.492.359L9 7.938v6.125c0 .241-.224.437-.5.437c-.245 0-.45-.155-.492-.359L8 14.062V7.939c0-.242.224-.438.5-.438zm3 0c.245 0 .45.155.492.359l.008.079v6.125c0 .241-.224.437-.5.437c-.245 0-.45-.155-.492-.359L11 14.062V7.939c0-.242.224-.438.5-.438z'
,
fill
:
'currentColor'
})
]
)
)
]
);
}
};
return
[...
baseColumns
,
actionColumn
];
});
// 根据标签类型获取颜色
function
getTagColor
(
type
:
string
)
{
switch
(
type
)
{
case
'default'
:
return
'#f0f0f0'
;
case
'primary'
:
return
'#2080f0'
;
case
'info'
:
return
'#909399'
;
case
'success'
:
return
'#18a058'
;
case
'warning'
:
return
'#f0a020'
;
case
'error'
:
return
'#d03050'
;
default
:
return
'#f0f0f0'
;
}
}
</
script
>
<
template
>
<NCard
class=
"search-card"
:bordered=
"false"
content-style=
"padding: 0 12px 8px;margin-top:10px"
>
<NDataTable
ref=
"table"
remote
:columns=
"processedColumns"
:data=
"tableData"
:loading=
"loading"
:pagination=
"
{
...pagination,
onChange: handlePageChange,
onUpdatePageSize: handlePageSizeChange
}"
:row-key="row => row.Kvid"
:max-height="tableHeight"
:scroll-x="tableScrollWidth"
:single-line="!showBorder"
:striped="showStripe"
:size="tableSize"
@update:sorter="handleSorterChange"
@update:filters="handleFiltersChange"
>
<template
#
loading
>
<NSpace
vertical
justify=
"center"
class=
"py-4"
>
<NSpin
size=
"medium"
></NSpin>
<NText
depth=
"3"
>
正在加载数据...
</NText>
</NSpace>
</
template
>
<
template
#
empty
>
<NEmpty
description=
"暂无数据"
>
<template
#
extra
>
<NButton
size=
"small"
@
click=
"fetchData"
>
重新加载
</NButton>
</
template
>
</NEmpty>
</template>
</NDataTable>
</NCard>
</template>
<
style
scoped
>
.search-card
{
margin-bottom
:
8px
;
}
.tag-container
{
display
:
flex
;
align-items
:
center
;
justify-content
:
flex-start
;
}
:deep
(
.n-data-table
)
{
--n-merged-th-color
:
var
(
--n-table-color
);
--n-merged-td-color
:
var
(
--n-table-color
);
}
:deep
(
.n-data-table-base-table-body
)
{
overflow
:
auto
!important
;
}
:deep
(
.n-data-table-table
)
{
min-width
:
100%
;
}
/*:deep(.n-data-table__pagination) {*/
/* position: fixed;*/
/* bottom: 12px;*/
/* right: 12px;*/
/*}*/
/* 优化分页器页码选择器的样式 */
:deep
(
.n-pagination
.n-pagination-size-picker
)
{
width
:
84px
!important
;
}
:deep
(
.n-pagination
.n-pagination-size-picker
.n-base-selection
)
{
width
:
84px
!important
;
}
/* 优化分页器整体布局 */
:deep
(
.n-pagination
)
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
}
/* 确保总条数显示正常 */
:deep
(
.n-pagination-prefix
)
{
margin-right
:
auto
;
white-space
:
nowrap
;
}
/* 优化表格滚动相关样式 */
:deep
(
.n-data-table-base-table
)
{
width
:
100%
;
}
:deep
(
.n-data-table-base-table-body
)
{
overflow-x
:
auto
!important
;
}
:deep
(
.n-scrollbar-container
)
{
min-width
:
100%
;
}
/* 图标按钮样式 */
.icon-button
{
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
transition
:
transform
0.2s
ease
;
flex-shrink
:
0
;
}
.icon-button
:hover
{
transform
:
scale
(
1.2
);
}
:deep
(
.n-data-table
.action-buttons
.n-button
)
{
min-width
:
60px
;
cursor
:
pointer
!important
;
}
/* 确保操作列固定在右侧 */
:deep
(
.n-data-table-td--fixed-right
)
{
background-color
:
v-bind
(
borderColor
)
!important
;
right
:
0px
;
}
:deep
(
.n-data-table-th--fixed-right
)
{
background-color
:
v-bind
(
borderColor
)
!important
;
right
:
0px
;
}
/* 直接针对最后一列的单元格设置样式 */
:deep
(
.n-data-table-td--last-col
)
{
background-color
:
v-bind
(
borderColor
)
!important
;
}
</
style
>
src/views/tableComponent/codet/components/DataTransform.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
;
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
// 获取表格数据和列
const
{
tagMappings
,
transformRules
,
selectedField
,
availableFields
}
=
tableState
;
// 颜色选项
const
colorOptions
=
[
{
label
:
'默认'
,
value
:
'default'
},
{
label
:
'主要'
,
value
:
'primary'
},
{
label
:
'信息'
,
value
:
'info'
},
{
label
:
'成功'
,
value
:
'success'
},
{
label
:
'警告'
,
value
:
'warning'
},
{
label
:
'错误'
,
value
:
'error'
}
];
// 添加/删除映射规则
const
addTagMapping
=
()
=>
{
tableMethods
.
addTagMapping
();
};
const
removeTagMapping
=
(
index
:
number
)
=>
{
tableMethods
.
removeTagMapping
(
index
);
};
// 添加转换规则
const
addTransformRule
=
()
=>
{
if
(
!
selectedField
.
value
)
return
;
// 过滤掉空值映射
const
validMappings
=
tagMappings
.
value
.
filter
((
mapping
:
any
)
=>
mapping
.
value
!==
''
&&
mapping
.
label
!==
''
);
if
(
validMappings
.
length
===
0
)
{
window
.
$message
?.
error
(
'请添加至少一个有效的标记映射'
);
return
;
}
tableMethods
.
addTransformRule
({
field
:
selectedField
.
value
,
fieldLabel
:
availableFields
.
value
.
find
((
f
:
any
)
=>
f
.
value
===
selectedField
.
value
)?.
label
||
selectedField
.
value
,
type
:
'tag'
,
typeLabel
:
'转换标记'
,
params
:
{
mappings
:
validMappings
}
});
// 重置输入
selectedField
.
value
=
''
;
tagMappings
.
value
=
[{
value
:
''
,
label
:
''
,
color
:
''
}];
};
// 移除转换规则
const
removeTransformRule
=
(
index
:
number
)
=>
{
tableMethods
.
removeTransformRule
(
index
);
};
// 应用转换规则
const
applyTransforms
=
()
=>
{
tableMethods
.
applyTransformations
();
};
// 重置数据转换
const
resetData
=
()
=>
{
// 清除所有转换规则
transformRules
.
value
=
[];
// 重新获取数据
tableMethods
.
fetchData
();
window
.
$message
?.
success
(
'数据已重置'
);
};
// 暴露方法给父组件
defineExpose
({
transformRules
,
applyTransforms
,
resetData
});
defineOptions
({
name
:
'DataTransform'
});
</
script
>
<
template
>
<div
class=
"data-transform-container"
>
<h3>
数据转换设置
</h3>
<div
class=
"transform-form"
>
<div
class=
"form-row"
>
<div
class=
"form-item"
>
<label>
字段名
</label>
<NSelect
v-model:value=
"selectedField"
:options=
"availableFields"
placeholder=
"请选择字段"
/>
</div>
</div>
<!-- 标记转换参数 -->
<div
class=
"param-section"
>
<h4>
转换内容
</h4>
<div
v-for=
"(mapping, index) in tagMappings"
:key=
"index"
class=
"mapping-item"
>
<div
class=
"mapping-row"
>
<div
class=
"mapping-field"
>
<label>
Name
</label>
<NInput
v-model:value=
"mapping.value"
placeholder=
"表格值"
/>
</div>
<div
class=
"mapping-field"
>
<label>
Value
</label>
<NInput
v-model:value=
"mapping.label"
placeholder=
"显示文本"
/>
</div>
<div
class=
"mapping-field"
>
<label>
颜色
</label>
<NSelect
v-model:value=
"mapping.color"
:options=
"colorOptions"
placeholder=
"选择颜色"
/>
</div>
<NButton
circle
type=
"error"
size=
"small"
class=
"remove-btn"
:disabled=
"tagMappings.length === 1"
@
click=
"removeTagMapping(index)"
>
<span>
-
</span>
</NButton>
</div>
</div>
<div
class=
"add-mapping"
>
<NButton
circle
type=
"primary"
size=
"small"
class=
"add-btn"
@
click=
"addTagMapping"
>
<span>
+
</span>
</NButton>
</div>
</div>
<div
class=
"button-row"
>
<NButton
type=
"primary"
:disabled=
"!selectedField"
@
click=
"addTransformRule"
>
添加规则
</NButton>
</div>
<div
v-if=
"transformRules.length > 0"
class=
"rules-list"
>
<h4>
转换规则列表
</h4>
<div
v-for=
"(rule, index) in transformRules"
:key=
"index"
class=
"rule-item"
>
<div
class=
"rule-content"
>
<span
class=
"field-name"
>
{{
rule
.
fieldLabel
}}
</span>
<span
class=
"transform-arrow"
>
→
</span>
<span
class=
"transform-type"
>
{{
rule
.
typeLabel
}}
</span>
</div>
<NButton
size=
"small"
type=
"error"
@
click=
"removeTransformRule(index)"
>
删除
</NButton>
</div>
<div
class=
"action-buttons"
>
<NButton
type=
"primary"
:disabled=
"transformRules.length === 0"
@
click=
"applyTransforms"
>
应用转换
</NButton>
<NButton
@
click=
"resetData"
>
重置数据
</NButton>
</div>
</div>
</div>
</div>
</
template
>
<
style
scoped
>
.data-transform-container
{
padding
:
16px
;
height
:
100%
;
overflow-y
:
auto
;
}
.transform-form
{
margin-top
:
16px
;
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
}
.form-row
{
display
:
flex
;
gap
:
12px
;
}
.form-item
{
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
gap
:
4px
;
}
.param-section
{
border
:
1px
solid
#eee
;
border-radius
:
4px
;
padding
:
12px
;
}
.param-section
h4
{
margin-top
:
0
;
margin-bottom
:
12px
;
color
:
#666
;
}
.button-row
{
display
:
flex
;
justify-content
:
flex-end
;
margin-top
:
16px
;
}
.rules-list
{
margin-top
:
24px
;
border-top
:
1px
solid
#eee
;
padding-top
:
16px
;
}
.rule-item
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
8px
12px
;
background-color
:
#f8f8f8
;
border-radius
:
4px
;
margin-bottom
:
8px
;
}
.rule-content
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
}
.field-name
{
font-weight
:
500
;
}
.transform-arrow
{
color
:
#666
;
}
.transform-type
{
color
:
#2080f0
;
}
.action-buttons
{
display
:
flex
;
gap
:
12px
;
margin-top
:
16px
;
}
.mapping-item
{
margin-bottom
:
12px
;
padding-bottom
:
12px
;
border-bottom
:
1px
dashed
#eee
;
}
.mapping-row
{
display
:
flex
;
gap
:
8px
;
align-items
:
flex-end
;
}
.mapping-field
{
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
gap
:
4px
;
}
.remove-btn
{
margin-bottom
:
4px
;
}
.add-mapping
{
display
:
flex
;
justify-content
:
center
;
margin-top
:
8px
;
}
</
style
>
src/views/tableComponent/codet/components/SearchFieldEditDrawer.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
;
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
const
{
showSearchFieldDrawer
,
currentSearchField
,
newOptionKey
,
newOptionValue
}
=
tableState
;
const
{
saveSearchField
,
addOption
,
removeOption
}
=
tableMethods
;
defineOptions
({
name
:
'SearchFieldEditDrawer'
});
</
script
>
<
template
>
<NDrawer
v-model:show=
"showSearchFieldDrawer"
:width=
"400"
placement=
"right"
>
<NDrawerContent
title=
"编辑搜索字段"
>
<NForm
ref=
"searchFieldForm"
:model=
"currentSearchField"
label-placement=
"left"
label-width=
"100"
>
<NFormItem
label=
"显示名称"
path=
"label"
>
<NInput
v-model:value=
"currentSearchField.label"
placeholder=
"请输入显示名称"
></NInput>
</NFormItem>
<NFormItem
label=
"字段名"
path=
"key"
>
<NInput
v-model:value=
"currentSearchField.key"
disabled
></NInput>
</NFormItem>
<NFormItem
label=
"控件类型"
path=
"type"
>
<NSelect
v-model:value=
"currentSearchField.type"
:options=
"[
{ label: '输入框', value: 'input' },
{ label: '下拉选择', value: 'select' },
{ label: '日期选择', value: 'date' },
{ label: '数字范围', value: 'number-range' }
]"
>
</NSelect>
</NFormItem>
<NFormItem
label=
"占位提示"
path=
"placeholder"
>
<NInput
v-model:value=
"currentSearchField.placeholder"
placeholder=
"请输入占位提示"
></NInput>
</NFormItem>
<NFormItem
label=
"默认值"
path=
"defaultValue"
>
<NInput
v-model:value=
"currentSearchField.defaultValue"
placeholder=
"请输入默认值"
></NInput>
</NFormItem>
<!-- 当类型为 select 时显示选项配置 -->
<template
v-if=
"currentSearchField.type === 'select'"
>
<NDivider>
选项配置
</NDivider>
<!-- 添加新选项的输入框 -->
<NSpace
vertical
>
<NInputGroup>
<NInput
v-model:value=
"newOptionKey"
placeholder=
"选项名称"
></NInput>
<NInput
v-model:value=
"newOptionValue"
placeholder=
"选项值"
></NInput>
<NButton
type=
"primary"
:disabled=
"!newOptionKey || !newOptionValue"
@
click=
"addOption"
>
添加
</NButton>
</NInputGroup>
</NSpace>
<!-- 已添加选项列表 -->
<NSpace
vertical
style=
"margin-top: 12px"
>
<div
v-for=
"(option, index) in currentSearchField.options"
:key=
"index"
style=
"display: flex; justify-content: space-between; align-items: center"
>
<span>
{{
option
.
label
}}
-
{{
option
.
value
}}
</span>
<NButton
text
type=
"error"
@
click=
"removeOption(index)"
>
<template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 24 24"
>
<path
d=
"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"
fill=
"currentColor"
/>
</svg>
</NIcon>
</
template
>
</NButton>
</div>
</NSpace>
</template>
</NForm>
<
template
#
footer
>
<NSpace
justify=
"end"
>
<NButton
@
click=
"showSearchFieldDrawer = false"
>
取消
</NButton>
<NButton
type=
"primary"
@
click=
"saveSearchField"
>
保存
</NButton>
</NSpace>
</
template
>
</NDrawerContent>
</NDrawer>
</template>
<
style
scoped
>
.n-input-group
{
display
:
flex
;
gap
:
8px
;
}
.n-input-group
.n-input
{
flex
:
1
;
}
.n-divider
{
margin
:
16px
0
;
}
</
style
>
src/views/tableComponent/codet/components/SearchFieldSettings.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
;
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
const
{
searchFields
}
=
tableState
;
defineOptions
({
name
:
'SearchFieldSettings'
});
const
{
addSearchField
,
editSearchField
,
removeSearchField
,
updateFieldVisibility
,
handleSearchFieldDragStart
,
handleSearchFieldDragOver
,
handleSearchFieldDrop
}
=
tableMethods
;
</
script
>
<
template
>
<div>
<div
class=
"search-config-header"
>
<span>
搜索字段配置
</span>
<NButton
size=
"small"
type=
"primary"
@
click=
"addSearchField"
>
添加字段
</NButton>
</div>
<div
class=
"search-fields-list"
>
<NScrollbar
style=
"max-height: calc(100vh - 200px)"
>
<div
class=
"search-fields-container"
>
<div
v-for=
"field in searchFields"
:key=
"field.key"
class=
"search-field-item"
draggable=
"true"
@
dragstart=
"handleSearchFieldDragStart($event, field)"
@
dragover=
"handleSearchFieldDragOver"
@
drop=
"handleSearchFieldDrop($event, field)"
>
<div
class=
"field-item-left"
>
<NIcon
class=
"field-drag-handle"
>
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 24 24"
>
<path
d=
"M8 18h8v-2H8v2zm0-4h8v-2H8v2zm0-4h8V8H8v2zm0-4h8V4H8v2z"
fill=
"currentColor"
/>
</svg>
</NIcon>
<NSwitch
:value=
"field.visible"
size=
"small"
@
update:value=
"val => updateFieldVisibility(field, val)"
></NSwitch>
</div>
<div
class=
"field-item-content"
>
<span
class=
"field-label"
>
{{
field
.
label
}}
</span>
<span
class=
"field-key"
>
(
{{
field
.
key
}}
)
</span>
</div>
<div
class=
"field-item-right"
>
<NButton
text
@
click=
"editSearchField(field)"
>
<template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 24 24"
>
<path
d=
"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a.996.996 0 0 0 0-1.41l-2.34-2.34a.996.996 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"
fill=
"currentColor"
/>
</svg>
</NIcon>
</
template
>
</NButton>
<NButton
text
@
click=
"removeSearchField(field)"
>
<
template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 24 24"
>
<path
d=
"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"
fill=
"currentColor"
/>
</svg>
</NIcon>
</
template
>
</NButton>
</div>
</div>
</div>
</NScrollbar>
</div>
</div>
</template>
<
style
scoped
>
.search-config-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
0
16px
12px
;
border-bottom
:
1px
solid
var
(
--n-border-color
);
}
.search-fields-container
{
padding
:
8px
0
;
}
.search-field-item
{
display
:
flex
;
align-items
:
center
;
padding
:
8px
16px
;
border-bottom
:
1px
solid
var
(
--n-border-color
);
cursor
:
move
;
}
.field-item-left
{
display
:
flex
;
align-items
:
center
;
gap
:
12px
;
}
.field-drag-handle
{
cursor
:
move
;
color
:
var
(
--n-text-color-3
);
}
.field-item-content
{
flex
:
1
;
margin
:
0
12px
;
}
.field-label
{
font-size
:
14px
;
color
:
var
(
--n-text-color
);
}
.field-key
{
font-size
:
12px
;
color
:
var
(
--n-text-color-3
);
margin-left
:
8px
;
}
.field-item-right
{
display
:
flex
;
gap
:
4px
;
opacity
:
0
;
transition
:
opacity
0.3s
;
}
.search-field-item
:hover
.field-item-right
{
opacity
:
1
;
}
</
style
>
src/views/tableComponent/codet/components/SearchForm.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
;
defineOptions
({
name
:
'SearchForm'
});
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
const
{
searchForm
,
formItems
,
isExpanded
}
=
tableState
;
const
{
handleSearch
,
resetSearch
,
fetchData
,
handleCreate
}
=
tableMethods
;
const
toggleExpand
=
()
=>
{
tableState
.
isExpanded
.
value
=
!
tableState
.
isExpanded
.
value
;
};
const
openSettings
=
()
=>
{
tableState
.
showSettingDrawer
.
value
=
true
;
};
</
script
>
<
template
>
<NCard
class=
"search-card"
:bordered=
"false"
content-style=
"padding: 0 12px 8px;margin-top:10px"
>
<NGrid
:cols=
"6"
x-gap=
"8"
y-gap=
"8"
>
<!-- 默认显示前5个表单项 -->
<NGridItem
v-for=
"item in formItems.slice(0, 5)"
:key=
"item.key"
>
<NFormItem
:label=
"item.label"
label-placement=
"left"
>
<NInput
v-if=
"item.type === 'input'"
v-model:value=
"searchForm[item.key]"
:placeholder=
"item.placeholder"
></NInput>
<NSelect
v-if=
"item.type === 'select'"
v-model:value=
"searchForm[item.key]"
:options=
"item.options"
:placeholder=
"item.placeholder"
></NSelect>
<NDatePicker
v-if=
"item.type === 'date'"
v-model:value=
"searchForm[item.key]"
type=
"daterange"
:placeholder=
"item.placeholder"
clearable
></NDatePicker>
</NFormItem>
</NGridItem>
<!-- 未展开时的操作按钮 -->
<NGridItem
v-if=
"!isExpanded"
>
<div
class=
"operation-buttons"
>
<NSpace
:size=
"8"
align=
"center"
:wrap=
"false"
>
<NButton
type=
"primary"
size=
"small"
@
click=
"() => handleCreate(null)"
>
新建
</NButton>
<NButton
type=
"primary"
size=
"small"
@
click=
"handleSearch"
>
查询
</NButton>
<NButton
size=
"small"
@
click=
"resetSearch"
>
重置
</NButton>
<NButton
size=
"small"
@
click=
"toggleExpand"
>
<template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 24 24"
>
<path
d=
"M18 6.41L16.59 5L12 9.58L7.41 5L6 6.41l6 6z"
fill=
"currentColor"
></path>
<path
d=
"M18 13l-1.41-1.41L12 16.17l-4.59-4.58L6 13l6 6z"
fill=
"currentColor"
></path>
</svg>
</NIcon>
</
template
>
</NButton>
<NButton
size=
"small"
title=
"刷新数据"
@
click=
"fetchData"
>
<
template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 24 24"
>
<path
d=
"M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"
fill=
"currentColor"
></path>
</svg>
</NIcon>
</
template
>
</NButton>
<NButton
size=
"small"
@
click=
"openSettings"
>
<
template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 24 24"
>
<path
opacity=
".3"
d=
"M19.28 8.6l-.7-1.21l-1.27.51l-1.06.43l-.91-.7c-.39-.3-.8-.54-1.23-.71l-1.06-.43l-.16-1.13L12.7 4h-1.4l-.19 1.35l-.16 1.13l-1.06.44c-.41.17-.82.41-1.25.73l-.9.68l-1.05-.42l-1.27-.52l-.7 1.21l1.08.84l.89.7l-.14 1.13c-.03.3-.05.53-.05.73s.02.43.05.73l.14 1.13l-.89.7l-1.08.84l.7 1.21l1.27-.51l1.06-.43l.91.7c.39.3.8.54 1.23.71l1.06.43l.16 1.13l.19 1.36h1.39l.19-1.35l.16-1.13l1.06-.43c.41-.17.82-.41 1.25-.73l.9-.68l1.04.42l1.27.51l.7-1.21l-1.08-.84l-.89-.7l.14-1.13c.04-.31.05-.52.05-.73c0-.21-.02-.43-.05-.73l-.14-1.13l.89-.7l1.1-.84zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4s4 1.79 4 4s-1.79 4-4 4z"
fill=
"currentColor"
></path>
<path
d=
"M19.43 12.98c.04-.32.07-.64.07-.98c0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.566.566 0 0 0-.18-.03c-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46a.5.5 0 0 0 .61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.42.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03c.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73c0 .21-.02.43-.05.73l-.14 1.13l.89.7l1.08.84l-.7 1.21l-1.27-.51l-1.04-.42l-.9.68c-.43.32-.84.56-1.25.73l-1.06.43l-.16 1.13l-.2 1.35h-1.4l-.19-1.35l-.16-1.13l-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7l-1.06.43l-1.27.51l-.7-1.21l1.08-.84l.89-.7l-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13l-.89-.7l-1.08-.84l.7-1.21l1.27.51l1.04.42l.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43l.16-1.13l.2-1.35h1.39l.19 1.35l.16 1.13l1.06.43c.43.18.83.41 1.23.71l.91.7l1.06-.43l1.27-.51l.7 1.21l-1.07.85l-.89.7l.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4s4-1.79 4-4s-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2s2 .9 2 2s-.9 2-2 2z"
fill=
"currentColor"
></path>
</svg>
</NIcon>
</
template
>
</NButton>
</NSpace>
</div>
</NGridItem>
<!-- 展开后的额外表单项 -->
<
template
v-if=
"isExpanded"
>
<NGridItem
v-for=
"item in formItems.slice(5)"
:key=
"item.key"
>
<NFormItem
:label=
"item.label"
label-placement=
"left"
>
<NInput
v-if=
"item.type === 'input'"
v-model:value=
"searchForm[item.key]"
:placeholder=
"item.placeholder"
></NInput>
<NSelect
v-if=
"item.type === 'select'"
v-model:value=
"searchForm[item.key]"
:options=
"item.options"
:placeholder=
"item.placeholder"
></NSelect>
<NDatePicker
v-if=
"item.type === 'date'"
v-model:value=
"searchForm[item.key]"
type=
"daterange"
:placeholder=
"item.placeholder"
clearable
></NDatePicker>
</NFormItem>
</NGridItem>
<!-- 展开后的操作按钮,独占一行并靠左对齐 -->
<NGridItem
:span=
"6"
>
<div
class=
"operation-buttons-expanded"
>
<NSpace
:size=
"8"
align=
"center"
:wrap=
"false"
>
<NButton
size=
"small"
@
click=
"() => handleCreate(null)"
>
新建
</NButton>
<NButton
size=
"small"
@
click=
"handleSearch"
>
查询
</NButton>
<NButton
size=
"small"
@
click=
"resetSearch"
>
重置
</NButton>
<NButton
size=
"small"
@
click=
"toggleExpand"
>
<template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 16 16"
>
<g
fill=
"none"
>
<path
d=
"M4.26 8.3a.75.75 0 1 1-1.02-1.1l4.25-4a.75.75 0 0 1 1.02 0l4.25 4a.75.75 0 1 1-1.02 1.1L8 4.773L4.26 8.3zm0 4a.75.75 0 0 1-1.02-1.1l4.25-4a.75.75 0 0 1 1.02 0l4.25 4a.75.75 0 1 1-1.02 1.1L8 8.773L4.26 12.3z"
fill=
"currentColor"
></path>
</g>
</svg>
</NIcon>
</
template
>
</NButton>
<NButton
size=
"small"
title=
"刷新数据"
@
click=
"fetchData"
>
<
template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 24 24"
>
<path
d=
"M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"
fill=
"currentColor"
></path>
</svg>
</NIcon>
</
template
>
</NButton>
<NButton
size=
"small"
@
click=
"openSettings"
>
<
template
#
icon
>
<NIcon>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 24 24"
>
<path
opacity=
".3"
d=
"M19.28 8.6l-.7-1.21l-1.27.51l-1.06.43l-.91-.7c-.39-.3-.8-.54-1.23-.71l-1.06-.43l-.16-1.13L12.7 4h-1.4l-.19 1.35l-.16 1.13l-1.06.44c-.41.17-.82.41-1.25.73l-.9.68l-1.05-.42l-1.27-.52l-.7 1.21l1.08.84l.89.7l-.14 1.13c-.03.3-.05.53-.05.73s.02.43.05.73l.14 1.13l-.89.7l-1.08.84l.7 1.21l1.27-.51l1.06-.43l.91.7c.39.3.8.54 1.23.71l1.06.43l.16 1.13l.19 1.36h1.39l.19-1.35l.16-1.13l1.06-.43c.41-.17.82-.41 1.25-.73l.9-.68l1.04.42l1.27.51l.7-1.21l-1.08-.84l-.89-.7l.14-1.13c.04-.31.05-.52.05-.73c0-.21-.02-.43-.05-.73l-.14-1.13l.89-.7l1.1-.84zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4s4 1.79 4 4s-1.79 4-4 4z"
fill=
"currentColor"
></path>
<path
d=
"M19.43 12.98c.04-.32.07-.64.07-.98c0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.566.566 0 0 0-.18-.03c-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46a.5.5 0 0 0 .61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.42.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03c.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73c0 .21-.02.43-.05.73l-.14 1.13l.89.7l1.08.84l-.7 1.21l-1.27-.51l-1.04-.42l-.9.68c-.43.32-.84.56-1.25.73l-1.06.43l-.16 1.13l-.2 1.35h-1.4l-.19-1.35l-.16-1.13l-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7l-1.06.43l-1.27.51l-.7-1.21l1.08-.84l.89-.7l-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13l-.89-.7l-1.08-.84l.7-1.21l1.27.51l1.04.42l.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43l.16-1.13l.2-1.35h1.39l.19 1.35l.16 1.13l1.06.43c.43.18.83.41 1.23.71l.91.7l1.06-.43l1.27-.51l.7 1.21l-1.07.85l-.89.7l.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4s4-1.79 4-4s-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2s2 .9 2 2s-.9 2-2 2z"
fill=
"currentColor"
></path>
</svg>
</NIcon>
</
template
>
</NButton>
</NSpace>
</div>
</NGridItem>
</template>
</NGrid>
</NCard>
</template>
<
style
scoped
>
.search-card
{
margin-bottom
:
8px
;
}
.operation-buttons
{
display
:
flex
;
align-items
:
center
;
height
:
100%
;
padding-bottom
:
2px
;
}
.operation-buttons-expanded
{
display
:
flex
;
align-items
:
center
;
padding
:
8px
0
0
0
;
justify-content
:
flex-end
;
}
:deep
(
.n-space
)
{
flex-wrap
:
nowrap
;
}
:deep
(
.n-form-item
)
{
margin-bottom
:
0
;
}
:deep
(
.n-form-item-label
)
{
width
:
120px
;
justify-content
:
flex-end
;
padding
:
0
;
margin-right
:
4px
;
text-align
:
right
;
white-space
:
normal
;
}
:deep
(
.n-form-item-label
label
)
{
display
:
inline-block
;
text-align
:
right
;
width
:
100%
;
}
:deep
(
.n-form-item-label
label
:after
)
{
content
:
''
;
display
:
block
;
text-align
:
left
;
}
:deep
(
.n-form-item-blank
)
{
min-height
:
28px
;
justify-content
:
flex-end
;
}
:deep
(
.n-input
)
{
--n-height
:
28px
;
}
:deep
(
.n-select
)
{
--n-height
:
28px
;
}
:deep
(
.n-date-picker
)
{
--n-height
:
28px
;
}
:deep
(
.n-form-item-feedback-wrapper
)
{
display
:
none
;
}
</
style
>
src/views/tableComponent/codet/components/TableBasicSettings.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
;
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
{
tableSize
,
showBorder
,
showStripe
,
enableSingleSelect
,
pagination
,
tableScrollWidth
,
tableHeight
}
=
tableState
;
</
script
>
<
template
>
<div>
<!-- 表格大小设置 -->
<div
class=
"setting-item"
>
<div
class=
"setting-label"
>
表格的大小
</div>
<div
class=
"setting-control"
>
<NRadioGroup
v-model:value=
"tableSize"
name=
"tableSize"
class=
"size-group"
size=
"small"
>
<NRadioButton
value=
"small"
>
紧凑
</NRadioButton>
<NRadioButton
value=
"medium"
>
默认
</NRadioButton>
<NRadioButton
value=
"large"
>
宽松
</NRadioButton>
</NRadioGroup>
</div>
</div>
<!-- 表格纵向边框 -->
<div
class=
"setting-item"
>
<div
class=
"setting-label"
>
表格纵向边框
</div>
<div
class=
"setting-control"
>
<NSwitch
v-model:value=
"showBorder"
></NSwitch>
</div>
</div>
<!-- 表格隔行换色 -->
<div
class=
"setting-item"
>
<div
class=
"setting-label"
>
表格条纹
</div>
<div
class=
"setting-control"
>
<NSpace
align=
"center"
>
<NSwitch
v-model:value=
"showStripe"
></NSwitch>
</NSpace>
</div>
</div>
<!-- 是否开启单选 -->
<div
class=
"setting-item"
>
<div
class=
"setting-label"
>
是否开启序号
</div>
<div
class=
"setting-control"
>
<NSwitch
v-model:value=
"enableSingleSelect"
></NSwitch>
</div>
</div>
<!-- 每页显示条数 -->
<div
class=
"setting-item"
>
<div
class=
"setting-label"
>
每页显示条数
</div>
<div
class=
"setting-control"
>
<NInputNumber
v-model:value=
"pagination.pageSize"
></NInputNumber>
</div>
</div>
<!-- 表格宽度 -->
<div
class=
"setting-item"
>
<div
class=
"setting-label"
>
表格宽度
</div>
<div
class=
"setting-control"
>
<NInputNumber
v-model:value=
"tableScrollWidth"
></NInputNumber>
</div>
</div>
<!-- 表格高度 -->
<div
class=
"setting-item"
>
<div
class=
"setting-label"
>
表格高度
</div>
<div
class=
"setting-control"
>
<NInputNumber
v-model:value=
"tableHeight"
></NInputNumber>
</div>
</div>
</div>
</
template
>
<
style
scoped
>
.setting-item
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
font-size
:
13px
;
padding
:
10px
0
;
border-bottom
:
1px
solid
var
(
--n-border-color
);
}
.setting-item
:last-child
{
border-bottom
:
none
;
}
.setting-label
{
flex
:
0
0
120px
;
color
:
var
(
--n-text-color-2
);
font-size
:
14px
;
}
.setting-control
{
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
flex-end
;
}
.size-group
{
width
:
100%
;
}
:deep
(
.n-radio-group
)
{
display
:
flex
;
gap
:
8px
;
}
:deep
(
.n-radio-button
)
{
flex
:
1
;
text-align
:
center
;
}
:deep
(
.n-select
),
:deep
(
.n-input
),
:deep
(
.n-input-number
)
{
width
:
100%
;
}
</
style
>
src/views/tableComponent/codet/components/TableSettingsDrawer.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
import
{
defineExpose
,
inject
,
ref
}
from
'vue'
;
import
TableBasicSettings
from
'./TableBasicSettings.vue'
;
import
ColumnSettings
from
'./ColumnSettings.vue'
;
import
SearchFieldSettings
from
'./SearchFieldSettings.vue'
;
import
DataTransform
from
'./DataTransform.vue'
;
defineOptions
({
name
:
'TableSettingsDrawer'
});
// 通过window对象使用axios
const
axios
=
(
window
as
any
).
$axios
;
// 使用全局注入的axios实例
// 从主组件注入状态和方法
const
tableState
:
any
=
inject
(
'tableState'
);
const
tableMethods
:
any
=
inject
(
'tableMethods'
);
const
{
showSettingDrawer
,
activeTab
,
tableSize
,
showBorder
,
showStripe
,
enableSingleSelect
,
tableHeight
,
tableScrollWidth
,
visibleColumns
,
searchFields
,
formItems
,
uiConfig
}
=
tableState
;
// 获取所有设置信息
const
getAllSettings
=
()
=>
{
// 获取列编辑配置
const
columnConfigs
=
tableState
.
rawColumns
.
value
.
map
((
column
:
any
,
index
:
number
)
=>
({
key
:
column
.
key
,
title
:
column
.
title
,
titleAlign
:
column
.
titleAlign
,
align
:
column
.
align
,
sortable
:
column
.
sortable
,
filter
:
column
.
filter
,
filterOptions
:
column
.
filterOptions
,
ellipsis
:
column
.
ellipsis
,
width
:
column
.
width
,
minWidth
:
column
.
minWidth
,
maxWidth
:
column
.
maxWidth
,
fixed
:
column
.
fixed
,
resizable
:
column
.
resizable
,
order
:
index
// 保存列的排序顺序
}));
// 获取搜索字段配置
const
searchFieldConfigs
=
tableState
.
searchFields
.
value
.
map
((
field
:
any
)
=>
({
key
:
field
.
key
,
label
:
field
.
label
,
type
:
field
.
type
,
visible
:
field
.
visible
,
defaultValue
:
field
.
defaultValue
,
placeholder
:
field
.
placeholder
,
options
:
field
.
options
,
order
:
field
.
order
}));
// 获取数据转换配置
const
transformConfigs
=
{
// 保存转换规则
transformRules
:
tableState
.
transformRules
.
value
,
// 保存标签映射
tagMappings
:
tableState
.
tagMappings
.
value
,
// 保存字段转换配置
transformConfigurations
:
tableState
.
transformConfigurations
.
value
,
// 保存列的自定义渲染函数配置
columnRenderConfigs
:
tableState
.
rawColumns
.
value
// eslint-disable-next-line no-underscore-dangle
.
filter
((
column
:
any
)
=>
column
.
__originalRender
)
.
map
((
column
:
any
)
=>
({
key
:
column
.
key
,
hasCustomRender
:
true
}))
};
return
{
// 表格基本设置
tableSettings
:
{
tableSize
:
tableSize
.
value
,
showBorder
:
showBorder
.
value
,
showStripe
:
showStripe
.
value
,
enableSingleSelect
:
enableSingleSelect
.
value
,
tableHeight
:
tableHeight
.
value
,
tableScrollWidth
:
tableScrollWidth
.
value
},
// 列设置
columnSettings
:
{
visibleColumns
:
visibleColumns
.
value
,
columnConfigs
,
columnOrder
:
tableState
.
rawColumns
.
value
.
map
((
col
:
any
)
=>
col
.
key
)
// 保存列的顺序
},
// 搜索设置
searchSettings
:
{
searchFields
:
searchFields
.
value
,
formItems
:
formItems
.
value
,
searchFieldConfigs
},
// 数据转换设置
transformSettings
:
transformConfigs
};
};
// 添加应用配置的方法
const
applySettings
=
(
settings
:
any
)
=>
{
// 应用表格基本设置
if
(
settings
.
tableSettings
)
{
tableSize
.
value
=
settings
.
tableSettings
.
tableSize
;
showBorder
.
value
=
settings
.
tableSettings
.
showBorder
;
showStripe
.
value
=
settings
.
tableSettings
.
showStripe
;
enableSingleSelect
.
value
=
settings
.
tableSettings
.
enableSingleSelect
;
tableHeight
.
value
=
settings
.
tableSettings
.
tableHeight
;
tableScrollWidth
.
value
=
settings
.
tableSettings
.
tableScrollWidth
;
}
// 应用列设置
if
(
settings
.
columnSettings
)
{
visibleColumns
.
value
=
settings
.
columnSettings
.
visibleColumns
;
// 应用列排序
if
(
settings
.
columnSettings
.
columnOrder
)
{
// 根据保存的顺序重新排列列
const
orderedColumns
:
any
[]
=
[];
settings
.
columnSettings
.
columnOrder
.
forEach
((
key
:
any
)
=>
{
const
column
=
tableState
.
rawColumns
.
value
.
find
((
col
:
any
)
=>
col
.
key
===
key
);
if
(
column
)
{
orderedColumns
.
push
(
column
);
}
});
// 添加可能新增的列(不在保存的顺序中的列)
tableState
.
rawColumns
.
value
.
forEach
((
column
:
any
)
=>
{
if
(
!
settings
.
columnSettings
.
columnOrder
.
includes
(
column
.
key
))
{
orderedColumns
.
push
(
column
);
}
});
// 更新列配置
tableState
.
rawColumns
.
value
=
orderedColumns
;
}
// 应用列配置
if
(
settings
.
columnSettings
.
columnConfigs
)
{
tableState
.
rawColumns
.
value
=
tableState
.
rawColumns
.
value
.
map
((
column
:
any
)
=>
{
const
config
=
settings
.
columnSettings
.
columnConfigs
.
find
((
c
:
any
)
=>
c
.
key
===
column
.
key
);
if
(
config
)
{
return
{
...
column
,
...
config
};
}
return
column
;
});
}
}
// 应用搜索设置
if
(
settings
.
searchSettings
)
{
searchFields
.
value
=
settings
.
searchSettings
.
searchFields
;
formItems
.
value
=
settings
.
searchSettings
.
formItems
;
// 应用搜索字段配置
if
(
settings
.
searchSettings
.
searchFieldConfigs
)
{
tableState
.
searchFields
.
value
=
settings
.
searchSettings
.
searchFieldConfigs
;
}
}
// 应用数据转换设置
if
(
settings
.
transformSettings
)
{
// 恢复转换规则
if
(
settings
.
transformSettings
.
transformRules
)
{
tableState
.
transformRules
.
value
=
settings
.
transformSettings
.
transformRules
;
}
// 恢复标签映射
if
(
settings
.
transformSettings
.
tagMappings
)
{
tableState
.
tagMappings
.
value
=
settings
.
transformSettings
.
tagMappings
;
}
// 恢复字段转换配置
if
(
settings
.
transformSettings
.
transformConfigurations
)
{
tableState
.
transformConfigurations
.
value
=
settings
.
transformSettings
.
transformConfigurations
;
}
// 恢复列的自定义渲染配置
if
(
settings
.
transformSettings
.
columnRenderConfigs
)
{
settings
.
transformSettings
.
columnRenderConfigs
.
forEach
((
config
:
any
)
=>
{
const
column
=
tableState
.
rawColumns
.
value
.
find
((
col
:
any
)
=>
col
.
key
===
config
.
key
);
if
(
column
&&
config
.
hasCustomRender
)
{
// 使用 tableMethods 中的方法更新列渲染函数
tableMethods
.
updateTransformedColumnRenders
();
}
});
}
// 如果有转换规则,自动应用转换
if
(
settings
.
transformSettings
.
transformRules
?.
length
>
0
)
{
tableMethods
.
applyTransformations
();
}
}
};
const
settings
=
ref
({});
// 获取配置信息的方法
const
handleGetSettings
=
async
()
=>
{
try
{
// 在请求前检查必要的配置参数
if
(
!
uiConfig
.
GetUrl
||
!
uiConfig
.
Type
)
{
// console.warn('缺少必要的配置参数,无法获取配置');
return
;
}
const
response
=
await
axios
.
post
(
uiConfig
.
GetUrl
,
{
Type
:
uiConfig
.
Type
,
InternalCode
:
uiConfig
.
InternalCode
,
IsDefault
:
uiConfig
.
IsDefault
});
if
(
response
.
data
?.
Parameters
)
{
settings
.
value
=
JSON
.
parse
(
response
.
data
.
Parameters
);
// console.log('获取到的配置信息:', settings.value);
applySettings
(
settings
.
value
);
// window.$message?.success('配置信息获取成功');
}
else
{
// console.log('没有获取到配置参数,使用默认配置');
}
}
catch
(
error
)
{
// console.error('获取配置失败:', error);
window
.
$message
?.
error
(
'获取配置失败'
);
}
};
// 保存配置信息的方法
const
handleSetSettings
=
async
()
=>
{
try
{
const
response
=
await
axios
.
post
(
uiConfig
.
SetUrl
,
{
Type
:
uiConfig
.
Type
,
InternalCode
:
uiConfig
.
InternalCode
,
IsDefault
:
uiConfig
.
IsDefault
,
Parameters
:
JSON
.
stringify
(
getAllSettings
())
});
if
(
response
.
data
)
{
// 应用配置
applySettings
(
settings
.
value
);
window
.
$message
?.
success
(
'配置信息保存成功'
);
handleGetSettings
();
}
}
catch
(
error
)
{
// console.error('保存配置失败:', error);
window
.
$message
?.
error
(
'保存配置失败'
);
}
};
// 导出方法供外部使用
defineExpose
({
getAllSettings
,
applySettings
,
handleGetSettings
});
</
script
>
<
template
>
<NDrawer
v-model:show=
"showSettingDrawer"
:width=
"550"
placement=
"right"
>
<NDrawerContent
title=
"表格设置"
>
<div
class=
"settings-container"
>
<NTabs
v-model:value=
"activeTab"
placement=
"left"
type=
"line"
class=
"settings-tabs"
>
<NTabPane
name=
"table"
tab=
"表格设置"
>
<div
class=
"tab-content"
>
<TableBasicSettings
/>
</div>
</NTabPane>
<NTabPane
name=
"columns"
tab=
"列设置"
>
<div
class=
"tab-content"
>
<ColumnSettings
/>
</div>
</NTabPane>
<NTabPane
name=
"buttons"
tab=
"搜索配置"
>
<div
class=
"tab-content"
>
<SearchFieldSettings
/>
</div>
</NTabPane>
<NTabPane
name=
"transform"
tab=
"数据转换"
>
<div
class=
"tab-content"
>
<DataTransform
/>
</div>
</NTabPane>
</NTabs>
</div>
<div
style=
"position: fixed; top: 5px; right: 5px"
>
<NButton
type=
"primary"
style=
"margin-right: 5px"
@
click=
"handleGetSettings"
>
获取配置信息
</NButton>
<NButton
type=
"primary"
@
click=
"handleSetSettings"
>
应用配置信息
</NButton>
</div>
</NDrawerContent>
</NDrawer>
</
template
>
<
style
scoped
>
.settings-container
{
height
:
80%
;
display
:
flex
;
}
.settings-tabs
{
flex
:
1
;
height
:
100%
;
}
.tab-content
{
padding
:
16px
0
;
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
}
:deep
(
.n-tabs
)
{
height
:
100%
;
}
:deep
(
.n-tabs-nav
)
{
width
:
100px
;
border-right
:
1px
solid
var
(
--n-border-color
);
}
:deep
(
.n-tabs-nav__scroll-container
)
{
padding
:
8px
0
;
}
:deep
(
.n-tabs-tab
)
{
justify-content
:
flex-start
;
padding
:
12px
16px
;
}
:deep
(
.n-tabs-tab-wrapper
)
{
padding
:
0
;
}
:deep
(
.n-tabs-wrapper
)
{
flex
:
1
;
height
:
100%
;
}
:deep
(
.n-tab-pane
)
{
padding
:
0
;
height
:
100%
;
}
:deep
(
.n-drawer-content
)
{
padding
:
0
;
}
:deep
(
.n-drawer-header
)
{
padding
:
16px
;
border-bottom
:
1px
solid
var
(
--n-border-color
);
}
:deep
(
.n-drawer-header__main
)
{
font-size
:
16px
;
font-weight
:
500
;
}
</
style
>
src/views/tableComponent/index.vue
0 → 100644
View file @
8516388e
<
script
setup
lang=
"ts"
>
/* eslint-disable no-underscore-dangle */
import
{
computed
,
defineProps
,
h
,
nextTick
,
onActivated
,
onMounted
,
provide
,
ref
,
watch
,
withDefaults
}
from
'vue'
;
import
SearchForm
from
'./codet/components/SearchForm.vue'
;
import
DataTable
from
'./codet/components/DataTable.vue'
;
import
TableSettingsDrawer
from
'./codet/components/TableSettingsDrawer.vue'
;
import
ColumnEditDrawer
from
'./codet/components/ColumnEditDrawer.vue'
;
import
SearchFieldEditDrawer
from
'./codet/components/SearchFieldEditDrawer.vue'
;
import
AddFieldDrawer
from
'./codet/components/AddFieldDrawer.vue'
;
import
DataTransform
from
'./codet/components/DataTransform.vue'
;
import
CreateDialog
from
'./codet/components/CreateDialog.vue'
;
// 通过window对象使用axios - 已在main.ts中全局注册
const
axios
=
(
window
as
any
).
$axios
;
// 定义组件名称,避免与HTML元素冲突
defineOptions
({
name
:
'TableView'
});
const
tableData
=
ref
([]);
const
loading
=
ref
(
false
);
// 在 script setup 中添加响应式变量
const
tableSize
=
ref
(
'medium'
);
// 表格大小
const
showBorder
=
ref
(
true
);
// 表格纵向边框
const
showStripe
=
ref
(
true
);
// 表格隔行换色
const
enableSingleSelect
=
ref
(
false
);
// 是否开启序号
const
activeTab
=
ref
(
'table'
);
// 当前激活的标签页
// 定义props
interface
Props
{
uiConfig
?:
Record
<
string
,
any
>
;
}
const
props
=
withDefaults
(
defineProps
<
Props
>
(),
{
uiConfig
:
()
=>
({})
});
// 分页配置
const
pagination
=
ref
({
page
:
1
,
pageSize
:
20
,
itemCount
:
0
,
showSizePicker
:
true
,
pageSizes
:
[
20
,
30
,
40
,
50
],
prefix
:
({
itemCount
}:
{
itemCount
:
number
})
=>
`共
${
itemCount
}
条`
});
// 定义列的类型
interface
TableColumn
{
title
:
string
;
key
:
string
;
width
:
number
;
minWidth
?:
number
;
render
?:
(
row
:
any
,
index
?:
number
)
=>
any
;
// 修改render函数类型定义
ellipsis
?:
{
tooltip
:
boolean
;
};
resizable
?:
boolean
;
align
?:
'left'
|
'center'
|
'right'
;
titleAlign
?:
'left'
|
'center'
|
'right'
;
sortable
?:
boolean
;
sorter
?:
(
row1
:
any
,
row2
:
any
)
=>
number
;
filter
?:
boolean
;
filterOptions
?:
{
label
:
string
;
value
:
string
}[];
filterOptionValues
?:
string
[]
|
null
;
// 用于存储当前选中的筛选值
__originalRender
?:
(
row
:
any
,
index
?:
number
)
=>
any
;
// 备份原始渲染函数
}
// 接口类型定义
interface
SearchFieldOption
{
label
:
string
;
value
:
string
;
}
interface
SearchField
{
label
:
string
;
key
:
string
;
type
:
string
;
visible
:
boolean
;
defaultValue
:
string
;
placeholder
:
string
;
options
:
SearchFieldOption
[];
order
:
number
;
}
interface
TagMapping
{
value
:
string
;
label
:
string
;
color
:
string
;
}
interface
TransformRule
{
field
:
string
;
fieldLabel
:
string
;
type
:
string
;
typeLabel
:
string
;
params
:
{
mappings
:
TagMapping
[];
};
}
// 添加日期格式化函数
const
formatDate
=
(
dateStr
:
string
)
=>
{
if
(
!
dateStr
)
return
''
;
const
date
=
new
Date
(
dateStr
);
if
(
Number
.
isNaN
(
date
.
getTime
()))
return
dateStr
;
const
year
=
date
.
getFullYear
();
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
);
const
day
=
String
(
date
.
getDate
()).
padStart
(
2
,
'0'
);
return
`
${
year
}
-
${
month
}
-
${
day
}
`
;
};
// 原始列配置
const
rawColumns
=
ref
<
TableColumn
[]
>
([]);
// 添加列配置相关的响应式变量
const
visibleColumns
=
ref
<
string
[]
>
([]);
// 当前筛选状态
const
currentFilters
=
ref
<
Record
<
string
,
any
>>
({});
// 获取当前所有列的筛选状态
const
getFiltersState
=
()
=>
{
const
filters
:
Record
<
string
,
string
[]
>
=
{};
rawColumns
.
value
.
forEach
(
col
=>
{
if
(
col
.
filter
&&
col
.
filterOptionValues
&&
col
.
filterOptionValues
.
length
)
{
filters
[
col
.
key
]
=
col
.
filterOptionValues
;
}
});
return
filters
;
};
// 筛选改变
const
handleFiltersChange
=
(
filters
:
any
)
=>
{
// 保存当前筛选状态到currentFilters
// 格式化筛选参数,确保数组标识在冒号后面,值在数组里
const
formattedFilters
:
Record
<
string
,
any
>
=
{};
for
(
const
key
in
filters
)
{
if
(
filters
[
key
]
&&
filters
[
key
].
length
)
{
// 移除可能存在的数组标记
const
cleanKey
=
key
.
replace
(
'[]'
,
''
);
// 将值作为数组存储
formattedFilters
[
cleanKey
]
=
filters
[
key
];
}
}
currentFilters
.
value
=
formattedFilters
;
pagination
.
value
.
page
=
1
;
fetchData
();
};
// 修改 generateColumns 函数
const
generateColumns
=
(
data
:
any
):
TableColumn
[]
=>
{
if
(
!
data
)
return
[];
const
columns
:
TableColumn
[]
=
[];
// 添加序号列
if
(
enableSingleSelect
.
value
)
{
columns
.
push
({
title
:
'序号'
,
key
:
'index'
,
width
:
80
,
minWidth
:
50
,
render
:
(
_
,
index
=
0
)
=>
index
+
1
+
(
pagination
.
value
.
page
-
1
)
*
pagination
.
value
.
pageSize
});
}
const
processField
=
(
obj
:
any
,
prefix
=
''
)
=>
{
Object
.
entries
(
obj
).
forEach
(([
key
,
value
])
=>
{
// 处理数组类型字段
if
(
Array
.
isArray
(
value
))
{
const
columnKey
=
prefix
+
key
;
// 查找原有列的配置
const
existingColumn
=
rawColumns
.
value
.
find
(
col
=>
col
.
key
===
columnKey
);
columns
.
push
({
title
:
key
,
key
:
columnKey
,
width
:
200
,
minWidth
:
150
,
ellipsis
:
existingColumn
?.
ellipsis
??
{
tooltip
:
true
},
resizable
:
true
,
sortable
:
false
,
// 数组类型字段不支持排序
render
:
(
row
:
any
)
=>
{
const
arrayValue
=
row
[
key
];
if
(
Array
.
isArray
(
arrayValue
))
{
return
arrayValue
.
map
(
item
=>
{
if
(
typeof
item
===
'object'
)
{
return
Object
.
entries
(
item
)
.
map
(([
k
,
v
])
=>
`
${
k
}
:
${
v
}
`
)
.
join
(
', '
);
}
return
String
(
item
);
})
.
join
(
'; '
);
}
return
''
;
}
});
}
// 处理嵌套对象
else
if
(
value
&&
typeof
value
===
'object'
&&
!
Array
.
isArray
(
value
))
{
Object
.
entries
(
value
as
object
).
forEach
(([
nestedKey
,
_
])
=>
{
const
columnKey
=
`
${
key
}
.
${
nestedKey
}
`
;
// 查找原有列的配置
const
existingColumn
=
rawColumns
.
value
.
find
(
col
=>
col
.
key
===
columnKey
);
columns
.
push
({
title
:
nestedKey
,
key
:
columnKey
,
width
:
key
.
includes
(
'Time'
)
?
200
:
150
,
minWidth
:
100
,
ellipsis
:
existingColumn
?.
ellipsis
??
{
tooltip
:
true
},
resizable
:
true
,
sortable
:
existingColumn
?.
sortable
??
false
,
...(
existingColumn
?.
filter
?
{
filter
:
existingColumn
.
filter
,
filterOptions
:
existingColumn
.
filterOptions
,
filterOptionValues
:
existingColumn
.
filterOptionValues
}
:
{}),
render
:
key
.
includes
(
'Time'
)
||
key
.
includes
(
'Date'
)
?
(
row
:
any
)
=>
formatDate
(
row
[
key
]?.[
nestedKey
])
:
undefined
,
sorter
:
(
row1
:
any
,
row2
:
any
)
=>
{
const
val1
=
row1
[
key
]?.[
nestedKey
];
const
val2
=
row2
[
key
]?.[
nestedKey
];
if
(
val1
>
val2
)
return
1
;
if
(
val1
<
val2
)
return
-
1
;
return
0
;
}
});
});
}
// 处理普通字段
else
{
const
columnKey
=
prefix
+
key
;
// 查找原有列的配置
const
existingColumn
=
rawColumns
.
value
.
find
(
col
=>
col
.
key
===
columnKey
);
columns
.
push
({
title
:
key
,
key
:
columnKey
,
width
:
key
.
includes
(
'Time'
)
?
200
:
150
,
minWidth
:
100
,
ellipsis
:
existingColumn
?.
ellipsis
??
{
tooltip
:
true
},
resizable
:
true
,
sortable
:
existingColumn
?.
sortable
??
false
,
...(
existingColumn
?.
filter
?
{
filter
:
existingColumn
.
filter
,
filterOptions
:
existingColumn
.
filterOptions
,
filterOptionValues
:
existingColumn
.
filterOptionValues
}
:
{}),
render
:
key
.
includes
(
'Time'
)
||
key
.
includes
(
'Date'
)
?
(
row
:
any
)
=>
formatDate
(
row
[
key
])
:
undefined
,
sorter
:
(
row1
:
any
,
row2
:
any
)
=>
{
const
val1
=
row1
[
key
];
const
val2
=
row2
[
key
];
if
(
val1
>
val2
)
return
1
;
if
(
val1
<
val2
)
return
-
1
;
return
0
;
}
});
}
});
};
processField
(
data
);
// 更新 rawColumns,但保留原有的排序设置
rawColumns
.
value
=
columns
;
// 初始化 visibleColumns
if
(
visibleColumns
.
value
.
length
===
0
)
{
visibleColumns
.
value
=
columns
.
map
(
col
=>
col
.
key
);
}
return
columns
;
};
// 修改计算属性,根据 visibleColumns 过滤显示的列
const
columns
=
computed
(()
=>
{
return
rawColumns
.
value
.
filter
(
col
=>
visibleColumns
.
value
.
includes
(
col
.
key
))
.
map
(
col
=>
({
...
col
,
title
:
col
.
title
,
width
:
col
.
width
,
sortable
:
col
.
sortable
,
sorter
:
col
.
sortable
?
(
row1
:
any
,
row2
:
any
)
=>
{
const
val1
=
col
.
key
.
includes
(
'.'
)
?
row1
[
col
.
key
.
split
(
'.'
)[
0
]]?.[
col
.
key
.
split
(
'.'
)[
1
]]
:
row1
[
col
.
key
];
const
val2
=
col
.
key
.
includes
(
'.'
)
?
row2
[
col
.
key
.
split
(
'.'
)[
0
]]?.[
col
.
key
.
split
(
'.'
)[
1
]]
:
row2
[
col
.
key
];
// 处理数字类型
if
(
!
Number
.
isNaN
(
Number
(
val1
))
&&
!
Number
.
isNaN
(
Number
(
val2
)))
{
return
Number
(
val1
)
-
Number
(
val2
);
}
// 处理日期类型
if
(
col
.
key
.
includes
(
'Time'
)
||
col
.
key
.
includes
(
'Date'
))
{
// 使用formatDate函数格式化日期,然后进行比较
const
formattedDate1
=
formatDate
(
val1
);
const
formattedDate2
=
formatDate
(
val2
);
// 如果格式化后的日期相同,则使用原始时间戳比较
if
(
formattedDate1
===
formattedDate2
)
{
return
new
Date
(
val1
).
getTime
()
-
new
Date
(
val2
).
getTime
();
}
// 否则比较格式化后的日期字符串
return
formattedDate1
.
localeCompare
(
formattedDate2
);
}
// 处理字符串类型
return
String
(
val1
).
localeCompare
(
String
(
val2
));
}
:
undefined
,
// 处理筛选相关属性
...(
col
.
filter
?
{
filter
:
true
,
filterOptions
:
col
.
filterOptions
,
filterOptionValues
:
col
.
filterOptionValues
,
// 添加筛选变化处理函数
onFilterChange
:
(
values
:
string
[])
=>
{
const
columnIndex
=
rawColumns
.
value
.
findIndex
(
c
=>
c
.
key
===
col
.
key
);
if
(
columnIndex
!==
-
1
)
{
// 保存筛选值到列配置
rawColumns
.
value
[
columnIndex
].
filterOptionValues
=
values
.
length
?
values
:
null
;
// 生成筛选状态并触发表格刷新
handleFiltersChange
(
getFiltersState
());
}
}
}
:
{})
}));
});
// 添加 watch 以在 enableSingleSelect 变化时重新生成列
watch
(
enableSingleSelect
,
()
=>
{
if
(
tableData
.
value
.
length
>
0
)
{
rawColumns
.
value
=
generateColumns
(
tableData
.
value
[
0
]);
}
});
// 当前排序状态
const
currentSorter
=
ref
();
// 添加搜索表单的展开状态
const
isExpanded
=
ref
(
true
);
// 在 script setup 部分添加以下代码
const
searchFields
=
ref
<
SearchField
[]
>
([]);
// 在 script setup 部分添加以下代码
const
searchForm
=
ref
<
Record
<
string
,
any
>>
({});
// 修改 watch searchFields 的处理
watch
(
searchFields
,
newFields
=>
{
// 更新 searchForm 结构
const
newSearchForm
:
Record
<
string
,
any
>
=
{};
newFields
.
forEach
(
field
=>
{
// 根据不同类型设置不同的初始值,优先使用默认值
if
(
field
.
type
===
'date'
)
{
newSearchForm
[
field
.
key
]
=
field
.
defaultValue
||
null
;
}
else
if
(
field
.
type
===
'select'
)
{
newSearchForm
[
field
.
key
]
=
field
.
defaultValue
||
undefined
;
}
else
{
newSearchForm
[
field
.
key
]
=
field
.
defaultValue
||
''
;
}
});
searchForm
.
value
=
newSearchForm
;
},
{
deep
:
true
}
);
// 处理搜索
const
handleSearch
=
()
=>
{
pagination
.
value
.
page
=
1
;
fetchData
();
};
// 重置搜索表单
const
resetSearch
=
()
=>
{
// 只保留分页参数,清除其他所有搜索条件
const
newSearchForm
=
{
Skip
:
(
pagination
.
value
.
page
-
1
)
*
pagination
.
value
.
pageSize
,
Take
:
pagination
.
value
.
pageSize
};
searchForm
.
value
=
newSearchForm
;
handleSearch
();
};
// 添加 CreateDialog 的引用
const
createDialogRef
=
ref
();
// 添加一个标志来追踪配置是否已加载
const
isConfigLoaded
=
ref
(
false
);
// 添加 TableSettingsDrawer 的引用
const
tableSettingsDrawerRef
=
ref
();
// 添加筛选功能相关配置
const
newFilterLabel
=
ref
(
''
);
const
newFilterValue
=
ref
(
''
);
// 添加获取所有设置的方法
const
getAllTableSettings
=
async
()
=>
{
if
(
tableSettingsDrawerRef
.
value
)
{
await
tableSettingsDrawerRef
.
value
.
handleGetSettings
();
}
};
// 处理新建
const
handleCreate
=
(
row
?:
any
)
=>
{
if
(
row
)
{
// 编辑模式
// console.log('进入编辑模式', row);
// 打开编辑弹窗并传递当前行数据
createDialogRef
.
value
?.
open
(
row
);
}
else
{
// 新建模式
// console.log('进入新建模式');
// 打开新建弹窗
createDialogRef
.
value
?.
open
();
}
};
// 处理搜索参数
const
buildSearchParams
=
()
=>
{
const
searchParams
:
any
=
{
...
searchForm
.
value
,
Skip
:
(
pagination
.
value
.
page
-
1
)
*
pagination
.
value
.
pageSize
,
Take
:
pagination
.
value
.
pageSize
,
OrderBy
:
currentSorter
.
value
?
(
currentSorter
.
value
.
order
===
'descend'
?
'-'
:
''
)
+
currentSorter
.
value
.
columnKey
:
undefined
};
// 处理筛选参数
if
(
Object
.
keys
(
currentFilters
.
value
).
length
)
{
for
(
const
key
in
currentFilters
.
value
)
{
if
(
currentFilters
.
value
[
key
]
&&
Array
.
isArray
(
currentFilters
.
value
[
key
])
&&
currentFilters
.
value
[
key
].
length
>
0
)
{
searchParams
[
key
]
=
currentFilters
.
value
[
key
];
}
}
}
// 处理表单中的值为空或undefined的情况,创建新对象而不是删除属性
const
cleanedParams
:
any
=
{};
for
(
const
key
in
searchParams
)
{
if
(
searchParams
[
key
]
!==
''
&&
searchParams
[
key
]
!==
null
&&
searchParams
[
key
]
!==
undefined
)
{
cleanedParams
[
key
]
=
searchParams
[
key
];
}
}
Object
.
assign
(
searchParams
,
cleanedParams
);
// 清空原对象的其他属性
for
(
const
key
in
searchParams
)
{
if
(
!
(
key
in
cleanedParams
))
{
searchParams
[
key
]
=
undefined
;
}
}
// 处理日期范围
if
(
searchForm
.
value
.
下单时间
)
{
searchParams
.
下单时间开始
=
searchForm
.
value
.
下单时间
[
0
];
searchParams
.
下单时间结束
=
searchForm
.
value
.
下单时间
[
1
];
delete
searchParams
.
下单时间
;
}
return
searchParams
;
};
// 获取数据
async
function
fetchData
()
{
console
.
log
(
'🚀 fetchData 被调用,axios 可用:'
,
Boolean
(
axios
));
loading
.
value
=
true
;
try
{
const
searchParams
=
buildSearchParams
();
// 自定义axios请求配置,使用自定义参数序列化函数
console
.
log
(
'📡 发送请求到 /table.json'
);
const
response
=
await
axios
.
get
(
'/table.json'
,
{
params
:
searchParams
,
paramsSerializer
:
(
params
:
any
)
=>
{
// 自定义参数序列化,避免数组被转换为key[]=value格式
const
queryParams
:
string
[]
=
[];
for
(
const
key
in
params
)
{
if
(
params
[
key
]
!==
undefined
&&
params
[
key
]
!==
null
)
{
if
(
Array
.
isArray
(
params
[
key
]))
{
// 只在数组不为空时添加参数
if
(
params
[
key
].
length
>
0
)
{
// 数组类型使用JSON.stringify处理,并将参数名变为复数形式
queryParams
.
push
(
`
${
encodeURIComponent
(
`
${
key
}
s`
)}
=
${
encodeURIComponent
(
JSON
.
stringify
(
params
[
key
]))}
`
);
}
}
else
{
// 非数组类型参数保持原样
queryParams
.
push
(
`
${
encodeURIComponent
(
key
)}
=
${
encodeURIComponent
(
params
[
key
])}
`
);
}
}
}
return
queryParams
.
join
(
'&'
);
}
});
console
.
log
(
'✅ 请求成功,响应:'
,
response
.
status
,
response
.
data
?.
Results
?.
length
||
0
,
'条数据'
);
if
(
response
.
data
?.
Results
?.
length
)
{
// 保存当前的filter状态
const
currentFilterState
:
Record
<
string
,
{
filter
:
boolean
;
filterOptions
?:
{
label
:
string
;
value
:
string
}[];
filterOptionValues
?:
string
[]
|
null
;
}
>
=
rawColumns
.
value
.
reduce
((
acc
:
Record
<
string
,
any
>
,
col
)
=>
{
if
(
col
.
filter
&&
col
.
filterOptionValues
)
{
acc
[
col
.
key
]
=
{
filter
:
col
.
filter
,
filterOptions
:
col
.
filterOptions
,
filterOptionValues
:
col
.
filterOptionValues
};
}
return
acc
;
},
{});
// 先获取字段配置
let
fieldConfigs
=
[];
try
{
const
entityResponse
=
await
axios
.
get
(
`/Server/Entity/
${
props
.
uiConfig
.
Type
}
`
);
if
(
entityResponse
.
data
?.
Results
)
{
fieldConfigs
=
entityResponse
.
data
.
Results
;
}
}
catch
(
error
)
{
// console.error('获取字段配置失败:', error);
}
// 生成列配置时传入字段配置
const
generateColumnsWithConfig
=
(
data
:
any
,
configs
:
any
[]):
TableColumn
[]
=>
{
if
(
!
data
)
return
[];
const
newColumns
:
TableColumn
[]
=
[];
// 添加序号列
if
(
enableSingleSelect
.
value
)
{
newColumns
.
push
({
title
:
'序号'
,
key
:
'index'
,
width
:
60
,
minWidth
:
30
,
render
:
(
_
,
index
=
0
)
=>
index
+
1
+
(
pagination
.
value
.
page
-
1
)
*
pagination
.
value
.
pageSize
});
}
const
processField
=
(
obj
:
any
,
prefix
=
''
)
=>
{
Object
.
entries
(
obj
).
forEach
(([
key
,
value
])
=>
{
// 查找字段配置
const
fieldConfig
=
configs
.
find
(
field
=>
field
.
Name
===
key
);
const
displayName
=
fieldConfig
?.
DisplayName
||
key
;
// 处理数组类型字段
if
(
Array
.
isArray
(
value
))
{
const
columnKey
=
prefix
+
key
;
const
existingColumn
=
rawColumns
.
value
.
find
(
col
=>
col
.
key
===
columnKey
);
newColumns
.
push
({
title
:
displayName
,
key
:
columnKey
,
width
:
200
,
minWidth
:
150
,
ellipsis
:
existingColumn
?.
ellipsis
??
{
tooltip
:
true
},
resizable
:
true
,
sortable
:
false
,
render
:
(
row
:
any
)
=>
{
const
arrayValue
=
row
[
key
];
if
(
Array
.
isArray
(
arrayValue
))
{
return
arrayValue
.
map
(
item
=>
{
if
(
typeof
item
===
'object'
)
{
return
Object
.
entries
(
item
)
.
map
(([
k
,
v
])
=>
`
${
k
}
:
${
v
}
`
)
.
join
(
', '
);
}
return
String
(
item
);
})
.
join
(
'; '
);
}
return
''
;
}
});
}
// 处理嵌套对象
else
if
(
value
&&
typeof
value
===
'object'
&&
!
Array
.
isArray
(
value
))
{
Object
.
entries
(
value
as
object
).
forEach
(([
nestedKey
,
_
])
=>
{
const
columnKey
=
`
${
key
}
.
${
nestedKey
}
`
;
const
existingColumn
=
rawColumns
.
value
.
find
(
col
=>
col
.
key
===
columnKey
);
const
nestedFieldConfig
=
configs
.
find
(
field
=>
field
.
Name
===
nestedKey
);
const
nestedDisplayName
=
nestedFieldConfig
?.
DisplayName
||
nestedKey
;
newColumns
.
push
({
title
:
nestedDisplayName
,
key
:
columnKey
,
width
:
key
.
includes
(
'Time'
)
?
200
:
150
,
minWidth
:
100
,
ellipsis
:
existingColumn
?.
ellipsis
??
{
tooltip
:
true
},
resizable
:
true
,
sortable
:
existingColumn
?.
sortable
??
false
,
...(
existingColumn
?.
filter
?
{
filter
:
existingColumn
.
filter
,
filterOptions
:
existingColumn
.
filterOptions
,
filterOptionValues
:
existingColumn
.
filterOptionValues
}
:
{}),
render
:
key
.
includes
(
'Time'
)
||
key
.
includes
(
'Date'
)
?
(
row
:
any
)
=>
formatDate
(
row
[
key
]?.[
nestedKey
])
:
undefined
,
sorter
:
(
row1
:
any
,
row2
:
any
)
=>
{
const
val1
=
row1
[
key
]?.[
nestedKey
];
const
val2
=
row2
[
key
]?.[
nestedKey
];
if
(
val1
>
val2
)
return
1
;
if
(
val1
<
val2
)
return
-
1
;
return
0
;
}
});
});
}
// 处理普通字段
else
{
const
columnKey
=
prefix
+
key
;
const
existingColumn
=
rawColumns
.
value
.
find
(
col
=>
col
.
key
===
columnKey
);
newColumns
.
push
({
title
:
displayName
,
key
:
columnKey
,
width
:
key
.
includes
(
'Time'
)
?
200
:
150
,
minWidth
:
100
,
ellipsis
:
existingColumn
?.
ellipsis
??
{
tooltip
:
true
},
resizable
:
true
,
sortable
:
existingColumn
?.
sortable
??
false
,
...(
existingColumn
?.
filter
?
{
filter
:
existingColumn
.
filter
,
filterOptions
:
existingColumn
.
filterOptions
,
filterOptionValues
:
existingColumn
.
filterOptionValues
}
:
{}),
render
:
key
.
includes
(
'Time'
)
||
key
.
includes
(
'Date'
)
?
(
row
:
any
)
=>
formatDate
(
row
[
key
])
:
undefined
,
sorter
:
(
row1
:
any
,
row2
:
any
)
=>
{
const
val1
=
row1
[
key
];
const
val2
=
row2
[
key
];
if
(
val1
>
val2
)
return
1
;
if
(
val1
<
val2
)
return
-
1
;
return
0
;
}
});
}
});
};
processField
(
data
);
return
newColumns
;
};
// 使用新的生成函数生成列配置
rawColumns
.
value
=
generateColumnsWithConfig
(
response
.
data
.
Results
[
0
],
fieldConfigs
);
// 确保所有列在rawColumns更新后默认可见
visibleColumns
.
value
=
rawColumns
.
value
.
map
(
col
=>
col
.
key
);
// 恢复之前的筛选状态
if
(
Object
.
keys
(
currentFilterState
).
length
>
0
)
{
rawColumns
.
value
.
forEach
(
col
=>
{
const
savedState
=
currentFilterState
[
col
.
key
];
if
(
savedState
)
{
col
.
filter
=
savedState
.
filter
;
col
.
filterOptions
=
savedState
.
filterOptions
;
col
.
filterOptionValues
=
savedState
.
filterOptionValues
;
}
});
}
tableData
.
value
=
response
.
data
.
Results
;
pagination
.
value
.
itemCount
=
response
.
data
.
Total
||
0
;
// 移除多余的判断,简化配置加载逻辑
if
(
!
isConfigLoaded
.
value
)
{
// console.log('首次加载配置');
isConfigLoaded
.
value
=
true
;
nextTick
(()
=>
{
getAllTableSettings
();
});
}
}
else
{
// 如果没有数据,清空表格数据并设置总数为0
tableData
.
value
=
[];
pagination
.
value
.
itemCount
=
response
.
data
?.
Total
||
0
;
}
}
catch
(
error
)
{
console
.
error
(
'❌ 请求失败:'
,
error
);
tableData
.
value
=
[];
pagination
.
value
.
itemCount
=
0
;
}
finally
{
loading
.
value
=
false
;
}
}
// 添加删除后的数据刷新处理方法
const
handleDeleteRefresh
=
async
()
=>
{
// 记录删除前的数据条数
const
beforeDeleteCount
=
tableData
.
value
.
length
;
const
currentPage
=
pagination
.
value
.
page
;
// 先刷新数据获取最新的总数
await
fetchData
();
// 如果当前页没有数据了,且不是第一页,则跳转到前一页
if
(
tableData
.
value
.
length
===
0
&&
currentPage
>
1
)
{
// console.log('当前页无数据,跳转到前一页');
pagination
.
value
.
page
=
currentPage
-
1
;
await
fetchData
();
}
// 如果删除后当前页还有数据,但数据条数减少了,说明删除成功
else
if
(
beforeDeleteCount
>
tableData
.
value
.
length
||
tableData
.
value
.
length
===
0
)
{
// console.log('删除成功,数据已更新');
}
};
// 页码改变
const
handlePageChange
=
(
page
:
number
)
=>
{
pagination
.
value
.
page
=
page
;
fetchData
();
};
// 每页条数改变
const
handlePageSizeChange
=
(
pageSize
:
number
)
=>
{
pagination
.
value
.
pageSize
=
pageSize
;
pagination
.
value
.
page
=
1
;
fetchData
();
};
// 排序改变
const
handleSorterChange
=
(
sorter
:
{
columnKey
:
string
;
order
:
'ascend'
|
'descend'
|
false
})
=>
{
currentSorter
.
value
=
sorter
;
fetchData
();
};
const
showSettingDrawer
=
ref
(
false
);
const
tableHeight
=
ref
(
600
);
const
tableScrollWidth
=
ref
(
0
);
// 设置一个默认值
// 添加列配置相关的响应式变量
const
showEditDrawer
=
ref
(
false
);
const
currentEditColumn
=
ref
<
TableColumn
|
null
>
(
null
);
// 修改拖拽排序相关的方法
const
handleDragStart
=
(
e
:
DragEvent
,
index
:
number
)
=>
{
e
.
dataTransfer
?.
setData
(
'text/plain'
,
index
.
toString
());
};
const
handleDragOver
=
(
e
:
DragEvent
)
=>
{
e
.
preventDefault
();
};
const
handleDrop
=
(
e
:
DragEvent
,
toIndex
:
number
)
=>
{
e
.
preventDefault
();
const
fromIndex
=
Number
(
e
.
dataTransfer
?.
getData
(
'text/plain'
));
if
(
!
Number
.
isNaN
(
fromIndex
))
{
const
reorderedColumns
=
[...
rawColumns
.
value
];
const
[
movedColumn
]
=
reorderedColumns
.
splice
(
fromIndex
,
1
);
reorderedColumns
.
splice
(
toIndex
,
0
,
movedColumn
);
rawColumns
.
value
=
reorderedColumns
;
}
};
// 添加计算属性判断是否为 index 字段
const
isIndexColumn
=
computed
(()
=>
{
if
(
!
currentEditColumn
.
value
)
return
false
;
return
currentEditColumn
.
value
.
key
===
'index'
||
currentEditColumn
.
value
.
key
.
toLowerCase
().
includes
(
'index'
);
});
// 修改编辑列的方法
const
handleEditColumn
=
(
column
:
TableColumn
)
=>
{
currentEditColumn
.
value
=
{
...
column
,
align
:
(
column
as
any
).
align
||
'left'
,
titleAlign
:
(
column
as
any
).
titleAlign
||
'left'
,
sortable
:
isIndexColumn
.
value
?
false
:
column
.
sortable
||
false
,
// 保持原有的 ellipsis 配置,如果没有则不设置
...(
column
.
ellipsis
?
{
ellipsis
:
{
...
column
.
ellipsis
}
}
:
{}),
// 保持原有的筛选配置
...(
column
.
filter
?
{
filter
:
true
,
filterOptions
:
[...(
column
.
filterOptions
||
[])]
}
:
{})
};
showEditDrawer
.
value
=
true
;
// 重置添加筛选选项输入框
newFilterLabel
.
value
=
''
;
newFilterValue
.
value
=
''
;
};
// 修改 saveColumnEdit 方法
const
saveColumnEdit
=
()
=>
{
if
(
!
currentEditColumn
.
value
)
return
;
const
index
=
rawColumns
.
value
.
findIndex
(
col
=>
col
.
key
===
currentEditColumn
.
value
?.
key
);
if
(
index
>
-
1
)
{
const
updatedColumn
=
{
...
rawColumns
.
value
[
index
],
title
:
currentEditColumn
.
value
.
title
,
align
:
currentEditColumn
.
value
.
align
,
titleAlign
:
currentEditColumn
.
value
.
titleAlign
,
sortable
:
currentEditColumn
.
value
.
sortable
};
// 只在存在 ellipsis 时添加该属性
if
(
currentEditColumn
.
value
.
ellipsis
)
{
updatedColumn
.
ellipsis
=
{
tooltip
:
currentEditColumn
.
value
.
ellipsis
.
tooltip
};
}
else
{
// 确保删除 ellipsis 属性
delete
updatedColumn
.
ellipsis
;
}
// 保存筛选相关设置
if
(
currentEditColumn
.
value
.
filter
)
{
updatedColumn
.
filter
=
true
;
updatedColumn
.
filterOptions
=
currentEditColumn
.
value
.
filterOptions
||
[];
// 添加筛选处理函数
updatedColumn
.
filterOptionValues
=
null
;
// 初始时没有选中的筛选值
}
else
{
// 删除筛选相关属性
delete
updatedColumn
.
filter
;
delete
updatedColumn
.
filterOptions
;
delete
updatedColumn
.
filterOptionValues
;
}
rawColumns
.
value
[
index
]
=
updatedColumn
;
}
showEditDrawer
.
value
=
false
;
currentEditColumn
.
value
=
null
;
};
// 添加列显示状态变化的处理方法
const
handleColumnVisibleChange
=
(
checked
:
boolean
,
column
:
TableColumn
)
=>
{
if
(
checked
)
{
if
(
!
visibleColumns
.
value
.
includes
(
column
.
key
))
{
visibleColumns
.
value
=
[...
visibleColumns
.
value
,
column
.
key
];
}
}
else
{
visibleColumns
.
value
=
visibleColumns
.
value
.
filter
(
key
=>
key
!==
column
.
key
);
}
};
// 添加列搜索相关的响应式变量
const
columnSearchText
=
ref
(
''
);
// 添加列搜索的计算属性
const
filteredColumns
=
computed
(()
=>
{
const
searchText
=
columnSearchText
.
value
.
toLowerCase
().
trim
();
if
(
!
searchText
)
return
rawColumns
.
value
;
return
rawColumns
.
value
.
filter
(
column
=>
{
const
title
=
column
.
title
.
toLowerCase
();
const
key
=
column
.
key
.
toLowerCase
();
return
title
.
includes
(
searchText
)
||
key
.
includes
(
searchText
);
});
});
// 添加搜索框清空方法
const
clearColumnSearch
=
()
=>
{
columnSearchText
.
value
=
''
;
};
// 在 script setup 部分添加以下代码
const
formItems
=
computed
(()
=>
{
const
items
=
searchFields
.
value
.
filter
(
field
=>
field
.
visible
)
.
sort
((
a
,
b
)
=>
a
.
order
-
b
.
order
)
.
map
(
field
=>
({
label
:
field
.
label
,
key
:
field
.
key
,
type
:
field
.
type
,
// 使用字段自身的 placeholder,如果没有则使用默认值
placeholder
:
field
.
placeholder
||
(
field
.
type
===
'select'
?
`请选择
${
field
.
label
}
`
:
`请输入
${
field
.
label
}
`
),
defaultValue
:
field
.
defaultValue
,
// 添加默认值
options
:
field
.
options
}));
return
items
;
});
// 修改添加搜索字段的方法
const
showAddFieldDrawer
=
ref
(
false
);
const
selectedColumns
=
ref
<
string
[]
>
([]);
const
confirmAddFields
=
()
=>
{
const
newFields
=
selectedColumns
.
value
.
map
(
key
=>
{
const
column
=
rawColumns
.
value
.
find
(
col
=>
col
.
key
===
key
);
const
isDateField
=
column
?.
key
.
toLowerCase
().
includes
(
'time'
)
||
column
?.
key
.
toLowerCase
().
includes
(
'date'
);
return
{
label
:
column
?.
title
||
''
,
key
:
column
?.
key
||
''
,
type
:
isDateField
?
'date'
:
'input'
,
visible
:
true
,
defaultValue
:
''
,
placeholder
:
isDateField
?
`请选择
${
column
?.
title
||
''
}
` : `
请输入
$
{
column
?.
title
||
''
}
`,
order: searchFields.value.length + 1,
options: []
};
});
searchFields.value = [...searchFields.value, ...newFields];
showAddFieldDrawer.value = false;
};
// 添加搜索字段编辑抽屉
const showSearchFieldDrawer = ref(false);
const currentSearchField = ref<SearchField>({
label: '',
key: '',
type: 'input',
visible: true,
defaultValue: '',
placeholder: '请输入占位提示',
options: [],
order: 0
});
// 修改 saveSearchField 方法
const saveSearchField = () => {
const index = searchFields.value.findIndex(field => field.key === currentSearchField.value.key);
if (index > -1) {
const oldType = searchFields.value[index].type;
const newType = currentSearchField.value.type;
searchFields.value[index] = {
...searchFields.value[index],
...currentSearchField.value,
placeholder: currentSearchField.value.placeholder,
options: currentSearchField.value.type === 'select' ? currentSearchField.value.options : []
};
// 如果类型改变为 select,重置对应的表单值为 undefined
if (oldType !== 'select' && newType === 'select') {
searchForm.value[currentSearchField.value.key] = undefined;
}
}
showSearchFieldDrawer.value = false;
};
// 添加搜索字段编辑方法
const addSearchField = () => {
selectedColumns.value = [];
showAddFieldDrawer.value = true;
};
// 编辑搜索字段
const editSearchField = (field: any) => {
currentSearchField.value = {
...field,
options: field.options || [] // 确保 options 属性存在
};
showSearchFieldDrawer.value = true;
};
// 移除搜索字段
const removeSearchField = (field: any) => {
searchFields.value = searchFields.value.filter(f => f.key !== field.key);
};
// 更新字段可见性
const updateFieldVisibility = (field: any, visible: boolean) => {
field.visible = visible;
};
// 获取可选择的列
const availableColumns = computed(() => {
const existingKeys = searchFields.value.map(f => f.key);
return rawColumns.value.filter(col => !existingKeys.includes(col.key));
});
// 在 script setup 部分添加以下响应式变量
const newOptionKey = ref('');
const newOptionValue = ref('');
// 添加一个方法来处理添加新选项
const addOption = () => {
if (!currentSearchField.value.options) {
currentSearchField.value.options = [];
}
if (newOptionKey.value && newOptionValue.value) {
currentSearchField.value.options.push({
label: newOptionKey.value,
value: newOptionValue.value
});
// 清空输入
newOptionKey.value = '';
newOptionValue.value = '';
}
};
// 添加删除选项的方法
const removeOption = (index: number) => {
if (currentSearchField.value.options) {
currentSearchField.value.options.splice(index, 1);
}
};
// 添加拖拽排序相关方法
const handleSearchFieldDragStart = (e: DragEvent, field: any) => {
e.dataTransfer?.setData('text/plain', field.key);
};
const handleSearchFieldDragOver = (e: DragEvent) => {
e.preventDefault();
};
const handleSearchFieldDrop = (e: DragEvent, targetField: any) => {
e.preventDefault();
const sourceKey = e.dataTransfer?.getData('text/plain');
if (!sourceKey) return;
const sourceField = searchFields.value.find(f => f.key === sourceKey);
const targetOrder = targetField.order;
if (sourceField) {
// 交换order值
const sourceOrder = sourceField.order;
sourceField.order = targetOrder;
targetField.order = sourceOrder;
// 重新排序数组
searchFields.value.sort((a, b) => a.order - b.order);
}
};
// 添加筛选选项
const addFilterOption = () => {
if (!currentEditColumn.value) return;
if (!currentEditColumn.value.filterOptions) {
currentEditColumn.value.filterOptions = [];
}
// 检查是否已存在相同的值,避免重复
const isDuplicate = currentEditColumn.value.filterOptions.some(option => option.value === newFilterValue.value);
if (!isDuplicate) {
currentEditColumn.value.filterOptions.push({
label: newFilterLabel.value,
value: newFilterValue.value
});
// 清空输入
newFilterLabel.value = '';
newFilterValue.value = '';
} else {
// 可以在这里添加重复选项的提示
// alert('该筛选值已存在,请勿重复添加');
}
};
// 移除筛选选项
const removeFilterOption = (index: number) => {
if (currentEditColumn.value?.filterOptions) {
currentEditColumn.value.filterOptions.splice(index, 1);
}
};
// 从当前数据生成选项
const generateFilterOptions = (column: TableColumn) => {
if (!column || !tableData.value.length) return;
// 获取当前列的所有唯一值
const uniqueValues = new Set<string>();
const key = column.key;
tableData.value.forEach(row => {
let value;
// 处理嵌套属性 (如 'user.name')
if (key.includes('.')) {
const [parentKey, childKey] = key.split('.');
value = row[parentKey]?.[childKey];
} else {
value = row[key];
}
// 值存在时添加到集合中
if (value !== undefined && value !== null) {
uniqueValues.add(String(value));
}
});
// 将唯一值转换为筛选选项
const options = Array.from(uniqueValues).map(value => ({
label: value,
value
}));
// 更新当前编辑列的筛选选项
if (currentEditColumn.value) {
currentEditColumn.value.filterOptions = options;
}
// 提供用户反馈
if (options.length > 0) {
// alert(`
成功生成
$
{
options
.
length
}
个筛选选项
!
`);
} else {
// alert('未能从当前数据中找到任何值,请检查数据是否已加载。');
}
};
// 数据转换相关的状态
const selectedField = ref('');
const availableFields = computed(() => {
return rawColumns.value.map((column: TableColumn) => ({
label: column.title,
value: column.key
}));
});
const tagMappings = ref<TagMapping[]>([{ value: '', label: '', color: '' }]);
const transformRules = ref<TransformRule[]>([]);
const transformedData = ref<any[]>([]);
// 添加转换配置存储
const transformConfigurations = ref<Record<string, any>>({});
// 添加获取标签颜色的辅助函数
const getTagColor = (color: string) => {
const colorMap: Record<string, string> = {
default: '#909399',
primary: '#409EFF',
success: '#67C23A',
warning: '#E6A23C',
error: '#F56C6C',
info: '#909399'
};
return colorMap[color] || colorMap.default;
};
// 添加更新列渲染函数的方法
const updateTransformedColumnRenders = () => {
// 遍历所有列,为有转换配置的列添加自定义渲染函数
rawColumns.value.forEach(column => {
const config = transformConfigurations.value[column.key];
if (config && config.type === 'tag') {
// 备份原有的render函数(如果有)
if (!column.__originalRender && column.render) {
column.__originalRender = column.render;
}
// 添加新的render函数,应用标签转换
column.render = (row, index) => {
let value;
// 处理嵌套字段
if (column.key.includes('.')) {
const [parentKey, childKey] = column.key.split('.');
value = row[parentKey]?.[childKey];
} else {
value = row[column.key];
}
if (value !== undefined && value !== null) {
const stringValue = String(value);
const mapping = config.mappings.find((m: any) => String(m.value) === stringValue);
if (mapping) {
// 返回标签组件
return h(
'span',
{
class: `
tag
tag
-
$
{
mapping
.
color
||
'default'
}
`,
style: {
padding: '2px 6px',
borderRadius: '4px',
backgroundColor: getTagColor(mapping.color),
color: '#fff',
fontSize: '12px'
}
},
mapping.label
);
}
}
// 如果没有匹配的映射或值不存在,使用原始渲染或值
if (column.__originalRender) {
return column.__originalRender(row, index);
}
return value;
};
} else if (!config && column.__originalRender) {
// 如果列没有转换配置但有原始渲染函数,恢复原始渲染
column.render = column.__originalRender;
column.__originalRender = undefined;
}
});
};
const applyTransformations = () => {
if (!tableData.value || tableData.value.length === 0) {
window.$message?.error('没有可转换的数据');
return;
}
try {
// 不修改原始数据,只提供渲染函数
// console.log('应用数据转换配置');
// console.log('转换配置:', transformConfigurations.value);
// 刷新表格视图
updateTransformedColumnRenders();
// window.$message?.success('数据转换成功应用到显示');
} catch (error: any) {
// console.error('转换过程中出错:', error);
window.$message?.error(`
转换过程中出错
:
$
{
error
.
message
}
`);
}
};
// 数据转换相关的方法
const addTagMapping = () => {
tagMappings.value.push({ value: '', label: '', color: '' });
};
const removeTagMapping = (index: number) => {
tagMappings.value.splice(index, 1);
};
const addTransformRule = () => {
if (!selectedField.value) return;
// 过滤掉空值映射
const validMappings = tagMappings.value.filter(mapping => mapping.value !== '' && mapping.label !== '');
if (validMappings.length === 0) {
window.$message?.error('请添加至少一个有效的标记映射');
return;
}
// 将转换规则添加到规则列表
transformRules.value.push({
field: selectedField.value,
fieldLabel: availableFields.value.find(f => f.value === selectedField.value)?.label || selectedField.value,
type: 'tag',
typeLabel: '转换标记',
params: { mappings: validMappings }
});
// 同时更新转换配置存储
transformConfigurations.value[selectedField.value] = {
type: 'tag',
mappings: validMappings
};
// 重置输入
selectedField.value = '';
tagMappings.value = [{ value: '', label: '', color: '' }];
};
const removeTransformRule = (index: number) => {
// 获取要删除的规则
const removedRule = transformRules.value[index];
// 从规则列表中删除
transformRules.value.splice(index, 1);
// 从转换配置中删除
if (removedRule && removedRule.field) {
transformConfigurations.value[removedRule.field] = undefined;
}
// 刷新表格以移除转换效果
applyTransformations();
};
// 添加恢复原始数据的方法
const restoreOriginalData = () => {
// 恢复所有列的原始渲染函数
rawColumns.value.forEach(column => {
if ((column as any).__originalRender) {
column.render = (column as any).__originalRender;
(column as any).__originalRender = undefined;
}
});
// 清空转换配置
transformConfigurations.value = {};
transformRules.value = [];
window.$message?.success('已恢复原始数据显示');
};
// 添加一个标志来追踪数据是否已经准备好
const isDataReady = ref(false);
// 监听数据变化
watch([tableData, rawColumns], ([newTableData, newRawColumns]) => {
if (newTableData.length > 0 && newRawColumns.length > 0) {
// console.log('数据已准备好');
isDataReady.value = true;
}
});
// 监听数据准备状态 - 不再自动加载配置
watch(isDataReady, newValue => {
if (newValue) {
// console.log('数据已准备就绪');
// 移除这里的配置加载,避免重复加载
// loadTableSettings();
}
});
onMounted(() => {
// console.log(props.uiConfig);
console.log('🔧 检查全局工具库:', {
axios: Boolean((window as any).$axios),
echarts: Boolean((window as any).$echarts),
WangEditor: Boolean((window as any).$WangEditor)
});
// 在组件挂载时就确保配置加载完成
fetchData();
Promise.resolve()
.then(() => fetchData())
.then(() => {
nextTick(() => {
// console.log('组件挂载完成,初始化配置');
// getAllTableSettings();
});
});
});
onActivated(() => {
// console.log('组件被激活');
fetchData();
});
// 提供共享状态和方法给子组件
provide('tableState', {
tableData,
loading,
columns,
pagination,
rawColumns,
visibleColumns,
tableSize,
showBorder,
showStripe,
tableHeight,
tableScrollWidth,
searchForm,
searchFields,
formItems,
isExpanded,
enableSingleSelect,
filteredColumns,
showSettingDrawer,
showEditDrawer,
showSearchFieldDrawer,
showAddFieldDrawer,
currentEditColumn,
currentSearchField,
activeTab,
columnSearchText,
selectedColumns,
newFilterLabel,
newFilterValue,
newOptionKey,
newOptionValue,
isIndexColumn,
availableColumns,
uiConfig: props.uiConfig,
tagMappings,
transformRules,
transformedData,
selectedField,
availableFields,
transformConfigurations
});
// 提供方法
provide('tableMethods', {
fetchData,
handleSearch,
resetSearch,
handlePageChange,
handlePageSizeChange,
handleSorterChange,
handleFiltersChange,
handleEditColumn,
saveColumnEdit,
handleColumnVisibleChange,
clearColumnSearch,
handleDragStart,
handleDragOver,
handleDrop,
addSearchField,
editSearchField,
removeSearchField,
updateFieldVisibility,
confirmAddFields,
saveSearchField,
addOption,
removeOption,
handleSearchFieldDragStart,
handleSearchFieldDragOver,
handleSearchFieldDrop,
addFilterOption,
removeFilterOption,
generateFilterOptions,
formatDate,
addTagMapping,
removeTagMapping,
addTransformRule,
removeTransformRule,
applyTransformations,
restoreOriginalData,
updateTransformedColumnRenders,
handleCreate,
handleDeleteRefresh
});
</
script
>
<
template
>
<div
class=
"table-container"
>
<!-- 搜索表单组件 -->
<SearchForm></SearchForm>
<!-- 数据表格组件 -->
<DataTable></DataTable>
<!-- 表格设置抽屉组件 -->
<TableSettingsDrawer
ref=
"tableSettingsDrawerRef"
></TableSettingsDrawer>
<!-- 列编辑抽屉 -->
<ColumnEditDrawer></ColumnEditDrawer>
<!-- 搜索字段编辑抽屉 -->
<SearchFieldEditDrawer></SearchFieldEditDrawer>
<!-- 添加字段抽屉 -->
<AddFieldDrawer></AddFieldDrawer>
<!-- 新建弹窗 -->
<CreateDialog
ref=
"createDialogRef"
></CreateDialog>
</div>
</
template
>
<
style
scoped
>
.table-container
{
width
:
100%
;
position
:
relative
;
}
</
style
>
table.json
0 → 100644
View file @
8516388e
{
"Offset"
:
0
,
"Total"
:
10
,
"Results"
:
[
{
"OwnerName"
:
"222"
,
"Name"
:
"333"
,
"SerialNumber"
:
"1111"
,
"Type"
:
"Pos"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"EF1D786D-06F7-43F5-A685-8E13AE0F33BF"
,
"CreateTime"
:
"2025-05-07T14:30:22.0000000+08:00"
,
"UpdateTime"
:
"2025-05-13T16:59:38.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"xxx"
,
"Name"
:
"xxx"
,
"SerialNumber"
:
"xxxx"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"USD"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"66B88D4E-A1B9-4E3C-922B-DD5211BA3FB5"
,
"CreateTime"
:
"2025-05-07T14:26:34.0000000+08:00"
,
"UpdateTime"
:
"2025-05-13T16:57:05.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"1111"
,
"Name"
:
"222"
,
"SerialNumber"
:
"333"
,
"Type"
:
"Pos"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"97F8DA3B-06AC-4C50-ABEB-430F58B3DD64"
,
"CreateTime"
:
"2025-05-07T14:30:08.0000000+08:00"
,
"UpdateTime"
:
"2025-05-07T14:30:08.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"11"
,
"Name"
:
"222"
,
"SerialNumber"
:
"3"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"USD"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"CC255F3E-9F80-4002-B68C-53C5FF86AE52"
,
"CreateTime"
:
"2025-05-07T14:29:49.0000000+08:00"
,
"UpdateTime"
:
"2025-05-07T14:29:49.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"22"
,
"Name"
:
"222"
,
"SerialNumber"
:
"33"
,
"Type"
:
"AliPay"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"99C366A3-5867-43D5-B22D-B71E970D9A3C"
,
"CreateTime"
:
"2025-05-07T14:29:33.0000000+08:00"
,
"UpdateTime"
:
"2025-05-07T14:29:33.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"xxx"
,
"Name"
:
"111"
,
"SerialNumber"
:
"2222"
,
"Type"
:
"Bank"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"F2475ECC-55BD-49FE-BC00-8822DC846739"
,
"CreateTime"
:
"2025-05-07T14:29:00.0000000+08:00"
,
"UpdateTime"
:
"2025-05-07T14:29:00.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"7275275"
,
"Name"
:
"4274272"
,
"SerialNumber"
:
"41042277"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"0C38277C-7BA6-4E9D-BEF5-D43DB49B5EA1"
,
"CreateTime"
:
"2025-03-27T14:47:54.0000000+08:00"
,
"UpdateTime"
:
"2025-03-27T14:47:54.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"12312322"
,
"Name"
:
"32131"
,
"SerialNumber"
:
"2231123"
,
"Type"
:
"Pos"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"2A42BDFE-CDED-4300-ADF3-020D1B3E0138"
,
"CreateTime"
:
"2025-03-27T14:33:13.0000000+08:00"
,
"UpdateTime"
:
"2025-03-27T14:33:13.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"123123"
,
"Name"
:
"321321"
,
"SerialNumber"
:
"123132312312"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"095872A0-513C-4834-A0C5-BA173F5189A4"
,
"CreateTime"
:
"2025-03-27T14:29:36.0000000+08:00"
,
"UpdateTime"
:
"2025-03-27T14:29:36.0000000+08:00"
,
"Status"
:
0
},
{
"OwnerName"
:
"12"
,
"Name"
:
"1212"
,
"SerialNumber"
:
"12121211"
,
"Type"
:
"Cash"
,
"CreditLine"
:
0
,
"Amount"
:
0
,
"Currency"
:
"CNY"
,
"Summary"
:
""
,
"Remark"
:
""
,
"CreatorKvid"
:
"7F5918CC-E15F-4D7A-B636-1B40A905B744"
,
"CreatorName"
:
"高源"
,
"OrganizationKvid"
:
"CC7B3DF9-1B95-46DF-83C5-150587E29612"
,
"Metadata"
:
{},
"Kvid"
:
"40D1968B-70A1-49D5-89FD-39943A080D0A"
,
"CreateTime"
:
"2025-03-27T14:17:12.0000000+08:00"
,
"UpdateTime"
:
"2025-03-27T14:17:12.0000000+08:00"
,
"Status"
:
0
}
]
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment