mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-23 20:06:37 +08:00
Merge pull request #227 from fast-crud/for-pr
feat(projects): integration fast-crud
This commit is contained in:
commit
032dbc6815
295
mock/api/crud/base.ts
Normal file
295
mock/api/crud/base.ts
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
export type ListItem = {
|
||||||
|
id?: number;
|
||||||
|
children?: ListItem[];
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
export type BaseMockOptions = { name: string; copyTimes?: number; list: ListItem[]; idGenerator: number };
|
||||||
|
type CopyListParams = { originList: ListItem[]; newList: ListItem[]; options: BaseMockOptions; parentId?: number };
|
||||||
|
|
||||||
|
function copyList(props: CopyListParams) {
|
||||||
|
const { originList, newList, options, parentId } = props;
|
||||||
|
for (const item of originList) {
|
||||||
|
const newItem: ListItem = { ...item, parentId };
|
||||||
|
newItem.id = options.idGenerator;
|
||||||
|
options.idGenerator += 1;
|
||||||
|
newList.push(newItem);
|
||||||
|
if (item.children) {
|
||||||
|
newItem.children = [];
|
||||||
|
copyList({
|
||||||
|
originList: item.children,
|
||||||
|
newList: newItem.children,
|
||||||
|
options,
|
||||||
|
parentId: newItem.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function delById(req: Service.MockOption, list: any[]) {
|
||||||
|
for (let i = 0; i < list.length; i += 1) {
|
||||||
|
const item = list[i];
|
||||||
|
if (item.id === parseInt(req.query.id, 10)) {
|
||||||
|
list.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
delById(req, item.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findById(id: number, list: ListItem[]): any {
|
||||||
|
for (const item of list) {
|
||||||
|
if (item.id === id) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
const sub = findById(id, item.children);
|
||||||
|
if (sub !== null && sub !== undefined) {
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchWithArrayCondition(value: any[], item: ListItem, key: string) {
|
||||||
|
if (value.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let matched = false;
|
||||||
|
for (const i of value) {
|
||||||
|
if (item[key] instanceof Array) {
|
||||||
|
for (const j of item[key]) {
|
||||||
|
if (i === j) {
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matched) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (item[key] === i || (typeof item[key] === 'string' && item[key].indexOf(`${i}`) >= 0)) {
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (matched) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchWithObjectCondition(value: any, item: ListItem, key: string) {
|
||||||
|
let matched = true;
|
||||||
|
for (const key2 of Object.keys(value)) {
|
||||||
|
const v = value[key2];
|
||||||
|
if (v && item[key] && v !== item[key][key2]) {
|
||||||
|
matched = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchFromList(list: ListItem[], query: any) {
|
||||||
|
const filter = (item: ListItem) => {
|
||||||
|
let allFound = true; // 是否所有条件都符合
|
||||||
|
for (const key of Object.keys(query)) {
|
||||||
|
const value = query[key];
|
||||||
|
if (value === undefined || value === null || value === '') {
|
||||||
|
// no nothing
|
||||||
|
} else if (value instanceof Array) {
|
||||||
|
// 如果条件中的value是数组的话,只要查到一个就行
|
||||||
|
const matched = matchWithArrayCondition(value, item, key);
|
||||||
|
if (!matched) {
|
||||||
|
allFound = false;
|
||||||
|
}
|
||||||
|
} else if (value instanceof Object) {
|
||||||
|
// 如果条件中的value是对象的话,需要每个key都匹配
|
||||||
|
const matched = matchWithObjectCondition(value, item, key);
|
||||||
|
if (!matched) {
|
||||||
|
allFound = false;
|
||||||
|
}
|
||||||
|
} else if (item[key] !== value) {
|
||||||
|
allFound = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allFound;
|
||||||
|
};
|
||||||
|
return list.filter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
buildMock(options: BaseMockOptions) {
|
||||||
|
const name = options.name;
|
||||||
|
if (!options.copyTimes) {
|
||||||
|
options.copyTimes = 29;
|
||||||
|
}
|
||||||
|
const list: any[] = [];
|
||||||
|
for (let i = 0; i < options.copyTimes; i += 1) {
|
||||||
|
copyList({
|
||||||
|
originList: options.list,
|
||||||
|
newList: list,
|
||||||
|
options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
options.list = list;
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
path: `/mock/${name}/page`,
|
||||||
|
method: 'post',
|
||||||
|
handle(req: Service.MockOption) {
|
||||||
|
let data = [...list];
|
||||||
|
let limit = 20;
|
||||||
|
let offset = 0;
|
||||||
|
for (const item of list) {
|
||||||
|
if (item.children && item.children.length === 0) {
|
||||||
|
item.hasChildren = false;
|
||||||
|
item.lazy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let orderAsc: any;
|
||||||
|
let orderProp: any;
|
||||||
|
if (req && req.body) {
|
||||||
|
const { page, query } = req.body;
|
||||||
|
if (page.limit) {
|
||||||
|
limit = parseInt(page.limit, 10);
|
||||||
|
}
|
||||||
|
if (page.offset) {
|
||||||
|
offset = parseInt(page.offset, 10);
|
||||||
|
}
|
||||||
|
if (Object.keys(query).length > 0) {
|
||||||
|
data = searchFromList(list, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = offset;
|
||||||
|
let end = offset + limit;
|
||||||
|
if (data.length < end) {
|
||||||
|
end = data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderProp) {
|
||||||
|
// 排序
|
||||||
|
data.sort((a, b) => {
|
||||||
|
let ret = 0;
|
||||||
|
if (a[orderProp] > b[orderProp]) {
|
||||||
|
ret = 1;
|
||||||
|
} else if (a[orderProp] < b[orderProp]) {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
return orderAsc ? ret : -ret;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = data.slice(start, end);
|
||||||
|
const lastOffset = data.length - (data.length % limit);
|
||||||
|
if (offset > lastOffset) {
|
||||||
|
offset = lastOffset;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: 'success',
|
||||||
|
data: {
|
||||||
|
records,
|
||||||
|
total: data.length,
|
||||||
|
limit,
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/mock/${name}/get`,
|
||||||
|
method: 'get',
|
||||||
|
handle(req: Service.MockOption) {
|
||||||
|
let id = req.query.id;
|
||||||
|
id = parseInt(id, 10);
|
||||||
|
let current = null;
|
||||||
|
for (const item of list) {
|
||||||
|
if (item.id === id) {
|
||||||
|
current = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: 'success',
|
||||||
|
data: current
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/mock/${name}/add`,
|
||||||
|
method: 'post',
|
||||||
|
handle(req: Service.MockOption) {
|
||||||
|
req.body.id = options.idGenerator;
|
||||||
|
options.idGenerator += 1;
|
||||||
|
list.unshift(req.body);
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: 'success',
|
||||||
|
data: req.body.id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/mock/${name}/update`,
|
||||||
|
method: 'post',
|
||||||
|
handle(req: Service.MockOption) {
|
||||||
|
const item = findById(req.body.id, list);
|
||||||
|
if (item) {
|
||||||
|
Object.assign(item, req.body);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/mock/${name}/delete`,
|
||||||
|
method: 'post',
|
||||||
|
handle(req: Service.MockOption) {
|
||||||
|
delById(req, list);
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/mock/${name}/batchDelete`,
|
||||||
|
method: 'post',
|
||||||
|
handle(req: Service.MockOption) {
|
||||||
|
const ids = req.body.ids;
|
||||||
|
for (let i = list.length - 1; i >= 0; i -= 1) {
|
||||||
|
const item = list[i];
|
||||||
|
if (ids.indexOf(item.id) >= 0) {
|
||||||
|
list.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/mock/${name}/all`,
|
||||||
|
method: 'post',
|
||||||
|
handle() {
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: 'success',
|
||||||
|
data: list
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
5
mock/api/crud/index.ts
Normal file
5
mock/api/crud/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import demo from './modules/demo';
|
||||||
|
import headerGroup from './modules/header-group';
|
||||||
|
|
||||||
|
const crudApis = [...demo, ...headerGroup];
|
||||||
|
export default crudApis;
|
56
mock/api/crud/modules/demo.ts
Normal file
56
mock/api/crud/modules/demo.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import type { MethodType, MockMethod } from 'vite-plugin-mock';
|
||||||
|
import type { BaseMockOptions } from '../base';
|
||||||
|
import mockBase from '../base';
|
||||||
|
import MockOption = Service.MockOption;
|
||||||
|
|
||||||
|
const options: BaseMockOptions = {
|
||||||
|
name: 'crud/demo',
|
||||||
|
idGenerator: 0,
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
select: '1',
|
||||||
|
text: '文本测试',
|
||||||
|
copyable: '文本可复制',
|
||||||
|
avatar: 'http://greper.handsfree.work/extends/avatar.jpg',
|
||||||
|
richtext: '富文本',
|
||||||
|
datetime: '2023-01-30 11:11:11'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: '2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: '0'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const mockedApis = mockBase.buildMock(options);
|
||||||
|
|
||||||
|
const apis: MockMethod[] = [
|
||||||
|
{
|
||||||
|
url: `/mock/${options.name}/dict`,
|
||||||
|
method: 'get',
|
||||||
|
response: () => {
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: '',
|
||||||
|
data: [
|
||||||
|
{ value: '0', label: '关', color: 'warning' },
|
||||||
|
{ value: '1', label: '开', color: 'success' },
|
||||||
|
{ value: '2', label: '停' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const mockedApi of mockedApis) {
|
||||||
|
apis.push({
|
||||||
|
url: mockedApi.path,
|
||||||
|
method: mockedApi.method as MethodType,
|
||||||
|
response: (request: MockOption) => {
|
||||||
|
return mockedApi.handle(request);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default apis;
|
46
mock/api/crud/modules/header-group.ts
Normal file
46
mock/api/crud/modules/header-group.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import type { MethodType, MockMethod } from 'vite-plugin-mock';
|
||||||
|
import type { BaseMockOptions } from '../base';
|
||||||
|
import mockBase from '../base';
|
||||||
|
import MockOption = Service.MockOption;
|
||||||
|
|
||||||
|
const options: BaseMockOptions = {
|
||||||
|
name: 'crud/header-group',
|
||||||
|
idGenerator: 0,
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
name: '张三',
|
||||||
|
age: 18,
|
||||||
|
province: '广东省',
|
||||||
|
city: '深圳市',
|
||||||
|
county: '南山区',
|
||||||
|
street: '粤海街道'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '李四',
|
||||||
|
age: 26,
|
||||||
|
province: '浙江省',
|
||||||
|
city: '杭州市',
|
||||||
|
county: '西湖区',
|
||||||
|
street: '西湖街道'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '王五',
|
||||||
|
age: 24
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const mockedApis = mockBase.buildMock(options);
|
||||||
|
|
||||||
|
const apis: MockMethod[] = [];
|
||||||
|
|
||||||
|
for (const mockedApi of mockedApis) {
|
||||||
|
apis.push({
|
||||||
|
url: mockedApi.path,
|
||||||
|
method: mockedApi.method as MethodType,
|
||||||
|
response: (request: MockOption) => {
|
||||||
|
return mockedApi.handle(request);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default apis;
|
@ -1,5 +1,6 @@
|
|||||||
import auth from './auth';
|
import auth from './auth';
|
||||||
import route from './route';
|
import route from './route';
|
||||||
import management from './management';
|
import management from './management';
|
||||||
|
import crud from './crud';
|
||||||
|
|
||||||
export default [...auth, ...route, ...management];
|
export default [...auth, ...route, ...management, ...crud];
|
||||||
|
@ -56,6 +56,11 @@
|
|||||||
"prepare": "soy init-git-hooks"
|
"prepare": "soy init-git-hooks"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons-vue": "^6.1.0",
|
||||||
|
"@fast-crud/fast-crud": "^1.13.6",
|
||||||
|
"@fast-crud/fast-extends": "^1.13.6",
|
||||||
|
"@fast-crud/ui-naive": "^1.13.6",
|
||||||
|
"@fast-crud/ui-interface": "^1.13.6",
|
||||||
"@antv/data-set": "^0.11.8",
|
"@antv/data-set": "^0.11.8",
|
||||||
"@antv/g2": "^4.2.10",
|
"@antv/g2": "^4.2.10",
|
||||||
"@better-scroll/core": "^2.5.1",
|
"@better-scroll/core": "^2.5.1",
|
||||||
|
3005
pnpm-lock.yaml
3005
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,9 @@
|
|||||||
class="h-full"
|
class="h-full"
|
||||||
>
|
>
|
||||||
<naive-provider>
|
<naive-provider>
|
||||||
<router-view />
|
<fs-ui-context>
|
||||||
|
<router-view />
|
||||||
|
</fs-ui-context>
|
||||||
</naive-provider>
|
</naive-provider>
|
||||||
</n-config-provider>
|
</n-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
@ -3,7 +3,7 @@ import App from './App.vue';
|
|||||||
import AppLoading from './components/common/app-loading.vue';
|
import AppLoading from './components/common/app-loading.vue';
|
||||||
import { setupDirectives } from './directives';
|
import { setupDirectives } from './directives';
|
||||||
import { setupRouter } from './router';
|
import { setupRouter } from './router';
|
||||||
import { setupAssets } from './plugins';
|
import { setupAssets, setupFastCrud } from './plugins';
|
||||||
import { setupStore } from './store';
|
import { setupStore } from './store';
|
||||||
import { setupI18n } from './locales';
|
import { setupI18n } from './locales';
|
||||||
|
|
||||||
@ -29,6 +29,8 @@ async function setupApp() {
|
|||||||
|
|
||||||
setupI18n(app);
|
setupI18n(app);
|
||||||
|
|
||||||
|
setupFastCrud(app);
|
||||||
|
|
||||||
// mount app
|
// mount app
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
}
|
}
|
||||||
|
11
src/plugins/fast-crud/common.scss
Normal file
11
src/plugins/fast-crud/common.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
html:root {
|
||||||
|
--baseColor: #fff;
|
||||||
|
}
|
||||||
|
/* 深色模式 */
|
||||||
|
html.dark:root {
|
||||||
|
--baseColor: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fs-container {
|
||||||
|
background-color: var(--baseColor);
|
||||||
|
}
|
171
src/plugins/fast-crud/index.tsx
Normal file
171
src/plugins/fast-crud/index.tsx
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
import type { FsSetupOptions, PageQuery } from '@fast-crud/fast-crud';
|
||||||
|
// eslint-disable-next-line import/order
|
||||||
|
import { FastCrud } from '@fast-crud/fast-crud';
|
||||||
|
import '@fast-crud/fast-crud/dist/style.css';
|
||||||
|
import './common.scss';
|
||||||
|
|
||||||
|
import type { FsUploaderOptions } from '@fast-crud/fast-extends';
|
||||||
|
import {
|
||||||
|
FsExtendsCopyable,
|
||||||
|
FsExtendsEditor,
|
||||||
|
FsExtendsJson,
|
||||||
|
FsExtendsTime,
|
||||||
|
FsExtendsUploader
|
||||||
|
} from '@fast-crud/fast-extends';
|
||||||
|
import '@fast-crud/fast-extends/dist/style.css';
|
||||||
|
import UiNaive from '@fast-crud/ui-naive';
|
||||||
|
import axios from 'axios';
|
||||||
|
import type { VueI18n } from 'vue-i18n';
|
||||||
|
import { mockRequest, request } from '@/service/request';
|
||||||
|
import { setupNaive } from '@/plugins/fast-crud/naive';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fast-crud的安装方法
|
||||||
|
* 注意:在App.vue中,需要用fs-ui-context组件包裹RouterView,让fs-crud拥有message、notification、dialog的能力
|
||||||
|
* @param app
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export type FsSetupOpts = {
|
||||||
|
i18n?: VueI18n;
|
||||||
|
};
|
||||||
|
function install(app: App, options: FsSetupOpts = {}) {
|
||||||
|
// 安装naive ui 常用组件
|
||||||
|
setupNaive(app);
|
||||||
|
app.use(UiNaive);
|
||||||
|
app.use(FastCrud, {
|
||||||
|
i18n: options.i18n,
|
||||||
|
async dictRequest(context: { url: string }) {
|
||||||
|
const url = context.url;
|
||||||
|
let res: Service.SuccessResult | Service.FailedResult;
|
||||||
|
if (url && url.startsWith('/mock')) {
|
||||||
|
// 如果是crud开头的dict请求视为mock
|
||||||
|
res = await mockRequest.get(url.replace('/mock', ''));
|
||||||
|
} else {
|
||||||
|
res = await request.get(url);
|
||||||
|
}
|
||||||
|
res = res || {};
|
||||||
|
return res.data || [];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* useCrud时会被执行
|
||||||
|
*/
|
||||||
|
commonOptions() {
|
||||||
|
return {
|
||||||
|
table: {
|
||||||
|
size: 'small',
|
||||||
|
pagination: false
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
options: {
|
||||||
|
size: 'medium'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rowHandle: {
|
||||||
|
buttons: {
|
||||||
|
view: { text: null, icon: 'EyeOutlined', size: 'small' },
|
||||||
|
edit: { text: null, icon: 'EditOutlined', size: 'small' },
|
||||||
|
remove: { type: 'error', text: null, icon: 'DeleteOutlined', size: 'small' }
|
||||||
|
},
|
||||||
|
dropdown: {
|
||||||
|
more: { size: 'small' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
// 查询参数转换
|
||||||
|
transformQuery: (query: PageQuery) => {
|
||||||
|
const { page, form, sort } = query;
|
||||||
|
const limit = page.pageSize;
|
||||||
|
const currentPage = page.currentPage ?? 1;
|
||||||
|
const offset = limit * (currentPage - 1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
page: {
|
||||||
|
limit,
|
||||||
|
offset
|
||||||
|
},
|
||||||
|
query: form,
|
||||||
|
sort: sort || {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// page请求结果转换
|
||||||
|
transformRes: originPageRes => {
|
||||||
|
const { res } = originPageRes;
|
||||||
|
const pageSize = res.limit;
|
||||||
|
let currentPage = res.offset / pageSize;
|
||||||
|
if (res.offset % pageSize === 0) {
|
||||||
|
currentPage += 1;
|
||||||
|
}
|
||||||
|
return { currentPage, pageSize, ...res };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
display: 'flex', // 表单布局
|
||||||
|
labelWidth: '120px' // 表单label宽度
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 从 useCrud({permission}) 里获取permission参数,去设置各个按钮的权限
|
||||||
|
// const crudPermission = useCrudPermission(context);
|
||||||
|
// return crudPermission.merge(opts);
|
||||||
|
}
|
||||||
|
} as FsSetupOptions);
|
||||||
|
|
||||||
|
// fast-extends里面的扩展组件均为异步组件,只有在使用时才会被加载,并不会影响首页加载速度
|
||||||
|
// 安装editor
|
||||||
|
app.use(FsExtendsEditor, {
|
||||||
|
// 编辑器的公共配置
|
||||||
|
wangEditor: {}
|
||||||
|
});
|
||||||
|
app.use(FsExtendsJson);
|
||||||
|
app.use(FsExtendsCopyable);
|
||||||
|
// 安装uploader 公共参数
|
||||||
|
const uploaderOptions: FsUploaderOptions = {
|
||||||
|
defaultType: 'form',
|
||||||
|
form: {
|
||||||
|
action: 'http://www.docmirror.cn:7070/api/upload/form/upload',
|
||||||
|
name: 'file',
|
||||||
|
withCredentials: false,
|
||||||
|
uploadRequest: async props => {
|
||||||
|
const { action, file, onProgress } = props;
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('file', file);
|
||||||
|
const res = await axios.post(action, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
timeout: 60000,
|
||||||
|
onUploadProgress(progress) {
|
||||||
|
onProgress({ percent: Math.round((progress.loaded / progress.total!) * 100) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 上传完成后的结果,一般返回个url 或者key,具体看你的后台返回啥
|
||||||
|
return res.data.data;
|
||||||
|
},
|
||||||
|
async successHandle(ret: string) {
|
||||||
|
// 上传完成后的结果处理, 此处应转换格式为{url:xxx,key:xxx}
|
||||||
|
return {
|
||||||
|
url: `http://www.docmirror.cn:7070${ret}`,
|
||||||
|
key: ret.replace('/api/upload/form/download?key=', '')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
app.use(FsExtendsUploader, uploaderOptions);
|
||||||
|
|
||||||
|
// 安装editor
|
||||||
|
app.use(FsExtendsEditor, {
|
||||||
|
// 编辑器的公共配置
|
||||||
|
wangEditor: {}
|
||||||
|
});
|
||||||
|
app.use(FsExtendsJson);
|
||||||
|
app.use(FsExtendsTime);
|
||||||
|
app.use(FsExtendsCopyable);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install
|
||||||
|
};
|
||||||
|
|
||||||
|
export function setupFastCrud(app: App<Element>, options: FsSetupOpts = {}) {
|
||||||
|
install(app, options);
|
||||||
|
}
|
59
src/plugins/fast-crud/naive.ts
Normal file
59
src/plugins/fast-crud/naive.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
import * as NaiveUI from 'naive-ui';
|
||||||
|
|
||||||
|
const naive = NaiveUI.create({
|
||||||
|
components: [
|
||||||
|
NaiveUI.NInput,
|
||||||
|
NaiveUI.NButton,
|
||||||
|
NaiveUI.NForm,
|
||||||
|
NaiveUI.NFormItem,
|
||||||
|
NaiveUI.NCheckboxGroup,
|
||||||
|
NaiveUI.NCheckbox,
|
||||||
|
NaiveUI.NIcon,
|
||||||
|
NaiveUI.NDropdown,
|
||||||
|
NaiveUI.NTooltip,
|
||||||
|
NaiveUI.NTabs,
|
||||||
|
NaiveUI.NTabPane,
|
||||||
|
NaiveUI.NCard,
|
||||||
|
NaiveUI.NRow,
|
||||||
|
NaiveUI.NCol,
|
||||||
|
NaiveUI.NDrawer,
|
||||||
|
NaiveUI.NDrawerContent,
|
||||||
|
NaiveUI.NDivider,
|
||||||
|
NaiveUI.NSwitch,
|
||||||
|
NaiveUI.NBadge,
|
||||||
|
NaiveUI.NAlert,
|
||||||
|
NaiveUI.NTag,
|
||||||
|
NaiveUI.NProgress,
|
||||||
|
NaiveUI.NDatePicker,
|
||||||
|
NaiveUI.NGrid,
|
||||||
|
NaiveUI.NGridItem,
|
||||||
|
NaiveUI.NDataTable,
|
||||||
|
NaiveUI.NPagination,
|
||||||
|
NaiveUI.NSelect,
|
||||||
|
NaiveUI.NRadioGroup,
|
||||||
|
NaiveUI.NRadio,
|
||||||
|
NaiveUI.NInputGroup,
|
||||||
|
NaiveUI.NTable,
|
||||||
|
NaiveUI.NInputNumber,
|
||||||
|
NaiveUI.NLoadingBarProvider,
|
||||||
|
NaiveUI.NModal,
|
||||||
|
NaiveUI.NUpload,
|
||||||
|
NaiveUI.NTree,
|
||||||
|
NaiveUI.NSpin,
|
||||||
|
NaiveUI.NTimePicker,
|
||||||
|
|
||||||
|
// add by fs
|
||||||
|
NaiveUI.NCascader,
|
||||||
|
NaiveUI.NRadioButton,
|
||||||
|
NaiveUI.NTreeSelect,
|
||||||
|
NaiveUI.NImageGroup,
|
||||||
|
NaiveUI.NImage,
|
||||||
|
NaiveUI.NCollapse,
|
||||||
|
NaiveUI.NCollapseItem
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export function setupNaive(app: App<Element>) {
|
||||||
|
app.use(naive);
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
import { setupFastCrud } from '@/plugins/fast-crud';
|
||||||
import setupAssets from './assets';
|
import setupAssets from './assets';
|
||||||
|
|
||||||
|
export { setupFastCrud };
|
||||||
export { setupAssets };
|
export { setupAssets };
|
||||||
|
45
src/router/modules/crud.ts
Normal file
45
src/router/modules/crud.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const component: any = {
|
||||||
|
name: 'crud',
|
||||||
|
path: '/crud',
|
||||||
|
component: 'basic',
|
||||||
|
meta: {
|
||||||
|
title: 'CRUD示例',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:table-large',
|
||||||
|
order: 4
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'crud_demo',
|
||||||
|
path: '/crud/demo',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '基本示例',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:button-cursor'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'crud_header_group',
|
||||||
|
path: '/crud/header_group',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '多级表头',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:button-cursor'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'crud_doc',
|
||||||
|
path: '/crud/doc',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: 'FastCrud文档',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'logos:vue'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default component;
|
10
src/typings/page-route.d.ts
vendored
10
src/typings/page-route.d.ts
vendored
@ -30,6 +30,12 @@ declare namespace PageRoute {
|
|||||||
| 'component_button'
|
| 'component_button'
|
||||||
| 'component_card'
|
| 'component_card'
|
||||||
| 'component_table'
|
| 'component_table'
|
||||||
|
| 'crud'
|
||||||
|
| 'crud_demo'
|
||||||
|
| 'crud_doc'
|
||||||
|
| 'crud_header'
|
||||||
|
| 'crud_header_group'
|
||||||
|
| 'crud_source'
|
||||||
| 'dashboard'
|
| 'dashboard'
|
||||||
| 'dashboard_analysis'
|
| 'dashboard_analysis'
|
||||||
| 'dashboard_workbench'
|
| 'dashboard_workbench'
|
||||||
@ -89,6 +95,10 @@ declare namespace PageRoute {
|
|||||||
| 'component_button'
|
| 'component_button'
|
||||||
| 'component_card'
|
| 'component_card'
|
||||||
| 'component_table'
|
| 'component_table'
|
||||||
|
| 'crud_demo'
|
||||||
|
| 'crud_doc'
|
||||||
|
| 'crud_header_group'
|
||||||
|
| 'crud_source'
|
||||||
| 'dashboard_analysis'
|
| 'dashboard_analysis'
|
||||||
| 'dashboard_workbench'
|
| 'dashboard_workbench'
|
||||||
| 'document_naive'
|
| 'document_naive'
|
||||||
|
43
src/views/crud/demo/api.ts
Normal file
43
src/views/crud/demo/api.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import type { UserPageQuery } from '@fast-crud/fast-crud';
|
||||||
|
import { mockRequest } from '@/service/request';
|
||||||
|
|
||||||
|
const request = mockRequest;
|
||||||
|
const apiPrefix = '/crud/demo';
|
||||||
|
|
||||||
|
export type DemoRecord = {
|
||||||
|
id: number;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
function resHandle(res: any) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
export async function GetList(query: UserPageQuery) {
|
||||||
|
const res = await request.post(`${apiPrefix}/page`, query);
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function AddObj(obj: DemoRecord) {
|
||||||
|
const res = await request.post(`${apiPrefix}/add`, obj);
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function UpdateObj(obj: DemoRecord) {
|
||||||
|
const res = await request.post(`${apiPrefix}/update`, obj);
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DelObj(id: number) {
|
||||||
|
const res = await request.post(`${apiPrefix}/delete`, { id });
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GetObj(id: number) {
|
||||||
|
const res = await request.get(`${apiPrefix}/info`, { params: { id } });
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function BatchDelete(ids: number[]) {
|
||||||
|
const res = await request.post(`${apiPrefix}/batchDelete`, { ids });
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
114
src/views/crud/demo/crud.tsx
Normal file
114
src/views/crud/demo/crud.tsx
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import type { AddReq, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from '@fast-crud/fast-crud';
|
||||||
|
import { dict } from '@fast-crud/fast-crud';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import * as api from './api';
|
||||||
|
|
||||||
|
export default function createCrudOptions(): CreateCrudOptionsRet {
|
||||||
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
|
return api.GetList(query);
|
||||||
|
};
|
||||||
|
const editRequest = async (ctx: EditReq) => {
|
||||||
|
const { form, row } = ctx;
|
||||||
|
form.id = row.id;
|
||||||
|
return api.UpdateObj(form);
|
||||||
|
};
|
||||||
|
const delRequest = async (ctx: DelReq) => {
|
||||||
|
const { row } = ctx;
|
||||||
|
return api.DelObj(row.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addRequest = async (req: AddReq) => {
|
||||||
|
const { form } = req;
|
||||||
|
return api.AddObj(form);
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
container: {
|
||||||
|
is: 'fs-layout-card'
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
pageRequest,
|
||||||
|
addRequest,
|
||||||
|
editRequest,
|
||||||
|
delRequest
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
id: {
|
||||||
|
title: 'ID',
|
||||||
|
key: 'id',
|
||||||
|
type: 'number',
|
||||||
|
column: {
|
||||||
|
width: 50
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
datetime: {
|
||||||
|
title: '时间',
|
||||||
|
type: 'datetime',
|
||||||
|
// naive 默认仅支持数字类型时间戳作为日期输入与输出
|
||||||
|
// 字符串类型的时间需要转换格式
|
||||||
|
valueBuilder(context) {
|
||||||
|
const { value, row, key } = context;
|
||||||
|
if (value) {
|
||||||
|
// naive 默认仅支持时间戳作为日期输入与输出
|
||||||
|
row[key] = dayjs(value).valueOf();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valueResolve(context) {
|
||||||
|
const { value, form, key } = context;
|
||||||
|
if (value) {
|
||||||
|
form[key] = dayjs(value).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
title: '状态',
|
||||||
|
search: { show: true },
|
||||||
|
type: 'dict-select',
|
||||||
|
dict: dict({
|
||||||
|
url: '/mock/crud/demo/dict'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
title: '文本',
|
||||||
|
type: 'text',
|
||||||
|
search: { show: true }
|
||||||
|
},
|
||||||
|
copyable: {
|
||||||
|
title: '可复制',
|
||||||
|
type: ['text', 'copyable'],
|
||||||
|
search: { show: true }
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
title: '头像裁剪',
|
||||||
|
type: 'cropper-uploader'
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
title: '文件上传',
|
||||||
|
type: 'file-uploader'
|
||||||
|
},
|
||||||
|
richtext: {
|
||||||
|
title: '富文本',
|
||||||
|
type: 'editor-wang5',
|
||||||
|
column: {
|
||||||
|
// cell中不显示
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
col: {
|
||||||
|
// 横跨两列
|
||||||
|
span: 24
|
||||||
|
},
|
||||||
|
component: {
|
||||||
|
style: {
|
||||||
|
height: '300px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
28
src/views/crud/demo/index.vue
Normal file
28
src/views/crud/demo/index.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full">
|
||||||
|
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onMounted } from 'vue';
|
||||||
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
|
import createCrudOptions from './crud';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ComponentCrud',
|
||||||
|
setup() {
|
||||||
|
const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
|
||||||
|
|
||||||
|
// 页面打开后获取列表数据
|
||||||
|
onMounted(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
crudBinding,
|
||||||
|
crudRef
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
13
src/views/crud/doc/index.vue
Normal file
13
src/views/crud/doc/index.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full">
|
||||||
|
<iframe class="wh-full" :src="src"></iframe>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const src = ref('http://fast-crud.docmirror.cn/');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
43
src/views/crud/header_group/api.ts
Normal file
43
src/views/crud/header_group/api.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import type { UserPageQuery } from '@fast-crud/fast-crud';
|
||||||
|
import { mockRequest } from '@/service/request';
|
||||||
|
|
||||||
|
const request = mockRequest;
|
||||||
|
const apiPrefix = '/crud/header-group';
|
||||||
|
|
||||||
|
export type HeaderGroupRecord = {
|
||||||
|
id: number;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
function resHandle(res: any) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
export async function GetList(query: UserPageQuery) {
|
||||||
|
const res = await request.post(`${apiPrefix}/page`, query);
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function AddObj(obj: HeaderGroupRecord) {
|
||||||
|
const res = await request.post(`${apiPrefix}/add`, obj);
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function UpdateObj(obj: HeaderGroupRecord) {
|
||||||
|
const res = await request.post(`${apiPrefix}/update`, obj);
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DelObj(id: number) {
|
||||||
|
const res = await request.post(`${apiPrefix}/delete`, { id });
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GetObj(id: number) {
|
||||||
|
const res = await request.get(`${apiPrefix}/info`, { params: { id } });
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function BatchDelete(ids: number[]) {
|
||||||
|
const res = await request.post(`${apiPrefix}/batchDelete`, { ids });
|
||||||
|
return resHandle(res);
|
||||||
|
}
|
96
src/views/crud/header_group/crud.tsx
Normal file
96
src/views/crud/header_group/crud.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import type { CreateCrudOptionsRet, UserPageQuery, UserPageRes } from '@fast-crud/fast-crud';
|
||||||
|
import type { HeaderGroupRecord } from './api';
|
||||||
|
import * as api from './api';
|
||||||
|
|
||||||
|
export default function createCrudOptions(): CreateCrudOptionsRet {
|
||||||
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
|
return api.GetList(query);
|
||||||
|
};
|
||||||
|
const editRequest = async (ctx: { form: HeaderGroupRecord; row: HeaderGroupRecord }) => {
|
||||||
|
const { form, row } = ctx;
|
||||||
|
form.id = row.id;
|
||||||
|
return api.UpdateObj(form);
|
||||||
|
};
|
||||||
|
const delRequest = async (ctx: { row: HeaderGroupRecord }) => {
|
||||||
|
const { row } = ctx;
|
||||||
|
return api.DelObj(row.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addRequest = async (ctx: { form: HeaderGroupRecord }) => {
|
||||||
|
const { form } = ctx;
|
||||||
|
return api.AddObj(form);
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
container: {
|
||||||
|
// is: 'fs-layout-card'
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
pageRequest,
|
||||||
|
addRequest,
|
||||||
|
editRequest,
|
||||||
|
delRequest
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
layout: 'flex',
|
||||||
|
labelWidth: '100px' // 表单label宽度
|
||||||
|
},
|
||||||
|
table: { size: 'small' },
|
||||||
|
columns: {
|
||||||
|
id: {
|
||||||
|
title: 'ID',
|
||||||
|
key: 'id',
|
||||||
|
type: 'number',
|
||||||
|
column: {
|
||||||
|
width: 50
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
title: '用户信息',
|
||||||
|
children: {
|
||||||
|
name: {
|
||||||
|
title: '姓名',
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
age: {
|
||||||
|
title: '年龄',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
title: '地址',
|
||||||
|
children: {
|
||||||
|
area: {
|
||||||
|
title: '地区',
|
||||||
|
children: {
|
||||||
|
province: {
|
||||||
|
title: '省',
|
||||||
|
type: 'text',
|
||||||
|
search: { show: true }
|
||||||
|
},
|
||||||
|
city: {
|
||||||
|
title: '市',
|
||||||
|
search: { show: true },
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
county: {
|
||||||
|
title: '区',
|
||||||
|
search: { show: true },
|
||||||
|
type: 'text'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
street: {
|
||||||
|
title: '街道',
|
||||||
|
type: 'text'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
32
src/views/crud/header_group/index.vue
Normal file
32
src/views/crud/header_group/index.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full fs-page-header-group">
|
||||||
|
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onMounted } from 'vue';
|
||||||
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
|
import createCrudOptions from './crud';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CrudHeaderGroup',
|
||||||
|
setup() {
|
||||||
|
const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
|
||||||
|
|
||||||
|
// 页面打开后获取列表数据
|
||||||
|
onMounted(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
crudBinding,
|
||||||
|
crudRef
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.fs-page-header-group {
|
||||||
|
}
|
||||||
|
</style>
|
13
src/views/crud/source/index.vue
Normal file
13
src/views/crud/source/index.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full">
|
||||||
|
<iframe class="wh-full" :src="src"></iframe>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const src = ref('https://github.com/fast-crud/fast-crud');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
@ -16,6 +16,10 @@ export const views: Record<
|
|||||||
component_button: () => import('./component/button/index.vue'),
|
component_button: () => import('./component/button/index.vue'),
|
||||||
component_card: () => import('./component/card/index.vue'),
|
component_card: () => import('./component/card/index.vue'),
|
||||||
component_table: () => import('./component/table/index.vue'),
|
component_table: () => import('./component/table/index.vue'),
|
||||||
|
crud_demo: () => import('./crud/demo/index.vue'),
|
||||||
|
crud_doc: () => import('./crud/doc/index.vue'),
|
||||||
|
crud_header_group: () => import('./crud/header_group/index.vue'),
|
||||||
|
crud_source: () => import('./crud/source/index.vue'),
|
||||||
dashboard_analysis: () => import('./dashboard/analysis/index.vue'),
|
dashboard_analysis: () => import('./dashboard/analysis/index.vue'),
|
||||||
dashboard_workbench: () => import('./dashboard/workbench/index.vue'),
|
dashboard_workbench: () => import('./dashboard/workbench/index.vue'),
|
||||||
document_naive: () => import('./document/naive/index.vue'),
|
document_naive: () => import('./document/naive/index.vue'),
|
||||||
|
Loading…
Reference in New Issue
Block a user