@xizs/nuxt-antui 0.0.1
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/.playground/nuxt.config.ts +5 -0
- package/README.md +75 -0
- package/app/app.config.ts +14 -0
- package/app/assets/css/main.css +2 -0
- package/app/components/global/Form.tsx +124 -0
- package/app/components/global/FormTable.tsx +271 -0
- package/app/components/global/TablePage.tsx +15 -0
- package/app/components/layout/Base.vue +178 -0
- package/app/components/layout/Menu.vue +79 -0
- package/app/composables/drawer.ts +26 -0
- package/app/composables/form.ts +1 -0
- package/app/composables/modal.ts +126 -0
- package/app/composables/table.ts +63 -0
- package/eslint.config.mjs +6 -0
- package/nuxt.config.ts +22 -0
- package/package.json +24 -0
- package/server/tsconfig.json +3 -0
- package/tsconfig.json +3 -0
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Nuxt Minimal Starter
|
|
2
|
+
|
|
3
|
+
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
Make sure to install dependencies:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# npm
|
|
11
|
+
npm install
|
|
12
|
+
|
|
13
|
+
# pnpm
|
|
14
|
+
pnpm install
|
|
15
|
+
|
|
16
|
+
# yarn
|
|
17
|
+
yarn install
|
|
18
|
+
|
|
19
|
+
# bun
|
|
20
|
+
bun install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Development Server
|
|
24
|
+
|
|
25
|
+
Start the development server on `http://localhost:3000`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# npm
|
|
29
|
+
npm run dev
|
|
30
|
+
|
|
31
|
+
# pnpm
|
|
32
|
+
pnpm dev
|
|
33
|
+
|
|
34
|
+
# yarn
|
|
35
|
+
yarn dev
|
|
36
|
+
|
|
37
|
+
# bun
|
|
38
|
+
bun run dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Production
|
|
42
|
+
|
|
43
|
+
Build the application for production:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# npm
|
|
47
|
+
npm run build
|
|
48
|
+
|
|
49
|
+
# pnpm
|
|
50
|
+
pnpm build
|
|
51
|
+
|
|
52
|
+
# yarn
|
|
53
|
+
yarn build
|
|
54
|
+
|
|
55
|
+
# bun
|
|
56
|
+
bun run build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Locally preview production build:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# npm
|
|
63
|
+
npm run preview
|
|
64
|
+
|
|
65
|
+
# pnpm
|
|
66
|
+
pnpm preview
|
|
67
|
+
|
|
68
|
+
# yarn
|
|
69
|
+
yarn preview
|
|
70
|
+
|
|
71
|
+
# bun
|
|
72
|
+
bun run preview
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { AForm, AFormItem, AInput, ASelect,AInputNumber, LineOutlined, ASwitch, ATextarea } from "#components"
|
|
2
|
+
|
|
3
|
+
export type FormItemType = {
|
|
4
|
+
label:string,
|
|
5
|
+
key:string|string[],
|
|
6
|
+
is:string,
|
|
7
|
+
bind:any,
|
|
8
|
+
rules:any[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type FormPropsType = {
|
|
12
|
+
form:FormItemType[],
|
|
13
|
+
submit:(formData:any)=>{
|
|
14
|
+
|
|
15
|
+
},
|
|
16
|
+
showSubmit?:boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const FormComs = {
|
|
20
|
+
input:(form:any,item:FormItemType)=>{
|
|
21
|
+
return <AInput v-model:value={form[item.key]}></AInput>
|
|
22
|
+
},
|
|
23
|
+
textarea:(form:any,item:FormItemType)=>{
|
|
24
|
+
return <ATextarea v-model:value={form[item.key]}></ATextarea>
|
|
25
|
+
},
|
|
26
|
+
inputNumber:(form:any,item:FormItemType)=>{
|
|
27
|
+
return <AInputNumber v-model:value={form[item.key]} {...item.bind}></AInputNumber>
|
|
28
|
+
},
|
|
29
|
+
inputNumberRange:(form:any,item:FormItemType)=>{
|
|
30
|
+
return <div class="flex items-center gap-2">
|
|
31
|
+
<AInputNumber class="flex-1" v-model:value={form[item.key[0]]} {...item.bind[0]}></AInputNumber>
|
|
32
|
+
<LineOutlined />
|
|
33
|
+
<AInputNumber class="flex-1" v-model:value={form[item.key[1]]} {...item.bind[1]}></AInputNumber>
|
|
34
|
+
</div>
|
|
35
|
+
},
|
|
36
|
+
select:(form:any,item:FormItemType)=>{
|
|
37
|
+
return <ASelect v-model:value={form[item.key]} options={item.bind.options}></ASelect>
|
|
38
|
+
},
|
|
39
|
+
countrySelect:(form:any,item:FormItemType)=>{
|
|
40
|
+
return <ASelect v-model:value={form[item.key]} options={Const.CountrySelectList()}></ASelect>
|
|
41
|
+
},
|
|
42
|
+
switch:(form:any,item:FormItemType)=>{
|
|
43
|
+
return <ASwitch v-model:checked={form[item.key]} {...item.bind}></ASwitch>
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default defineComponent({
|
|
48
|
+
props:{
|
|
49
|
+
// form: Array as () => FormType[],
|
|
50
|
+
// action: Array as () => JSX.Element[],
|
|
51
|
+
// table: Object as () => FormTableProps<any>["table"],
|
|
52
|
+
// control: Object
|
|
53
|
+
form:{
|
|
54
|
+
type:Array as ()=>FormPropsType['form'],
|
|
55
|
+
required:true
|
|
56
|
+
},
|
|
57
|
+
submit:{
|
|
58
|
+
type:Function as FormPropsType['submit'],
|
|
59
|
+
required:true
|
|
60
|
+
},
|
|
61
|
+
showSubmit:{
|
|
62
|
+
type:Boolean,
|
|
63
|
+
default:true
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
setup(props,{expose}){
|
|
67
|
+
|
|
68
|
+
let thisRef = ref(null)
|
|
69
|
+
let formData = ref({})
|
|
70
|
+
let rules = ref({})
|
|
71
|
+
props.form.forEach(item=>{
|
|
72
|
+
if(Array.isArray(item.key)){
|
|
73
|
+
item.key.forEach(key=>{
|
|
74
|
+
formData.value[key] = item.value??''
|
|
75
|
+
rules.value[item.key] = item.rules??[]
|
|
76
|
+
|
|
77
|
+
})
|
|
78
|
+
}else{
|
|
79
|
+
formData.value[item.key] = item.value??''
|
|
80
|
+
rules.value[item.key] = item.rules??[]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// if(Array.isArray(item.rules)){
|
|
84
|
+
// item.rules.forEach(rule=>{
|
|
85
|
+
// rules.value
|
|
86
|
+
// })
|
|
87
|
+
// }
|
|
88
|
+
// rules.value[item.key] = item.rules??[]
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
let loading = ref(false)
|
|
92
|
+
let submit = async ()=>{
|
|
93
|
+
if(loading.value){
|
|
94
|
+
throw new Error('loading')
|
|
95
|
+
}
|
|
96
|
+
loading.value = true
|
|
97
|
+
try{
|
|
98
|
+
await thisRef.value.validate()
|
|
99
|
+
await props.submit(formData.value)
|
|
100
|
+
}finally{
|
|
101
|
+
loading.value = false
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
expose({
|
|
106
|
+
submit,
|
|
107
|
+
loading
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
return ()=><AForm ref={thisRef} model={formData.value} rules={rules.value} label-col={{ span: 6 }} wrapper-col={{ span: 16 }}>
|
|
113
|
+
{/* {JSON.stringify(formData.value)} */}
|
|
114
|
+
{
|
|
115
|
+
props.form.map(item=>{
|
|
116
|
+
return <AFormItem label={item.label} name={item.key}>
|
|
117
|
+
{FormComs[item.is](formData.value,item)}
|
|
118
|
+
</AFormItem>
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
</AForm>
|
|
123
|
+
}
|
|
124
|
+
})
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { defineComponent, ref } from 'vue'
|
|
2
|
+
import { AForm, AFormItem, AInput, AButton, ATable, ASelect, APagination, ARangePicker, RedoOutlined } from '#components'
|
|
3
|
+
import type { TableColumnType } from 'ant-design-vue'
|
|
4
|
+
import dayjs from 'dayjs'
|
|
5
|
+
|
|
6
|
+
type TableFormItemType = {
|
|
7
|
+
label: string
|
|
8
|
+
key: string
|
|
9
|
+
is: string
|
|
10
|
+
bind?: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let dateRange = defineComponent({
|
|
14
|
+
props:{
|
|
15
|
+
form:{
|
|
16
|
+
type:Object as ()=>any,
|
|
17
|
+
required:true
|
|
18
|
+
},
|
|
19
|
+
item:{
|
|
20
|
+
type:Object as ()=>TableFormItemType,
|
|
21
|
+
required:true
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
setup(props){
|
|
25
|
+
let value = watchRef(ref([]),(nv,ov)=>{
|
|
26
|
+
if(nv?.length>0){
|
|
27
|
+
props.form[props.item.key[0]] = nv[0].format('YYYY-MM-DD 00:00:00')
|
|
28
|
+
props.form[props.item.key[1]] = nv[1].format('YYYY-MM-DD 23:59:59')
|
|
29
|
+
}else{
|
|
30
|
+
props.form[props.item.key[0]] = ''
|
|
31
|
+
props.form[props.item.key[1]] = ''
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const rangePresets = ref([
|
|
36
|
+
{ label: '今天', value: [dayjs(), dayjs()] },
|
|
37
|
+
{ label: '昨天', value: [dayjs().add(-1, 'd'), dayjs().add(-1, 'd')] },
|
|
38
|
+
{ label: '最近7天', value: [dayjs().add(-7, 'd'), dayjs()] },
|
|
39
|
+
{ label: '最近30天', value: [dayjs().add(-30, 'd'), dayjs()] },
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
return ()=><ARangePicker presets={rangePresets.value} v-model:value={value.value} />
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
const FormItems = {
|
|
48
|
+
input:(form:any,item:TableFormItemType)=>{
|
|
49
|
+
return <AInput size="middle" v-model:value={form[item.key]} placeholder={`please input ${item.label}`}></AInput>
|
|
50
|
+
},
|
|
51
|
+
select:(form:any,item:TableFormItemType)=>{
|
|
52
|
+
return <ASelect allowClear class="min-w-[150px]" v-model:value={form[item.key]} options={item.bind.options} placeholder={`please select ${item.label}`}></ASelect>
|
|
53
|
+
},
|
|
54
|
+
dateRange:(form:any,item:TableFormItemType)=>{
|
|
55
|
+
|
|
56
|
+
return h(dateRange,{form,item})
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type TablePropsType = {
|
|
61
|
+
form: TableFormItemType[]
|
|
62
|
+
action?: {
|
|
63
|
+
search?: boolean
|
|
64
|
+
reset?: boolean
|
|
65
|
+
export?: boolean
|
|
66
|
+
opther: () => any
|
|
67
|
+
}
|
|
68
|
+
table: {
|
|
69
|
+
'v-slots'?:any
|
|
70
|
+
enableSelection?: boolean
|
|
71
|
+
columns: TableColumnType[]
|
|
72
|
+
data: () => any[]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type AttributeType = {
|
|
77
|
+
selectItems:any[],
|
|
78
|
+
selectKeys:any[],
|
|
79
|
+
page:number,
|
|
80
|
+
pageSize:number
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const FormTableProps = {
|
|
84
|
+
form: {
|
|
85
|
+
type: Array as () => TableFormItemType[],
|
|
86
|
+
required: true
|
|
87
|
+
},
|
|
88
|
+
formOptions:{
|
|
89
|
+
type:Object as () => {
|
|
90
|
+
search:boolean,
|
|
91
|
+
reset:boolean,
|
|
92
|
+
export:boolean,
|
|
93
|
+
opther?:()=>any
|
|
94
|
+
},
|
|
95
|
+
default:()=>({
|
|
96
|
+
search:true,
|
|
97
|
+
reset:true,
|
|
98
|
+
export:false,
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
action: {
|
|
102
|
+
type: Object as () => TablePropsType['action'],
|
|
103
|
+
default: () => ({
|
|
104
|
+
search: true,
|
|
105
|
+
reset: true,
|
|
106
|
+
export: true,
|
|
107
|
+
opther: () => null
|
|
108
|
+
})
|
|
109
|
+
},
|
|
110
|
+
table: {
|
|
111
|
+
type: Object as () => TablePropsType['table'],
|
|
112
|
+
default: () => ({
|
|
113
|
+
enableSelection: false,
|
|
114
|
+
columns: [],
|
|
115
|
+
data: () => [],
|
|
116
|
+
rowKey:(row:any)=>any
|
|
117
|
+
})
|
|
118
|
+
},
|
|
119
|
+
attribute:{
|
|
120
|
+
type:Object as ()=> AttributeType
|
|
121
|
+
},
|
|
122
|
+
control:{
|
|
123
|
+
type:Object,
|
|
124
|
+
required:true
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default defineComponent({
|
|
129
|
+
name: 'TableLayout',
|
|
130
|
+
props: FormTableProps,
|
|
131
|
+
setup(props) {
|
|
132
|
+
|
|
133
|
+
const form = ref({})
|
|
134
|
+
props.form.forEach((item) => {
|
|
135
|
+
form.value[item.key] = item.value?? ''
|
|
136
|
+
})
|
|
137
|
+
let pagination =reactive({
|
|
138
|
+
page:1,
|
|
139
|
+
pageSize:20,
|
|
140
|
+
total:0
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
const tableData = asyncReactive<any[]>(async ()=>{
|
|
145
|
+
let res = await props.table.data({...form.value,...pagination})
|
|
146
|
+
if(Array.isArray(res)){
|
|
147
|
+
res = {
|
|
148
|
+
list:res,
|
|
149
|
+
meta:{
|
|
150
|
+
pagination:{
|
|
151
|
+
total:0,
|
|
152
|
+
per_page:pagination.pageSize,
|
|
153
|
+
current_page:pagination.page
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
console.log(res)
|
|
159
|
+
pagination.total = res.meta.pagination.total
|
|
160
|
+
pagination.pageSize = res.meta.pagination.per_page
|
|
161
|
+
pagination.page = res.meta.pagination.current_page
|
|
162
|
+
console.log(res)
|
|
163
|
+
return res.list
|
|
164
|
+
},[])
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
const rowSelection = {
|
|
168
|
+
selectedRowKeys: props.attribute?.selectKeys,
|
|
169
|
+
onChange: (keys,rows)=>{
|
|
170
|
+
props.attribute!.selectKeys.splice(0,props.attribute!.selectKeys.length,...keys)
|
|
171
|
+
props.attribute!.selectItems.splice(0,props.attribute!.selectItems.length,...rows)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
props.control.refresh = ()=>tableData.load()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
return () => (
|
|
179
|
+
<div class="flex flex-col gap-2 h-full ">
|
|
180
|
+
{/* {JSON.stringify(form.value)} */}
|
|
181
|
+
{props.form&&<AForm layout="inline gap-2">
|
|
182
|
+
{props.form?.map((item) => {
|
|
183
|
+
form[item.key] = item.value?? ''
|
|
184
|
+
return (
|
|
185
|
+
<AFormItem label={item.label}>
|
|
186
|
+
{/* <AInput type="text" /> */}
|
|
187
|
+
{FormItems[item.is](form.value,item)}
|
|
188
|
+
</AFormItem>
|
|
189
|
+
)
|
|
190
|
+
})}
|
|
191
|
+
<div class="flex gap-2">
|
|
192
|
+
{props.formOptions.search && <AButton type="primary" onClick={()=>tableData.load()}>搜索</AButton>}
|
|
193
|
+
{props.formOptions.reset && <AButton>重置</AButton>}
|
|
194
|
+
{props.formOptions.export && <AButton type="primary">导出</AButton>}
|
|
195
|
+
</div>
|
|
196
|
+
</AForm>}
|
|
197
|
+
|
|
198
|
+
<div class="flex items-center justify-between">
|
|
199
|
+
<div>{props.action.other}</div>
|
|
200
|
+
|
|
201
|
+
{/* <div class="flex gap-2">
|
|
202
|
+
{props.action.search && <AButton type="primary" onClick={()=>tableData.load()}>搜索</AButton>}
|
|
203
|
+
{props.action.reset && <AButton>重置</AButton>}
|
|
204
|
+
{props.action.export && <AButton type="primary">导出</AButton>}
|
|
205
|
+
</div> */}
|
|
206
|
+
</div>
|
|
207
|
+
{/* <a-descriptions bordered size="small" column={5} layout="vertical">
|
|
208
|
+
<a-descriptions-item label="游玩人数">33</a-descriptions-item>
|
|
209
|
+
<a-descriptions-item label="游玩人次">1810000000</a-descriptions-item>
|
|
210
|
+
<a-descriptions-item label="转入/下注">33</a-descriptions-item>
|
|
211
|
+
<a-descriptions-item label="转出">12</a-descriptions-item>
|
|
212
|
+
<a-descriptions-item label="人均盈亏"> 11</a-descriptions-item>
|
|
213
|
+
</a-descriptions> */}
|
|
214
|
+
<div class="flex-1 mt-3 overflow-scroll flex flex-col">
|
|
215
|
+
<ATable
|
|
216
|
+
row-selection={props.table.enableSelection?rowSelection:null}
|
|
217
|
+
rowKey={props.table.rowKey??((row,key)=>key)}
|
|
218
|
+
loading={tableData.loading}
|
|
219
|
+
scroll={{x: true}}
|
|
220
|
+
columns={props.table.columns}
|
|
221
|
+
pagination={false}
|
|
222
|
+
sticky={tableData.value.length>0}
|
|
223
|
+
v-slots={{
|
|
224
|
+
headerCell: ({ title,column }: any) => (
|
|
225
|
+
<div style={{'white-space': 'nowrap'}}>
|
|
226
|
+
{title }
|
|
227
|
+
</div>
|
|
228
|
+
),
|
|
229
|
+
bodyCell:(row)=>{
|
|
230
|
+
if(row.column?.customRender!=null){
|
|
231
|
+
return row.column.customRender(row)
|
|
232
|
+
}
|
|
233
|
+
return <div class="whitespace-nowrap" style={{ width:row.column.width??'100px'}}>{row.text}</div>
|
|
234
|
+
},
|
|
235
|
+
...props.table['v-slots']
|
|
236
|
+
|
|
237
|
+
}}
|
|
238
|
+
dataSource={tableData.value}
|
|
239
|
+
/>
|
|
240
|
+
</div>
|
|
241
|
+
<div class="mt-3 flex items-center justify-between">
|
|
242
|
+
<div>
|
|
243
|
+
{ props.table.enableSelection && (`选中项: ${props.attribute.selectKeys.length}`)}
|
|
244
|
+
</div>
|
|
245
|
+
<div class="flex items-center gap-2">
|
|
246
|
+
<APagination
|
|
247
|
+
hideOnSinglePage={true}
|
|
248
|
+
v-model:current={pagination.page}
|
|
249
|
+
total={pagination.total}
|
|
250
|
+
pageSize={pagination.pageSize}
|
|
251
|
+
onChange={(page) => {
|
|
252
|
+
pagination.page = page
|
|
253
|
+
tableData.load()
|
|
254
|
+
}}
|
|
255
|
+
pageSizeOptions={['20','50','100']}
|
|
256
|
+
onShowSizeChange={(current, size) => {
|
|
257
|
+
pagination.pageSize = size
|
|
258
|
+
pagination.page = current
|
|
259
|
+
tableData.load()
|
|
260
|
+
}}
|
|
261
|
+
/>
|
|
262
|
+
|
|
263
|
+
<AButton icon={h(RedoOutlined)} loading={tableData.loading} onClick={tableData.load} />
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
</div>
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<a-layout class="top-0 bottom-0 left-0 right-0 absolute">
|
|
3
|
+
<a-layout-sider
|
|
4
|
+
v-model:collapsed="collapsed"
|
|
5
|
+
breakpoint="lg"
|
|
6
|
+
collapsed-width="0"
|
|
7
|
+
width="220px"
|
|
8
|
+
>
|
|
9
|
+
<div class="text-center text-white p-2 text-lg " >
|
|
10
|
+
LOGO
|
|
11
|
+
</div>
|
|
12
|
+
<LayoutMenu :items="props.menu"></LayoutMenu>
|
|
13
|
+
</a-layout-sider>
|
|
14
|
+
|
|
15
|
+
<a-layout>
|
|
16
|
+
<a-layout-header :style="{ background: '#fff', padding: 0 }">
|
|
17
|
+
<div class="flex h-full items-center justify-between gap-2 px-4">
|
|
18
|
+
<div class="flex items-center gap-2">
|
|
19
|
+
<menu-unfold-outlined
|
|
20
|
+
v-if="collapsed"
|
|
21
|
+
class="trigger text-[20px]"
|
|
22
|
+
@click="() => (collapsed = !collapsed)"
|
|
23
|
+
/>
|
|
24
|
+
<menu-fold-outlined v-else class="trigger text-[20px]" @click="() => (collapsed = !collapsed)" />
|
|
25
|
+
<div class="leading-4 gap-1 flex flex-col justify-center h-full">
|
|
26
|
+
<div class="text-[12px] text-[#aaa]">{{routeInfo.names.join(' / ')}}</div>
|
|
27
|
+
<div class="font-bold text-[18px]">{{routeInfo.name}}</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div class=" flex items-center">
|
|
31
|
+
<div >
|
|
32
|
+
<a-dropdown >
|
|
33
|
+
<div class="h-[30px] leading-[30px]">{{ useAdmin().value?.username }}</div>
|
|
34
|
+
<template #overlay>
|
|
35
|
+
<a-menu>
|
|
36
|
+
<a-menu-item @click="()=>singOut()">
|
|
37
|
+
<a href="javascript:;">退出</a>
|
|
38
|
+
</a-menu-item>
|
|
39
|
+
</a-menu>
|
|
40
|
+
</template>
|
|
41
|
+
</a-dropdown>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</a-layout-header>
|
|
47
|
+
<!-- <div class="p-4 bg-white" style="border-top:1px solid #eee">
|
|
48
|
+
<div class="leading-4 gap-1 flex flex-col justify-center h-full">
|
|
49
|
+
<div class="text-[12px] text-[#aaa]">{{routeInfo.names.join(' / ')}}</div>
|
|
50
|
+
<div class="font-bold text-[18px]">{{routeInfo.name}}</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div> -->
|
|
53
|
+
<a-layout-content :style="{ }" class="relative m-4">
|
|
54
|
+
<div class="absolute top-0 left-0 right-0 bottom-0">
|
|
55
|
+
<slot ></slot>
|
|
56
|
+
</div>
|
|
57
|
+
</a-layout-content>
|
|
58
|
+
</a-layout>
|
|
59
|
+
</a-layout>
|
|
60
|
+
</template>
|
|
61
|
+
<script lang="ts" setup>
|
|
62
|
+
import type { MenuItemType } from './Menu.vue';
|
|
63
|
+
|
|
64
|
+
const collapsed = ref(false)
|
|
65
|
+
|
|
66
|
+
const props = defineProps<{
|
|
67
|
+
menu: MenuItemType[]
|
|
68
|
+
}>()
|
|
69
|
+
|
|
70
|
+
// const menuItems:MenuItemType = [
|
|
71
|
+
// {
|
|
72
|
+
// title:'首页',
|
|
73
|
+
// icon: () => h(PieChartOutlined),
|
|
74
|
+
// path:'/'
|
|
75
|
+
// },
|
|
76
|
+
// {
|
|
77
|
+
// title:'用户',
|
|
78
|
+
// icon: () => h(PieChartOutlined),
|
|
79
|
+
// path:'/user',
|
|
80
|
+
// children:[
|
|
81
|
+
// {
|
|
82
|
+
// title:'用户列表',
|
|
83
|
+
// icon:'InboxOutlined',
|
|
84
|
+
// path:'/user/list'
|
|
85
|
+
// }
|
|
86
|
+
// ]
|
|
87
|
+
// },
|
|
88
|
+
// {
|
|
89
|
+
// title:'数据报表',
|
|
90
|
+
// icon: () => h(PieChartOutlined),
|
|
91
|
+
// path:'/dataReport',
|
|
92
|
+
// children:[
|
|
93
|
+
// {
|
|
94
|
+
// title:'平台活跃报表',
|
|
95
|
+
// path:'/DataReport/platformActiveReport'
|
|
96
|
+
// },
|
|
97
|
+
// {
|
|
98
|
+
// title:'平台财务报表',
|
|
99
|
+
// path:'/DataReport/platformfinanceReport'
|
|
100
|
+
// },
|
|
101
|
+
// {
|
|
102
|
+
// title:'消耗报表',
|
|
103
|
+
// path:'/DataReport/consumptionReport'
|
|
104
|
+
// }
|
|
105
|
+
// ]
|
|
106
|
+
// },
|
|
107
|
+
// {
|
|
108
|
+
// title:'结算管理',
|
|
109
|
+
// icon: () => h(PieChartOutlined),
|
|
110
|
+
// path:'/settleManage',
|
|
111
|
+
// children:[
|
|
112
|
+
// {
|
|
113
|
+
// title:'分包游戏报表',
|
|
114
|
+
// path:'/settleManage/subGameReport'
|
|
115
|
+
// },
|
|
116
|
+
|
|
117
|
+
// ]
|
|
118
|
+
// },
|
|
119
|
+
// {
|
|
120
|
+
// title:'综艺直播游戏',
|
|
121
|
+
// icon: () => h(PieChartOutlined),
|
|
122
|
+
// path:'/liveGame',
|
|
123
|
+
// children:[
|
|
124
|
+
// {
|
|
125
|
+
// title:'综艺直播间数据',
|
|
126
|
+
// path:'/liveGame/liveData'
|
|
127
|
+
// },
|
|
128
|
+
// {
|
|
129
|
+
// title:'直播间开奖操控',
|
|
130
|
+
// path:'/liveGame/LotteryControl'
|
|
131
|
+
// },
|
|
132
|
+
// {
|
|
133
|
+
// title:'直播间实时监控',
|
|
134
|
+
// path:'/liveGame/liveMonitor'
|
|
135
|
+
// },
|
|
136
|
+
// {
|
|
137
|
+
// title:'机器人管理',
|
|
138
|
+
// path:'/liveGame/robotManage',
|
|
139
|
+
// children:[
|
|
140
|
+
// {
|
|
141
|
+
// title:'机器人列表',
|
|
142
|
+
// path:'/liveGame/robotManage/robotList'
|
|
143
|
+
// },
|
|
144
|
+
// {
|
|
145
|
+
// title:'机器人配置',
|
|
146
|
+
// path:'/liveGame/robotManage/robotConfig'
|
|
147
|
+
// },
|
|
148
|
+
// {
|
|
149
|
+
// title:'机器人资源库',
|
|
150
|
+
// path:'/liveGame/robotManage/robotResourceLibrary'
|
|
151
|
+
// },
|
|
152
|
+
// {
|
|
153
|
+
// title:'机器人类型',
|
|
154
|
+
// path:'/liveGame/robotManage/robotType'
|
|
155
|
+
// },
|
|
156
|
+
// {
|
|
157
|
+
// title:'自动弹幕文本配置',
|
|
158
|
+
// path:'/liveGame/robotManage/autoDanmuTextConfig'
|
|
159
|
+
// },
|
|
160
|
+
// ]
|
|
161
|
+
// },
|
|
162
|
+
// {
|
|
163
|
+
// title:'综艺游戏直播间配置',
|
|
164
|
+
// path:'/liveGame/gameLiveConfig'
|
|
165
|
+
// }
|
|
166
|
+
|
|
167
|
+
// ]
|
|
168
|
+
// }
|
|
169
|
+
|
|
170
|
+
// ]
|
|
171
|
+
const router = useRouter()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
let routeInfo = useRouteInfo(props.menu)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
</script>
|
|
178
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<a-menu
|
|
3
|
+
v-model:openKeys="state.openKeys"
|
|
4
|
+
v-model:selectedKeys="state.openKeys"
|
|
5
|
+
mode="inline"
|
|
6
|
+
theme="dark"
|
|
7
|
+
:inline-collapsed="state.collapsed"
|
|
8
|
+
:items="items"
|
|
9
|
+
@select="select"
|
|
10
|
+
></a-menu>
|
|
11
|
+
</template>
|
|
12
|
+
<script lang="ts" setup>
|
|
13
|
+
|
|
14
|
+
export type MenuItemType = {
|
|
15
|
+
icon?:string,
|
|
16
|
+
title:string,
|
|
17
|
+
path:string,
|
|
18
|
+
children?:MenuItemType[]
|
|
19
|
+
}
|
|
20
|
+
const props = defineProps<{
|
|
21
|
+
items: MenuItemType[],
|
|
22
|
+
}>()
|
|
23
|
+
|
|
24
|
+
const router = useRouter()
|
|
25
|
+
const select = ({ item, key, keyPath })=>{
|
|
26
|
+
router.push(key)
|
|
27
|
+
state.openKeys = keyPath
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const state = reactive({
|
|
31
|
+
collapsed: false,
|
|
32
|
+
selectedKeys: [''],
|
|
33
|
+
openKeys: [''],
|
|
34
|
+
preOpenKeys: ['sub1'],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const items = computed(()=>{
|
|
38
|
+
let a = props.items.map((item) => {
|
|
39
|
+
const { icon, title, path, children } = item;
|
|
40
|
+
let newItem = {
|
|
41
|
+
key: path,
|
|
42
|
+
icon: icon,
|
|
43
|
+
label: title,
|
|
44
|
+
|
|
45
|
+
};
|
|
46
|
+
if(children){
|
|
47
|
+
newItem.children=children?.map((child) =>{
|
|
48
|
+
let c = {
|
|
49
|
+
key: child.path,
|
|
50
|
+
label: child.title,
|
|
51
|
+
}
|
|
52
|
+
if(child.children){
|
|
53
|
+
c.children=child.children?.map((subChild) =>{
|
|
54
|
+
return {
|
|
55
|
+
key: subChild.path,
|
|
56
|
+
label: subChild.title,
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
return c
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
return newItem
|
|
64
|
+
});
|
|
65
|
+
console.log(a)
|
|
66
|
+
return a
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
let routeInfo = useRouteInfo(props.items)
|
|
70
|
+
|
|
71
|
+
onMounted(()=>{
|
|
72
|
+
state.selectedKeys=routeInfo.value.paths
|
|
73
|
+
state.openKeys=routeInfo.value.paths
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createVNode, render } from "vue"
|
|
2
|
+
|
|
3
|
+
export const Drawer = (el)=>{
|
|
4
|
+
|
|
5
|
+
let com = defineComponent({
|
|
6
|
+
setup(){
|
|
7
|
+
let open=ref(true)
|
|
8
|
+
console.log('aaaaaa')
|
|
9
|
+
return ()=> h(el,{
|
|
10
|
+
open: open.value, // 等价于 v-model:open
|
|
11
|
+
'onUpdate:open': (val: boolean) => {
|
|
12
|
+
open.value = val
|
|
13
|
+
console.log(val,open.value)
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
let container = document.createDocumentFragment()
|
|
21
|
+
const vnode = h(com)
|
|
22
|
+
vnode.appContext = useNuxtApp().vueApp._context
|
|
23
|
+
render(vnode,container)
|
|
24
|
+
document.body.appendChild(container)
|
|
25
|
+
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FormItem = {}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createVNode, render } from "vue"
|
|
2
|
+
import Form from "../components/global/Form"
|
|
3
|
+
import { AModal } from "#components"
|
|
4
|
+
|
|
5
|
+
export const NormalModal = (el)=>{
|
|
6
|
+
let control = {close:()=>{}}
|
|
7
|
+
let container = document.createDocumentFragment()
|
|
8
|
+
|
|
9
|
+
let com = defineComponent({
|
|
10
|
+
setup(){
|
|
11
|
+
let open=ref(true)
|
|
12
|
+
control.close = ()=>{
|
|
13
|
+
open.value = false
|
|
14
|
+
}
|
|
15
|
+
return ()=> h(el,{
|
|
16
|
+
open: open.value, // 等价于 v-model:open
|
|
17
|
+
'onUpdate:open': (val: boolean) => {
|
|
18
|
+
open.value = val
|
|
19
|
+
if(!val){
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
render(null, container) // 卸载组件
|
|
22
|
+
if (container.parentNode) {
|
|
23
|
+
container.parentNode.removeChild(container) // 移除 DOM
|
|
24
|
+
}
|
|
25
|
+
}, 1000);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const vnode = h(com)
|
|
33
|
+
vnode.appContext = useNuxtApp().vueApp._context
|
|
34
|
+
render(vnode,container)
|
|
35
|
+
document.body.appendChild(container)
|
|
36
|
+
|
|
37
|
+
return control
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// export Modal.Normal = NormalModal
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
type FormModalParamsType= {
|
|
45
|
+
title:string,
|
|
46
|
+
form:{
|
|
47
|
+
label:string,
|
|
48
|
+
key:string,
|
|
49
|
+
is:string
|
|
50
|
+
}[],
|
|
51
|
+
submit:()=>void
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const FormModal = (params:FormModalParamsType)=>{
|
|
55
|
+
|
|
56
|
+
let submit:()=>void
|
|
57
|
+
|
|
58
|
+
// let control = NormalModal(h(AModal,{
|
|
59
|
+
// title: params.title,
|
|
60
|
+
// okText: '提交'+loading?.value,
|
|
61
|
+
// cancelText: '取消',
|
|
62
|
+
// onOk: async ()=>{
|
|
63
|
+
// await submit()
|
|
64
|
+
// control.close()
|
|
65
|
+
// }
|
|
66
|
+
|
|
67
|
+
// },h(defineComponent({
|
|
68
|
+
// setup(){
|
|
69
|
+
// let formRef = ref(null)
|
|
70
|
+
|
|
71
|
+
// onMounted(()=>{
|
|
72
|
+
// submit = formRef.value.submit
|
|
73
|
+
// loading = formRef.value.loading
|
|
74
|
+
// })
|
|
75
|
+
|
|
76
|
+
// return ()=>h(Form,{
|
|
77
|
+
// ref: formRef,
|
|
78
|
+
// form: params.form,
|
|
79
|
+
// submit:params.submit,
|
|
80
|
+
// showSubmit: false
|
|
81
|
+
// })
|
|
82
|
+
// }
|
|
83
|
+
// }))))
|
|
84
|
+
|
|
85
|
+
let control = NormalModal(h(defineComponent({
|
|
86
|
+
setup(){
|
|
87
|
+
let formRef = ref(null)
|
|
88
|
+
let loading =ref(false)
|
|
89
|
+
|
|
90
|
+
onMounted(()=>{
|
|
91
|
+
submit = async ()=>{
|
|
92
|
+
loading.value = true
|
|
93
|
+
try{
|
|
94
|
+
await formRef.value.submit()
|
|
95
|
+
}finally{
|
|
96
|
+
|
|
97
|
+
loading.value = false
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// loading = formRef.value.loading
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return ()=>h(AModal,{
|
|
104
|
+
title: params.title,
|
|
105
|
+
okText: '提交',
|
|
106
|
+
cancelText: '取消',
|
|
107
|
+
confirmLoading: loading.value,
|
|
108
|
+
onOk: async ()=>{
|
|
109
|
+
await submit()
|
|
110
|
+
control.close()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
},h(Form,{
|
|
114
|
+
ref: formRef,
|
|
115
|
+
form: params.form,
|
|
116
|
+
submit:params.submit,
|
|
117
|
+
showSubmit: false
|
|
118
|
+
}))
|
|
119
|
+
}
|
|
120
|
+
})))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const MModal = {
|
|
124
|
+
Normal: NormalModal,
|
|
125
|
+
Form: FormModal
|
|
126
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ACard, ADropdown, AMenu, EllipsisOutlined } from "#components";
|
|
2
|
+
import type { TablePropsType } from "../components/global/FormTable";
|
|
3
|
+
import FormTableCom from "../components/global/FormTable";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export const FormTable = (props:TablePropsType)=>{
|
|
7
|
+
|
|
8
|
+
const attribute = reactive({
|
|
9
|
+
selectItems:[],
|
|
10
|
+
selectKeys:[],
|
|
11
|
+
page:1,
|
|
12
|
+
pageSize:10
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const control = {
|
|
16
|
+
refresh:()=>{}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
com:h(FormTableCom,{...props,attribute,control}),
|
|
21
|
+
attribute,
|
|
22
|
+
control
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const TablePage = (props:TablePropsType)=>{
|
|
27
|
+
let TableParams = FormTable(props)
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
...TableParams,
|
|
31
|
+
com: h(ACard,{class:"absolute top-0 left-0 right-0 bottom-0 h-full",bodyStyle:"height:100%"},TableParams.com)
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const TableCell = {
|
|
37
|
+
Dropdown:(params:{label:string,icon:string,onClick:()=>void}[])=>{
|
|
38
|
+
// return h(EllipsisOutlined,{class:"mr-2"},{default:()=>{
|
|
39
|
+
// return h()
|
|
40
|
+
// }})
|
|
41
|
+
|
|
42
|
+
return h('div',{style:{'min-width':'50px'}},h(ADropdown,{},{
|
|
43
|
+
default:()=>{
|
|
44
|
+
return h(EllipsisOutlined,{class:"mr-2"})
|
|
45
|
+
},
|
|
46
|
+
overlay:()=>{
|
|
47
|
+
return h(AMenu,{items:params.map(item=>{
|
|
48
|
+
return {
|
|
49
|
+
label:item.label,
|
|
50
|
+
icon:()=>item.icon,
|
|
51
|
+
onClick:item.onClick
|
|
52
|
+
}
|
|
53
|
+
})})
|
|
54
|
+
}
|
|
55
|
+
}))
|
|
56
|
+
},
|
|
57
|
+
Tenant:(params:{name:string,id:string})=>{
|
|
58
|
+
return h('div',{class:"flex flex-col"},[
|
|
59
|
+
h('span',{class:"mr-2"},params.name),
|
|
60
|
+
h('span',{class:"mr-2 whitespace-nowrap"},`ID: ${params.id}`)
|
|
61
|
+
])
|
|
62
|
+
},
|
|
63
|
+
}
|
package/nuxt.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
import { dirname, join } from 'path'
|
|
4
|
+
const currentDir = dirname(fileURLToPath(import.meta.url))
|
|
5
|
+
console.log(currentDir)
|
|
6
|
+
|
|
7
|
+
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
8
|
+
export default defineNuxtConfig({
|
|
9
|
+
compatibilityDate: '2024-11-01',
|
|
10
|
+
devtools: { enabled: true },
|
|
11
|
+
|
|
12
|
+
future: {
|
|
13
|
+
compatibilityVersion: 4
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
modules: ['@ant-design-vue/nuxt'],
|
|
17
|
+
extends: ['@xizs/nuxt-base'],
|
|
18
|
+
css: [join(currentDir, './app/assets/css/main.css')],
|
|
19
|
+
vite:{
|
|
20
|
+
plugins: [tailwindcss()],
|
|
21
|
+
}
|
|
22
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xizs/nuxt-antui",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"main": "./nuxt.config.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "nuxi dev .playground",
|
|
8
|
+
"build": "nuxt build .playground",
|
|
9
|
+
"generate": "nuxt generate .playground",
|
|
10
|
+
"preview": "nuxt preview .playground",
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"postinstall": "nuxt prepare .playground"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@ant-design-vue/nuxt": "1.4.6",
|
|
16
|
+
"@tailwindcss/vite": "^4.1.3",
|
|
17
|
+
"@xizs/nuxt-base": "^0.0.4",
|
|
18
|
+
"ant-design-vue": ">=4",
|
|
19
|
+
"nuxt": "^3.16.2",
|
|
20
|
+
"tailwindcss": "^4.0.14",
|
|
21
|
+
"vue": "^3.5.13",
|
|
22
|
+
"vue-router": "^4.5.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/tsconfig.json
ADDED