@xizs/nuxt-antui 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/global/FormTable.tsx +53 -37
- package/app/components/global/FormTable2.tsx +312 -0
- package/app/components/layout/Base.vue +52 -40
- package/app/components/layout/Menu.vue +2 -2
- package/app/composables/menu.ts +56 -0
- package/app/composables/table.ts +34 -0
- package/nuxt.config.ts +7 -0
- package/package.json +1 -1
- package/tsconfig.json +1 -1
- package/app/components/global/TablePage.tsx +0 -15
|
@@ -14,8 +14,8 @@ type TableFormItemType = {
|
|
|
14
14
|
export type TablePropsType = {
|
|
15
15
|
form?: TableFormItemType[],
|
|
16
16
|
formOptions: {
|
|
17
|
-
search
|
|
18
|
-
reset
|
|
17
|
+
search?: boolean,
|
|
18
|
+
reset?: boolean,
|
|
19
19
|
export: boolean,
|
|
20
20
|
},
|
|
21
21
|
table: {
|
|
@@ -50,15 +50,16 @@ let dateRange = defineComponent({
|
|
|
50
50
|
item:{
|
|
51
51
|
type:Object as ()=>TableFormItemType,
|
|
52
52
|
required:true
|
|
53
|
-
}
|
|
53
|
+
},
|
|
54
|
+
|
|
54
55
|
},
|
|
55
56
|
setup(props) {
|
|
56
57
|
let {t:$t} = useI18n()
|
|
57
58
|
|
|
58
59
|
let value = watchRef(ref([]),(nv,ov)=>{
|
|
59
60
|
if(nv?.length>0){
|
|
60
|
-
props.form[props.item.key[0] as string] = nv[0].format('YYYY-MM-DD
|
|
61
|
-
props.form[props.item.key[1] as string] = nv[1].format('YYYY-MM-DD
|
|
61
|
+
props.form[props.item.key[0] as string] = dayjs(nv[0]).format('YYYY-MM-DD')
|
|
62
|
+
props.form[props.item.key[1] as string] = dayjs(nv[1]).format('YYYY-MM-DD')
|
|
62
63
|
}else{
|
|
63
64
|
props.form[props.item.key[0] as string] = ''
|
|
64
65
|
props.form[props.item.key[1] as string] = ''
|
|
@@ -66,8 +67,9 @@ let dateRange = defineComponent({
|
|
|
66
67
|
})
|
|
67
68
|
watch([() => props.form[props.item.key[0] as string],()=>props.form[props.item.key[1] as string]], (nv) => {
|
|
68
69
|
console.log(nv)
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
if (nv[0] == '') {
|
|
71
|
+
value.value = []
|
|
72
|
+
}
|
|
71
73
|
})
|
|
72
74
|
const rangePresets = ref([
|
|
73
75
|
{ label: $t('今天'), value: [dayjs(), dayjs()] },
|
|
@@ -88,7 +90,7 @@ const FormItems:Record<string, (form: any, item: TableFormItemType) => any> = {
|
|
|
88
90
|
},
|
|
89
91
|
select: (form: any, item: TableFormItemType) => {
|
|
90
92
|
let {t:$t} = useI18n()
|
|
91
|
-
return <ASelect allowClear
|
|
93
|
+
return <ASelect allowClear {...item.bind} class="min-w-[150px]" v-model:value={form[item.key]} options={item.bind?.options} placeholder={$t(`请选择{label}`,{label:$t(item.label)})}></ASelect>
|
|
92
94
|
},
|
|
93
95
|
selectTenant: (form: any, item: TableFormItemType) => {
|
|
94
96
|
let { t: $t } = useI18n()
|
|
@@ -144,7 +146,11 @@ export const FormTableProps = {
|
|
|
144
146
|
type:Object as () => any,
|
|
145
147
|
required:true
|
|
146
148
|
},
|
|
147
|
-
|
|
149
|
+
isInitLoad: {
|
|
150
|
+
type: Boolean,
|
|
151
|
+
default: true
|
|
152
|
+
}
|
|
153
|
+
}
|
|
148
154
|
|
|
149
155
|
export default defineComponent({
|
|
150
156
|
name: 'TableLayout',
|
|
@@ -180,7 +186,13 @@ export default defineComponent({
|
|
|
180
186
|
})
|
|
181
187
|
|
|
182
188
|
|
|
183
|
-
|
|
189
|
+
let _isInitLoad = props.isInitLoad
|
|
190
|
+
const tableData = asyncReactive<any[]>(async () => {
|
|
191
|
+
//初始化不加载数据
|
|
192
|
+
if (!_isInitLoad) {
|
|
193
|
+
_isInitLoad = true
|
|
194
|
+
return []
|
|
195
|
+
}
|
|
184
196
|
let res:any = await props.table.data({...form.value,...pagination})
|
|
185
197
|
if(Array.isArray(res)){
|
|
186
198
|
res = {
|
|
@@ -194,6 +206,7 @@ export default defineComponent({
|
|
|
194
206
|
}
|
|
195
207
|
}
|
|
196
208
|
}
|
|
209
|
+
console.log(res)
|
|
197
210
|
pagination.total = res.meta.pagination.total
|
|
198
211
|
pagination.pageSize = res.meta.pagination.per_page
|
|
199
212
|
pagination.page = res.meta.pagination.current_page
|
|
@@ -231,33 +244,36 @@ export default defineComponent({
|
|
|
231
244
|
</div>
|
|
232
245
|
</AForm>}
|
|
233
246
|
{slots.center?.()}
|
|
234
|
-
<div class="flex-1 overflow-scroll flex flex-col">
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
247
|
+
<div class="flex-1 relative overflow-scroll flex flex-col">
|
|
248
|
+
<div class="absolute inset-0 ">
|
|
249
|
+
{slots.content?.(tableData.value) ??
|
|
250
|
+
<ATable
|
|
251
|
+
row-selection={props.table.enableSelection ? rowSelection : null}
|
|
252
|
+
rowKey={props.table.rowKey ?? ((row) => row.id)}
|
|
253
|
+
loading={tableData.loading}
|
|
254
|
+
scroll={{ x: true,}}
|
|
255
|
+
columns={props.table.columns}
|
|
256
|
+
pagination={false}
|
|
257
|
+
sticky={tableData.value.length>0}
|
|
258
|
+
|
|
259
|
+
v-slots={{
|
|
260
|
+
headerCell: ({ title, column }: any) => (
|
|
261
|
+
<div style={{ 'white-space': 'nowrap' }}>
|
|
262
|
+
{title}
|
|
263
|
+
</div>
|
|
264
|
+
),
|
|
265
|
+
bodyCell: (row: any) => {
|
|
266
|
+
if (row.column?.customRender != null) {
|
|
267
|
+
return row.column.customRender(row)
|
|
268
|
+
}
|
|
269
|
+
return <div class="whitespace-nowrap" style={{ width: row.column.width ?? '100px' }}>{row.text}</div>
|
|
270
|
+
},
|
|
271
|
+
...props.table['v-slots']
|
|
272
|
+
}}
|
|
273
|
+
dataSource={tableData.value}
|
|
274
|
+
/>
|
|
275
|
+
}
|
|
276
|
+
</div>
|
|
261
277
|
</div>
|
|
262
278
|
|
|
263
279
|
{props.footerOptions.show && <div class="mt-3 flex items-center justify-between">
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { defineComponent, h, ref } from 'vue'
|
|
2
|
+
import { AForm, AFormItem, AInput, AButton, ATable, ASelect, APagination, ARangePicker, RedoOutlined, ASwitch, AInputNumber } from '#components'
|
|
3
|
+
import type { TableColumnType } from 'ant-design-vue'
|
|
4
|
+
import dayjs from 'dayjs'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
type TableFormLabelsItemType = {
|
|
8
|
+
label: string,
|
|
9
|
+
key: string | string[],
|
|
10
|
+
is: string,
|
|
11
|
+
bind?: any,
|
|
12
|
+
value?:any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type TableParamsType = {
|
|
16
|
+
form?: {
|
|
17
|
+
labels?: TableFormLabelsItemType[],
|
|
18
|
+
'v-slots'?: Record<string, ()=>any>,
|
|
19
|
+
option?: {
|
|
20
|
+
search?: boolean,
|
|
21
|
+
reset?: boolean,
|
|
22
|
+
export?: boolean,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
table?: {
|
|
26
|
+
'v-slots'?:any
|
|
27
|
+
enableSelection?: boolean
|
|
28
|
+
columns: TableColumnType[]
|
|
29
|
+
rowKey: (row: any) => any
|
|
30
|
+
},
|
|
31
|
+
data: (params: any) => any
|
|
32
|
+
footerOptions?: {
|
|
33
|
+
show: boolean
|
|
34
|
+
},
|
|
35
|
+
'v-slots'?: {
|
|
36
|
+
content:(data:any)=>any
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type AttributeType = {
|
|
41
|
+
selectItems:any[],
|
|
42
|
+
selectKeys:any[],
|
|
43
|
+
page:number,
|
|
44
|
+
pageSize: number,
|
|
45
|
+
tableData: any[],
|
|
46
|
+
metaTableData:any[],
|
|
47
|
+
form:any
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let dateRange = defineComponent({
|
|
51
|
+
props:{
|
|
52
|
+
form:{
|
|
53
|
+
type:Object as ()=>any,
|
|
54
|
+
required:true
|
|
55
|
+
},
|
|
56
|
+
item:{
|
|
57
|
+
type:Object as ()=>TableFormLabelsItemType,
|
|
58
|
+
required:true
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
},
|
|
62
|
+
setup(props) {
|
|
63
|
+
let {t:$t} = useI18n()
|
|
64
|
+
|
|
65
|
+
let value = watchRef(ref([]),(nv,ov)=>{
|
|
66
|
+
if(nv?.length>0){
|
|
67
|
+
props.form[props.item.key[0] as string] = dayjs(nv[0]).format('YYYY-MM-DD')
|
|
68
|
+
props.form[props.item.key[1] as string] = dayjs(nv[1]).format('YYYY-MM-DD')
|
|
69
|
+
}else{
|
|
70
|
+
props.form[props.item.key[0] as string] = ''
|
|
71
|
+
props.form[props.item.key[1] as string] = ''
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
watch([() => props.form[props.item.key[0] as string],()=>props.form[props.item.key[1] as string]], (nv) => {
|
|
75
|
+
console.log(nv)
|
|
76
|
+
if (nv[0] == '') {
|
|
77
|
+
value.value = []
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
const rangePresets = ref([
|
|
81
|
+
{ label: $t('今天'), value: [dayjs(), dayjs()] },
|
|
82
|
+
{ label: $t('昨天'), value: [dayjs().add(-1, 'd'), dayjs().add(-1, 'd')] },
|
|
83
|
+
{ label: $t('最近7天'), value: [dayjs().add(-7, 'd'), dayjs()] },
|
|
84
|
+
{ label: $t('最近30天'), value: [dayjs().add(-30, 'd'), dayjs()] },
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
return ()=><ARangePicker presets={rangePresets.value} v-model:value={value.value} />
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
const FormItems:Record<string, (form: any, item: TableFormLabelsItemType) => any> = {
|
|
93
|
+
input: (form: any, item: TableFormLabelsItemType) => {
|
|
94
|
+
let {t:$t} = useI18n()
|
|
95
|
+
return <AInput size="middle" v-model:value={form[item.key as string]} placeholder={$t(`请输入{label}`,{label:$t(item.label)})}></AInput>
|
|
96
|
+
},
|
|
97
|
+
numberRange: (form: any, item: TableFormLabelsItemType) => {
|
|
98
|
+
let {t:$t} = useI18n()
|
|
99
|
+
return <div class="flex items-center">
|
|
100
|
+
<AInputNumber class="!min-w-[150px]" v-model:value={form[item.key[0] as string]} placeholder={$t(`请输入{label}开始`,{label:$t(item.label)})}></AInputNumber>
|
|
101
|
+
<span class="px-1">-</span>
|
|
102
|
+
<AInputNumber class="!min-w-[150px]" v-model:value={form[item.key[1] as string]} placeholder={$t(`请输入{label}结束`,{label:$t(item.label)})}></AInputNumber>
|
|
103
|
+
</div>
|
|
104
|
+
},
|
|
105
|
+
select: (form: any, item: TableFormLabelsItemType) => {
|
|
106
|
+
let {t:$t} = useI18n()
|
|
107
|
+
return <ASelect allowClear {...item.bind} class="min-w-[150px]" v-model:value={form[item.key as string]} options={item.bind?.options} placeholder={$t(`请选择{label}`,{label:$t(item.label)})}></ASelect>
|
|
108
|
+
},
|
|
109
|
+
switch: (form: any, item: TableFormLabelsItemType) => {
|
|
110
|
+
let {t:$t} = useI18n()
|
|
111
|
+
return <ASwitch allowClear {...item.bind} class="min-w-[150px]" v-model:value={form[item.key as string]} options={item.bind?.options} placeholder={$t(`请选择{label}`,{label:$t(item.label)})}></ASwitch>
|
|
112
|
+
},
|
|
113
|
+
dateRange:(form:any,item:TableFormLabelsItemType)=>{
|
|
114
|
+
return h(dateRange,{form,item})
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
export const FormTableProps = {
|
|
120
|
+
form: {
|
|
121
|
+
type: Object as () => TableParamsType['form'],
|
|
122
|
+
required: false
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
table: {
|
|
126
|
+
type: Object as () => TableParamsType['table'],
|
|
127
|
+
default: () => ({
|
|
128
|
+
enableSelection: false,
|
|
129
|
+
columns: [],
|
|
130
|
+
rowKey:(row:any)=>{}
|
|
131
|
+
})
|
|
132
|
+
},
|
|
133
|
+
data: {
|
|
134
|
+
type: Object as () => TableParamsType['data']
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
footerOptions: {
|
|
138
|
+
type: Object as () => {
|
|
139
|
+
show: boolean,
|
|
140
|
+
},
|
|
141
|
+
default: () => ({
|
|
142
|
+
show: true,
|
|
143
|
+
})
|
|
144
|
+
},
|
|
145
|
+
attribute:{
|
|
146
|
+
type: Object as () => AttributeType,
|
|
147
|
+
default:()=>{}
|
|
148
|
+
},
|
|
149
|
+
control:{
|
|
150
|
+
type:Object as () => any,
|
|
151
|
+
required:true
|
|
152
|
+
},
|
|
153
|
+
isInitLoad: {
|
|
154
|
+
type: Boolean,
|
|
155
|
+
default: true
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default defineComponent({
|
|
160
|
+
name: 'TableLayout',
|
|
161
|
+
props: FormTableProps,
|
|
162
|
+
setup(props,{slots}) {
|
|
163
|
+
const {t:$t} = useI18n()
|
|
164
|
+
|
|
165
|
+
//form 初始化
|
|
166
|
+
const form = ref<any>({})
|
|
167
|
+
props.form?.labels?.forEach((item) => {
|
|
168
|
+
|
|
169
|
+
if (Array.isArray(item.key)) {
|
|
170
|
+
item.key.forEach((key,index) => {
|
|
171
|
+
form.value[key] = item.value?.[index]??''
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
} else {
|
|
175
|
+
form.value[item.key] = item.value??''
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
props.attribute.form = form.value
|
|
179
|
+
const metaForm = JSON.parse(JSON.stringify(form.value))
|
|
180
|
+
|
|
181
|
+
//table初始化
|
|
182
|
+
props.table.columns?.forEach((item) => {
|
|
183
|
+
item.title = $t(item.title as string)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
let pagination =reactive({
|
|
187
|
+
page:1,
|
|
188
|
+
pageSize:20,
|
|
189
|
+
total:0
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
let _isInitLoad = props.isInitLoad
|
|
194
|
+
const tableData = asyncReactive<any[]>(async () => {
|
|
195
|
+
//初始化不加载数据
|
|
196
|
+
if (!_isInitLoad) {
|
|
197
|
+
_isInitLoad = true
|
|
198
|
+
return []
|
|
199
|
+
}
|
|
200
|
+
let res: any = await props.data?.({ ...form.value, ...pagination })
|
|
201
|
+
if(Array.isArray(res)){
|
|
202
|
+
res = {
|
|
203
|
+
list:res,
|
|
204
|
+
meta:{
|
|
205
|
+
pagination:{
|
|
206
|
+
total:0,
|
|
207
|
+
per_page:pagination.pageSize,
|
|
208
|
+
current_page:pagination.page
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
console.log(res)
|
|
214
|
+
pagination.total = res.meta.pagination.total
|
|
215
|
+
pagination.pageSize = res.meta.pagination.per_page
|
|
216
|
+
pagination.page = res.meta.pagination.current_page
|
|
217
|
+
props.attribute.metaTableData = JSON.parse(JSON.stringify(res.list))
|
|
218
|
+
props.attribute.tableData = res.list
|
|
219
|
+
return res.list
|
|
220
|
+
},[])
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
const rowSelection = {
|
|
224
|
+
selectedRowKeys: props.attribute?.selectKeys,
|
|
225
|
+
onChange: (keys:any,rows:any)=>{
|
|
226
|
+
props.attribute!.selectKeys.splice(0,props.attribute!.selectKeys.length,...keys)
|
|
227
|
+
props.attribute!.selectItems.splice(0,props.attribute!.selectItems.length,...rows)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
props.control.refresh = ()=>tableData.load()
|
|
232
|
+
|
|
233
|
+
return () => (
|
|
234
|
+
<div class="flex flex-col gap-2 h-full ">
|
|
235
|
+
{/* {JSON.stringify(form.value)} */}
|
|
236
|
+
{props.form?.labels&&<AForm layout="inline gap-2">
|
|
237
|
+
{props.form?.labels?.map((item: TableFormLabelsItemType) => {
|
|
238
|
+
return (
|
|
239
|
+
<AFormItem label={$t(item.label)}>
|
|
240
|
+
{FormItems[item.is]?.(form.value,item)}
|
|
241
|
+
</AFormItem>
|
|
242
|
+
)
|
|
243
|
+
})}
|
|
244
|
+
<div class="flex gap-2">
|
|
245
|
+
{props.form?.option?.search !== false && <AButton type="primary" onClick={()=>tableData.load()}>{$t('搜索')}</AButton>}
|
|
246
|
+
{props.form?.option?.reset !== false && <AButton onClick={() => patch(metaForm,form.value,true)}>{$t('重置')}</AButton>}
|
|
247
|
+
{props.form?.option?.export === true && <AButton type="primary">{$t('导出')}</AButton>}
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<div class="flex ml-auto">
|
|
251
|
+
{props.form['v-slots']?.extra?.()}
|
|
252
|
+
</div>
|
|
253
|
+
</AForm>}
|
|
254
|
+
{slots.center?.()}
|
|
255
|
+
<div class="flex-1 relative overflow-scroll flex flex-col">
|
|
256
|
+
{slots.content?.(tableData.value) ??< div class="absolute inset-0 ">
|
|
257
|
+
<ATable
|
|
258
|
+
row-selection={props.table.enableSelection ? rowSelection : null}
|
|
259
|
+
rowKey={props.table.rowKey ?? ((row) => row.id)}
|
|
260
|
+
loading={tableData.loading}
|
|
261
|
+
scroll={{ x: true,}}
|
|
262
|
+
columns={props.table.columns}
|
|
263
|
+
pagination={false}
|
|
264
|
+
sticky={tableData.value.length>0}
|
|
265
|
+
v-slots={{
|
|
266
|
+
headerCell: ({ title, column }: any) => (
|
|
267
|
+
<div style={{ 'white-space': 'nowrap' }}>
|
|
268
|
+
{title}
|
|
269
|
+
</div>
|
|
270
|
+
),
|
|
271
|
+
bodyCell: (row: any) => {
|
|
272
|
+
if (row.column?.customRender != null) {
|
|
273
|
+
return row.column.customRender(row)
|
|
274
|
+
}
|
|
275
|
+
return <div class="whitespace-nowrap" style={{ width: row.column.width ?? '100px' }}>{row.text}</div>
|
|
276
|
+
},
|
|
277
|
+
...props.table['v-slots']
|
|
278
|
+
}}
|
|
279
|
+
dataSource={tableData.value}
|
|
280
|
+
/>
|
|
281
|
+
</div>
|
|
282
|
+
}
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
{props.footerOptions.show && <div class="mt-3 flex items-center justify-between">
|
|
286
|
+
<div>
|
|
287
|
+
{ props.table.enableSelection && (`${$t('选中项')}: ${props.attribute.selectKeys.length}`)}
|
|
288
|
+
</div>
|
|
289
|
+
<div class="flex items-center gap-2">
|
|
290
|
+
<APagination
|
|
291
|
+
hideOnSinglePage={true}
|
|
292
|
+
v-model:current={pagination.page}
|
|
293
|
+
total={pagination.total}
|
|
294
|
+
pageSize={pagination.pageSize}
|
|
295
|
+
onChange={(page) => {
|
|
296
|
+
pagination.page = page
|
|
297
|
+
tableData.load()
|
|
298
|
+
}}
|
|
299
|
+
pageSizeOptions={['20','50','100']}
|
|
300
|
+
onShowSizeChange={(current, size) => {
|
|
301
|
+
pagination.pageSize = size
|
|
302
|
+
pagination.page = 1
|
|
303
|
+
tableData.load()
|
|
304
|
+
}}
|
|
305
|
+
/>
|
|
306
|
+
<AButton icon={h(RedoOutlined)} loading={tableData.loading} onClick={tableData.load} />
|
|
307
|
+
</div>
|
|
308
|
+
</div>}
|
|
309
|
+
</div>
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
})
|
|
@@ -6,54 +6,60 @@
|
|
|
6
6
|
collapsed-width="0"
|
|
7
7
|
width="220px"
|
|
8
8
|
:trigger="null"
|
|
9
|
+
class="overflow-scroll"
|
|
9
10
|
>
|
|
10
|
-
<div class="text-center text-white p-2 text-lg
|
|
11
|
-
|
|
11
|
+
<div class="text-center text-white p-2 text-lg " >
|
|
12
|
+
{{props.title}}
|
|
12
13
|
</div>
|
|
13
14
|
<LayoutMenu :items="props.menu"></LayoutMenu>
|
|
14
15
|
</a-layout-sider>
|
|
15
16
|
|
|
16
17
|
<a-layout>
|
|
17
18
|
<a-layout-header :style="{ background: '#fff', padding: 0 }">
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
<div class="flex h-full items-center justify-between gap-2 px-4">
|
|
20
|
+
<div class="flex items-center gap-2">
|
|
21
|
+
<menu-unfold-outlined v-if="collapsed" class="trigger text-[20px]" @click="() => (collapsed = !collapsed)" />
|
|
22
|
+
<menu-fold-outlined v-else class="trigger text-[20px]" @click="() => (collapsed = !collapsed)" />
|
|
23
|
+
<div class="leading-4 gap-1 flex flex-col justify-center h-full">
|
|
24
|
+
<div class="text-[12px] text-[#aaa]">{{routeInfo?.names.join(' / ')}}</div>
|
|
25
|
+
<div class="font-bold text-[18px]">{{routeInfo?.name}}</div>
|
|
26
|
+
</div>
|
|
25
27
|
</div>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
<a-menu
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
28
|
+
<div class=" flex items-center">
|
|
29
|
+
<slot name="header-right-extend"></slot>
|
|
30
|
+
|
|
31
|
+
<div class="flex items-center p-2 ml-2">
|
|
32
|
+
<a-dropdown >
|
|
33
|
+
<div class=" cursor-pointer flex items-center">
|
|
34
|
+
<GlobalOutlined class="text-[26px]" />
|
|
35
|
+
</div>
|
|
36
|
+
<template #overlay>
|
|
37
|
+
<a-menu>
|
|
38
|
+
<a-menu-item v-for="locale in locales" @click="setLocale(locale.code)">
|
|
39
|
+
{{ locale.name }}
|
|
40
|
+
</a-menu-item>
|
|
41
|
+
</a-menu>
|
|
42
|
+
</template>
|
|
43
|
+
</a-dropdown>
|
|
44
|
+
</div>
|
|
45
|
+
<div >
|
|
46
|
+
<a-dropdown >
|
|
47
|
+
<div class="h-[30px] leading-[30px] cursor-pointer">{{ useAdmin().value?.username ?? 'Admin'}}</div>
|
|
48
|
+
<template #overlay>
|
|
49
|
+
<a-menu>
|
|
50
|
+
<a-menu-item v-for="item in props.adminMenu" @click="item.click">
|
|
51
|
+
{{ item.name }}
|
|
52
|
+
</a-menu-item>
|
|
53
|
+
<a-menu-item @click="()=>singOut()">
|
|
54
|
+
<a href="javascript:;">{{$t('退出')}}</a>
|
|
55
|
+
</a-menu-item>
|
|
56
|
+
</a-menu>
|
|
57
|
+
</template>
|
|
58
|
+
</a-dropdown>
|
|
59
|
+
</div>
|
|
53
60
|
</div>
|
|
54
|
-
|
|
55
61
|
</div>
|
|
56
|
-
|
|
62
|
+
|
|
57
63
|
</a-layout-header>
|
|
58
64
|
<!-- <div class="p-4 bg-white" style="border-top:1px solid #eee">
|
|
59
65
|
<div class="leading-4 gap-1 flex flex-col justify-center h-full">
|
|
@@ -62,7 +68,7 @@
|
|
|
62
68
|
</div>
|
|
63
69
|
</div> -->
|
|
64
70
|
<a-layout-content :style="{ }" class="relative m-4">
|
|
65
|
-
<div class="absolute top-0 left-0 right-0 bottom-0">
|
|
71
|
+
<div class="absolute top-0 left-0 right-0 bottom-0 overflow-scroll">
|
|
66
72
|
<slot ></slot>
|
|
67
73
|
</div>
|
|
68
74
|
</a-layout-content>
|
|
@@ -76,10 +82,16 @@ const { locales, setLocale } = useI18n()
|
|
|
76
82
|
const collapsed = ref(false)
|
|
77
83
|
|
|
78
84
|
const props = defineProps<{
|
|
79
|
-
|
|
85
|
+
title:string,
|
|
86
|
+
menu: MenuItemType[],
|
|
87
|
+
adminMenu:{
|
|
88
|
+
name:string,
|
|
89
|
+
click:()=>void
|
|
90
|
+
}[]
|
|
80
91
|
}>()
|
|
81
92
|
|
|
82
93
|
let routeInfo = useRouteInfo(props.menu)
|
|
83
94
|
|
|
95
|
+
|
|
84
96
|
</script>
|
|
85
97
|
|
|
@@ -69,8 +69,8 @@ const items = computed(()=>{
|
|
|
69
69
|
let routeInfo = useRouteInfo(props.items)
|
|
70
70
|
|
|
71
71
|
onMounted(()=>{
|
|
72
|
-
state.selectedKeys=routeInfo
|
|
73
|
-
state.openKeys=routeInfo
|
|
72
|
+
state.selectedKeys=routeInfo?.value?.paths
|
|
73
|
+
state.openKeys=routeInfo?.value?.paths
|
|
74
74
|
})
|
|
75
75
|
|
|
76
76
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
export const useRouteInfo = (menuItems: any[]) =>
|
|
3
|
+
useState('routeName', () => {
|
|
4
|
+
const info = ref({
|
|
5
|
+
name: '',
|
|
6
|
+
names: [] as string[],
|
|
7
|
+
paths: [] as string[],
|
|
8
|
+
})
|
|
9
|
+
console.log(menuItems)
|
|
10
|
+
|
|
11
|
+
const route = useRoute()
|
|
12
|
+
|
|
13
|
+
const findPath = (
|
|
14
|
+
items: any[],
|
|
15
|
+
targetPath: string,
|
|
16
|
+
parents: { title: string; path: string }[] = []
|
|
17
|
+
): { titles: string[]; paths: string[] } | null => {
|
|
18
|
+
for (const item of items) {
|
|
19
|
+
const current = [...parents, { title: item.title, path: item.path }]
|
|
20
|
+
if (item.path === targetPath) {
|
|
21
|
+
return {
|
|
22
|
+
titles: current.map((i) => i.title),
|
|
23
|
+
paths: current.map((i) => i.path),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (item.children) {
|
|
27
|
+
const result = findPath(item.children, targetPath, current)
|
|
28
|
+
if (result) return result
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
watch(
|
|
35
|
+
() => route.fullPath,
|
|
36
|
+
() => {
|
|
37
|
+
const match = findPath(menuItems, route.path)
|
|
38
|
+
if (match) {
|
|
39
|
+
info.value = {
|
|
40
|
+
name: match.titles.at(-1) || '',
|
|
41
|
+
names: match.titles,
|
|
42
|
+
paths: match.paths,
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
info.value = {
|
|
46
|
+
name: '',
|
|
47
|
+
names: [],
|
|
48
|
+
paths: [],
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{ immediate: true }
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return info
|
|
56
|
+
})
|
package/app/composables/table.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ABadge, ACard, ADropdown, AMenu, ATag, EllipsisOutlined } from "#components";
|
|
2
2
|
import type { TablePropsType } from "../components/global/FormTable";
|
|
3
3
|
import FormTableCom from "../components/global/FormTable";
|
|
4
|
+
import FormTableCom2, { type TableParamsType } from "../components/global/FormTable2";
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
export const FormTable = (props: TablePropsType) => {
|
|
@@ -25,6 +26,28 @@ export const FormTable = (props: TablePropsType) => {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
export const FormTable2 = (props: TableParamsType) => {
|
|
30
|
+
|
|
31
|
+
const attribute = reactive({
|
|
32
|
+
selectItems: [],
|
|
33
|
+
selectKeys: [],
|
|
34
|
+
page: 1,
|
|
35
|
+
pageSize: 10,
|
|
36
|
+
tableData: [],
|
|
37
|
+
form: {}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const control = {
|
|
41
|
+
refresh: () => { }
|
|
42
|
+
}
|
|
43
|
+
console.log({ ...props, attribute, control })
|
|
44
|
+
return {
|
|
45
|
+
com: h(FormTableCom2, { ...props, attribute, control }, props['v-slots']),
|
|
46
|
+
attribute,
|
|
47
|
+
control
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
28
51
|
export const TablePage = (props: TablePropsType) => {
|
|
29
52
|
let TableParams = FormTable(props)
|
|
30
53
|
|
|
@@ -35,6 +58,17 @@ export const TablePage = (props: TablePropsType) => {
|
|
|
35
58
|
}
|
|
36
59
|
}
|
|
37
60
|
|
|
61
|
+
export const TablePage2 = (props: TableParamsType) => {
|
|
62
|
+
let TableParams = FormTable2(props)
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
...TableParams,
|
|
66
|
+
com: h(ACard, { class: "absolute top-0 left-0 right-0 bottom-0 h-full", bodyStyle: { height: '100%' } }, TableParams.com)
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
38
72
|
|
|
39
73
|
export const Table = {
|
|
40
74
|
Normal: FormTable,
|
package/nuxt.config.ts
CHANGED
|
@@ -21,6 +21,13 @@ export default defineNuxtConfig({
|
|
|
21
21
|
i18n: {
|
|
22
22
|
defaultLocale: 'zh',
|
|
23
23
|
strategy: 'no_prefix',
|
|
24
|
+
compilation: {
|
|
25
|
+
strictMessage: false,
|
|
26
|
+
escapeHtml: true,
|
|
27
|
+
},
|
|
28
|
+
bundle: {
|
|
29
|
+
optimizeTranslationDirective: false
|
|
30
|
+
},
|
|
24
31
|
locales: [
|
|
25
32
|
{ code: 'en', name: 'English', file: 'en.json' },
|
|
26
33
|
{ code: 'zh', name: '中文', file: 'zh.json' }
|
package/package.json
CHANGED
package/tsconfig.json
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { ACard } from "#components"
|
|
2
|
-
import { FormTableProps } from "./FormTable"
|
|
3
|
-
|
|
4
|
-
export default defineComponent({
|
|
5
|
-
|
|
6
|
-
props:FormTableProps,
|
|
7
|
-
|
|
8
|
-
setup(props){
|
|
9
|
-
const {com,params} = FormTable(props)
|
|
10
|
-
|
|
11
|
-
return ()=><ACard class="absolute top-0 left-0 right-0 bottom-0 h-full" bodyStyle="height:100%">
|
|
12
|
-
<com></com>
|
|
13
|
-
</ACard>
|
|
14
|
-
}
|
|
15
|
-
})
|