diff --git a/build/plugins/router.ts b/build/plugins/router.ts
index 40a5ae51..65460f92 100644
--- a/build/plugins/router.ts
+++ b/build/plugins/router.ts
@@ -19,7 +19,8 @@ export function setupElegantRouter() {
'document_vite',
'document_unocss',
'document_naive',
- 'document_antd'
+ 'document_antd',
+ 'document_alova'
]
},
routePathTransformer(routeName, routePath) {
diff --git a/src/assets/svg-icon/alova.svg b/src/assets/svg-icon/alova.svg
new file mode 100644
index 00000000..a21d0e27
--- /dev/null
+++ b/src/assets/svg-icon/alova.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/locales/langs/en-us.ts b/src/locales/langs/en-us.ts
index 9ba2d5a3..4cf367bc 100644
--- a/src/locales/langs/en-us.ts
+++ b/src/locales/langs/en-us.ts
@@ -113,7 +113,7 @@ const local: App.I18n.Schema = {
},
tab: {
visible: 'Tab Visible',
- cache: 'Tab Cache',
+ cache: 'Tag Bar Info Cache',
height: 'Tab Height',
mode: {
title: 'Tab Mode',
@@ -163,9 +163,14 @@ const local: App.I18n.Schema = {
document_unocss: 'UnoCSS Document',
document_naive: 'Naive UI Document',
document_antd: 'Ant Design Vue Document',
+ document_alova: 'Alova Document',
'user-center': 'User Center',
about: 'About',
function: 'System Function',
+ alova: 'Alova Example',
+ alova_request: 'Alova Request',
+ alova_user: 'User List',
+ alova_scenes: 'Scenario Request',
function_tab: 'Tab',
'function_multi-tab': 'Multi Tab',
'function_hide-child': 'Hide Child',
@@ -337,6 +342,20 @@ const local: App.I18n.Schema = {
repeatedErrorMsg2: 'Custom Request Error 2'
}
},
+ alova: {
+ scenes: {
+ captchaSend: 'Captcha Send',
+ autoRequest: 'Auto Request',
+ visibilityRequestTips: 'Automatically request when switching browser window',
+ pollingRequestTips: 'It will request every 3 seconds',
+ networkRequestTips: 'Automatically request after network reconnecting',
+ refreshTime: 'Refresh Time',
+ startRequest: 'Start Request',
+ stopRequest: 'Stop Request',
+ requestCrossComponent: 'Request Cross Component',
+ triggerAllRequest: 'Manually Trigger All Automated Requests'
+ }
+ },
manage: {
common: {
status: {
diff --git a/src/locales/langs/zh-cn.ts b/src/locales/langs/zh-cn.ts
index 8c2cdffd..5bdbda26 100644
--- a/src/locales/langs/zh-cn.ts
+++ b/src/locales/langs/zh-cn.ts
@@ -113,7 +113,7 @@ const local: App.I18n.Schema = {
},
tab: {
visible: '显示标签栏',
- cache: '缓存标签页',
+ cache: '标签栏信息缓存',
height: '标签栏高度',
mode: {
title: '标签栏风格',
@@ -163,9 +163,14 @@ const local: App.I18n.Schema = {
document_unocss: 'UnoCSS文档',
document_naive: 'Naive UI文档',
document_antd: 'Ant Design Vue文档',
+ document_alova: 'Alova文档',
'user-center': '个人中心',
about: '关于',
function: '系统功能',
+ alova: 'alova示例',
+ alova_request: 'alova请求',
+ alova_user: '用户列表',
+ alova_scenes: '场景化请求',
function_tab: '标签页',
'function_multi-tab': '多标签页',
'function_hide-child': '隐藏子菜单',
@@ -337,6 +342,20 @@ const local: App.I18n.Schema = {
repeatedErrorMsg2: '自定义请求错误 2'
}
},
+ alova: {
+ scenes: {
+ captchaSend: '发送验证码',
+ autoRequest: '自动请求',
+ visibilityRequestTips: '浏览器窗口切换自动请求数据',
+ pollingRequestTips: '每3秒自动请求一次',
+ networkRequestTips: '网络重连后自动请求',
+ refreshTime: '更新时间',
+ startRequest: '开始请求',
+ stopRequest: '停止请求',
+ requestCrossComponent: '跨组件触发请求',
+ triggerAllRequest: '手动触发所有自动请求'
+ }
+ },
manage: {
common: {
status: {
diff --git a/src/router/elegant/imports.ts b/src/router/elegant/imports.ts
index c992e847..b06a9180 100644
--- a/src/router/elegant/imports.ts
+++ b/src/router/elegant/imports.ts
@@ -21,6 +21,9 @@ export const views: Record Promise import("@/views/_builtin/iframe-page/[url].vue"),
login: () => import("@/views/_builtin/login/index.vue"),
about: () => import("@/views/about/index.vue"),
+ alova_request: () => import("@/views/alova/request/index.vue"),
+ alova_scenes: () => import("@/views/alova/scenes/index.vue"),
+ alova_user: () => import("@/views/alova/user/index.vue"),
"function_hide-child_one": () => import("@/views/function/hide-child/one/index.vue"),
"function_hide-child_three": () => import("@/views/function/hide-child/three/index.vue"),
"function_hide-child_two": () => import("@/views/function/hide-child/two/index.vue"),
diff --git a/src/router/elegant/routes.ts b/src/router/elegant/routes.ts
index 30a21da5..ed5a3dff 100644
--- a/src/router/elegant/routes.ts
+++ b/src/router/elegant/routes.ts
@@ -50,6 +50,51 @@ export const generatedRoutes: GeneratedRoute[] = [
order: 10
}
},
+ {
+ name: 'alova',
+ path: '/alova',
+ component: 'layout.base',
+ meta: {
+ title: 'alova',
+ i18nKey: 'route.alova',
+ icon: 'carbon:http',
+ order: 7
+ },
+ children: [
+ {
+ name: 'alova_request',
+ path: '/alova/request',
+ component: 'view.alova_request',
+ meta: {
+ title: 'alova_request',
+ i18nKey: 'route.alova_request',
+ order: 1
+ }
+ },
+ {
+ name: 'alova_scenes',
+ path: '/alova/scenes',
+ component: 'view.alova_scenes',
+ meta: {
+ title: 'alova_scenes',
+ i18nKey: 'route.alova_scenes',
+ icon: 'cbi:scene-dynamic',
+ order: 3
+ }
+ },
+ {
+ name: 'alova_user',
+ path: '/alova/user',
+ component: 'view.alova_user',
+ meta: {
+ title: 'alova_user',
+ i18nKey: 'route.alova_user',
+ icon: 'carbon:user-multiple',
+ order: 2
+ }
+ }
+ ]
+ },
{
name: 'function',
path: '/function',
diff --git a/src/router/elegant/transform.ts b/src/router/elegant/transform.ts
index 05d86a8a..734197e6 100644
--- a/src/router/elegant/transform.ts
+++ b/src/router/elegant/transform.ts
@@ -175,10 +175,15 @@ const routeMap: RouteMap = {
"document_unocss": "/document/unocss",
"document_naive": "/document/naive",
"document_antd": "/document/antd",
+ "document_alova": "/document/alova",
"403": "/403",
"404": "/404",
"500": "/500",
"about": "/about",
+ "alova": "/alova",
+ "alova_request": "/alova/request",
+ "alova_scenes": "/alova/scenes",
+ "alova_user": "/alova/user",
"function": "/function",
"function_hide-child": "/function/hide-child",
"function_hide-child_one": "/function/hide-child/one",
diff --git a/src/router/routes/index.ts b/src/router/routes/index.ts
index 96322a9e..5be1e3b7 100644
--- a/src/router/routes/index.ts
+++ b/src/router/routes/index.ts
@@ -91,6 +91,20 @@ const customRoutes: CustomRoute[] = [
icon: 'logos:naiveui'
}
},
+ {
+ name: 'document_alova',
+ path: '/document/alova',
+ component: 'view.iframe-page',
+ props: {
+ url: 'https://alova.js.org'
+ },
+ meta: {
+ title: 'document_alova',
+ i18nKey: 'route.document_alova',
+ order: 7,
+ localIcon: 'alova'
+ }
+ },
{
name: 'document_project',
path: '/document/project',
diff --git a/src/serviceAlova/api/index.ts b/src/serviceAlova/api/index.ts
index 89f4e581..c9d31d11 100644
--- a/src/serviceAlova/api/index.ts
+++ b/src/serviceAlova/api/index.ts
@@ -1,2 +1,3 @@
export * from './auth';
export * from './route';
+export * from './system-manage';
diff --git a/src/serviceAlova/api/system-manage.ts b/src/serviceAlova/api/system-manage.ts
new file mode 100644
index 00000000..986b5daf
--- /dev/null
+++ b/src/serviceAlova/api/system-manage.ts
@@ -0,0 +1,59 @@
+import { alova } from '../request';
+
+/** get role list */
+export function fetchGetRoleList(params?: Api.SystemManage.RoleSearchParams) {
+ return alova.Get('/systemManage/getRoleList', { params });
+}
+
+/**
+ * get all roles
+ *
+ * these roles are all enabled
+ */
+export function fetchGetAllRoles() {
+ return alova.Get('/systemManage/getAllRoles');
+}
+
+/** get user list */
+export function fetchGetUserList(params?: Api.SystemManage.UserSearchParams) {
+ return alova.Get('/systemManage/getUserList', { params });
+}
+
+export type UserModel = Pick<
+ Api.SystemManage.User,
+ 'userName' | 'userGender' | 'nickName' | 'userPhone' | 'userEmail' | 'userRoles' | 'status'
+>;
+/** add user */
+export function addUser(data: UserModel) {
+ return alova.Post('/systemManage/addUser', data);
+}
+
+/** update user */
+export function updateUser(data: UserModel) {
+ return alova.Post('/systemManage/updateUser', data);
+}
+
+/** delete user */
+export function deleteUser(id: number) {
+ return alova.Delete('/systemManage/deleteUser', { id });
+}
+
+/** batch delete user */
+export function batchDeleteUser(ids: number[]) {
+ return alova.Delete('/systemManage/batchDeleteUser', { ids });
+}
+
+/** get menu list */
+export function fetchGetMenuList() {
+ return alova.Get('/systemManage/getMenuList/v2');
+}
+
+/** get all pages */
+export function fetchGetAllPages() {
+ return alova.Get('/systemManage/getAllPages');
+}
+
+/** get menu tree */
+export function fetchGetMenuTree() {
+ return alova.Get('/systemManage/getMenuTree');
+}
diff --git a/src/typings/app.d.ts b/src/typings/app.d.ts
index f3a807f6..ae67f22e 100644
--- a/src/typings/app.d.ts
+++ b/src/typings/app.d.ts
@@ -523,6 +523,20 @@ declare namespace App {
repeatedErrorMsg2: string;
};
};
+ alova: {
+ scenes: {
+ captchaSend: string;
+ autoRequest: string;
+ visibilityRequestTips: string;
+ pollingRequestTips: string;
+ networkRequestTips: string;
+ refreshTime: string;
+ startRequest: string;
+ stopRequest: string;
+ requestCrossComponent: string;
+ triggerAllRequest: string;
+ };
+ };
manage: {
common: {
status: {
diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts
index 09f7cdd1..4a1ab6af 100644
--- a/src/typings/components.d.ts
+++ b/src/typings/components.d.ts
@@ -19,6 +19,8 @@ declare module 'vue' {
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
+ IconCarbonPlay: typeof import('~icons/carbon/play')['default']
+ IconCarbonStop: typeof import('~icons/carbon/stop')['default']
'IconCharm:download': typeof import('~icons/charm/download')['default']
'IconFileIcons:microsoftExcel': typeof import('~icons/file-icons/microsoft-excel')['default']
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
@@ -45,6 +47,7 @@ declare module 'vue' {
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
+ NAlert: typeof import('naive-ui')['NAlert']
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
NButton: typeof import('naive-ui')['NButton']
@@ -87,6 +90,7 @@ declare module 'vue' {
NSelect: typeof import('naive-ui')['NSelect']
NSkeleton: typeof import('naive-ui')['NSkeleton']
NSpace: typeof import('naive-ui')['NSpace']
+ NSpin: typeof import('naive-ui')['NSpin']
NStatistic: typeof import('naive-ui')['NStatistic']
NSwitch: typeof import('naive-ui')['NSwitch']
NTab: typeof import('naive-ui')['NTab']
diff --git a/src/typings/elegant-router.d.ts b/src/typings/elegant-router.d.ts
index 7c905d73..3dbe3e82 100644
--- a/src/typings/elegant-router.d.ts
+++ b/src/typings/elegant-router.d.ts
@@ -29,10 +29,15 @@ declare module "@elegant-router/types" {
"document_unocss": "/document/unocss";
"document_naive": "/document/naive";
"document_antd": "/document/antd";
+ "document_alova": "/document/alova";
"403": "/403";
"404": "/404";
"500": "/500";
"about": "/about";
+ "alova": "/alova";
+ "alova_request": "/alova/request";
+ "alova_scenes": "/alova/scenes";
+ "alova_user": "/alova/user";
"function": "/function";
"function_hide-child": "/function/hide-child";
"function_hide-child_one": "/function/hide-child/one";
@@ -107,6 +112,7 @@ declare module "@elegant-router/types" {
| "document_unocss"
| "document_naive"
| "document_antd"
+ | "document_alova"
>;
/**
@@ -123,6 +129,7 @@ declare module "@elegant-router/types" {
| "404"
| "500"
| "about"
+ | "alova"
| "function"
| "home"
| "iframe-page"
@@ -155,6 +162,9 @@ declare module "@elegant-router/types" {
| "iframe-page"
| "login"
| "about"
+ | "alova_request"
+ | "alova_scenes"
+ | "alova_user"
| "function_hide-child_one"
| "function_hide-child_three"
| "function_hide-child_two"
@@ -205,6 +215,7 @@ declare module "@elegant-router/types" {
| "document_unocss"
| "document_naive"
| "document_antd"
+ | "document_alova"
>;
/**
diff --git a/src/utils/agent.ts b/src/utils/agent.ts
index 736cbd8f..a8416b2a 100644
--- a/src/utils/agent.ts
+++ b/src/utils/agent.ts
@@ -1,5 +1,7 @@
export function isPC() {
const agents = ['Android', 'iPhone', 'webOS', 'BlackBerry', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod'];
- return !agents.includes(window.navigator.userAgent);
+ const isMobile = agents.some(agent => window.navigator.userAgent.includes(agent));
+
+ return !isMobile;
}
diff --git a/src/views/alova/request/index.vue b/src/views/alova/request/index.vue
new file mode 100644
index 00000000..e5dee1f1
--- /dev/null
+++ b/src/views/alova/request/index.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+ {{ $t('common.trigger') }}
+
+
+ {{ $t('common.trigger') }}
+
+
+ {{ $t('common.trigger') }}
+
+
+ {{ $t('page.function.request.repeatedError') }}(Message)
+
+ {{ $t('page.function.request.repeatedError') }}(Modal)
+
+
+
+
+
+
diff --git a/src/views/alova/scenes/index.vue b/src/views/alova/scenes/index.vue
new file mode 100644
index 00000000..173f504a
--- /dev/null
+++ b/src/views/alova/scenes/index.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/alova/scenes/modules/BrowserVisivilityRequest.vue b/src/views/alova/scenes/modules/BrowserVisivilityRequest.vue
new file mode 100644
index 00000000..0a770450
--- /dev/null
+++ b/src/views/alova/scenes/modules/BrowserVisivilityRequest.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+ {{ $t('page.alova.scenes.visibilityRequestTips') }}
+
+
+
+
+ {{ isStop ? $t('page.alova.scenes.startRequest') : $t('page.alova.scenes.stopRequest') }}
+
+
+ {{ $t('page.alova.scenes.refreshTime') }}: {{ data.time || '--' }}
+
+
+
+
diff --git a/src/views/alova/scenes/modules/Captcha.vue b/src/views/alova/scenes/modules/Captcha.vue
new file mode 100644
index 00000000..c5f3ab6f
--- /dev/null
+++ b/src/views/alova/scenes/modules/Captcha.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+ {{ $t('common.confirm') }}
+
+
+
+
+
+
diff --git a/src/views/alova/scenes/modules/CrossComponentRequest.vue b/src/views/alova/scenes/modules/CrossComponentRequest.vue
new file mode 100644
index 00000000..404c3544
--- /dev/null
+++ b/src/views/alova/scenes/modules/CrossComponentRequest.vue
@@ -0,0 +1,13 @@
+
+
+
+ {{ $t('page.alova.scenes.triggerAllRequest') }}
+
diff --git a/src/views/alova/scenes/modules/NetworkToggleRequest.vue b/src/views/alova/scenes/modules/NetworkToggleRequest.vue
new file mode 100644
index 00000000..f11bd5a0
--- /dev/null
+++ b/src/views/alova/scenes/modules/NetworkToggleRequest.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+ {{ $t('page.alova.scenes.networkRequestTips') }}
+
+
+
+
+ {{ isStop ? $t('page.alova.scenes.startRequest') : $t('page.alova.scenes.stopRequest') }}
+
+
+ {{ $t('page.alova.scenes.refreshTime') }}: {{ data.time || '--' }}
+
+
+
+
diff --git a/src/views/alova/scenes/modules/PollingRequest.vue b/src/views/alova/scenes/modules/PollingRequest.vue
new file mode 100644
index 00000000..0efbf5c6
--- /dev/null
+++ b/src/views/alova/scenes/modules/PollingRequest.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+ {{ $t('page.alova.scenes.pollingRequestTips') }}
+
+
+
+
+ {{ isStop ? $t('page.alova.scenes.startRequest') : $t('page.alova.scenes.stopRequest') }}
+
+
+ {{ $t('page.alova.scenes.refreshTime') }}: {{ data.time || '--' }}
+
+
+
+
diff --git a/src/views/alova/user/hooks/useCheckedColumns.ts b/src/views/alova/user/hooks/useCheckedColumns.ts
new file mode 100644
index 00000000..2d4edfa4
--- /dev/null
+++ b/src/views/alova/user/hooks/useCheckedColumns.ts
@@ -0,0 +1,78 @@
+import type { TableColumnCheck } from '@sa/hooks';
+import { computed, ref } from 'vue';
+import type { DataTableBaseColumn, DataTableColumn } from 'naive-ui';
+import { $t } from '@/locales';
+import type { AlovaGenerics, Method } from '~/packages/alova/src';
+
+function isTableColumnHasKey(column: DataTableColumn): column is DataTableBaseColumn {
+ return Boolean((column as NaiveUI.TableColumnWithKey).key);
+}
+
+type TableAlovaApiFn = (
+ params: R
+) => Method>>;
+
+// this hook is used to manage table columns
+// if you choose alova, you can move this hook to the `src/hooks` to handle all list page in your project
+export default function useCheckedColumns>['records'][number]>(
+ getColumns: () => DataTableColumn[]
+) {
+ const SELECTION_KEY = '__selection__';
+
+ const EXPAND_KEY = '__expand__';
+
+ const getColumnChecks = (cols: DataTableColumn[]) => {
+ const checks: NaiveUI.TableColumnCheck[] = [];
+ cols.forEach(column => {
+ if (isTableColumnHasKey(column)) {
+ checks.push({
+ key: column.key as string,
+ title: column.title as string,
+ checked: true
+ });
+ } else if (column.type === 'selection') {
+ checks.push({
+ key: SELECTION_KEY,
+ title: $t('common.check'),
+ checked: true
+ });
+ } else if (column.type === 'expand') {
+ checks.push({
+ key: EXPAND_KEY,
+ title: $t('common.expandColumn'),
+ checked: true
+ });
+ }
+ });
+
+ return checks;
+ };
+
+ const columnChecks = ref(getColumnChecks(getColumns()));
+
+ const columns = computed(() => {
+ const cols = getColumns();
+ const columnMap = new Map>();
+
+ cols.forEach(column => {
+ if (isTableColumnHasKey(column)) {
+ columnMap.set(column.key as string, column);
+ } else if (column.type === 'selection') {
+ columnMap.set(SELECTION_KEY, column);
+ } else if (column.type === 'expand') {
+ columnMap.set(EXPAND_KEY, column);
+ }
+ });
+
+ const filteredColumns = columnChecks.value
+ .filter(item => item.checked)
+ .map(check => columnMap.get(check.key) as NaiveUI.TableColumn);
+
+ return filteredColumns;
+ });
+
+ return {
+ columnChecks,
+ columns
+ };
+}
diff --git a/src/views/alova/user/hooks/useTableOperate.ts b/src/views/alova/user/hooks/useTableOperate.ts
new file mode 100644
index 00000000..a8ed5921
--- /dev/null
+++ b/src/views/alova/user/hooks/useTableOperate.ts
@@ -0,0 +1,83 @@
+import { useBoolean } from '@sa/hooks';
+import type { Ref } from 'vue';
+import { ref } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { $t } from '@/locales';
+
+type TableData = NaiveUI.TableData;
+interface Operations {
+ delete?: (row: T) => Promise;
+ batchDelete?: (rows: T[]) => Promise;
+}
+
+// this hook is used to handle the table operations
+// if you choose alova, you can move this hook to the `src/hooks` to handle all list page in your project
+export default function useTableOperate(data: Ref, operations: Operations) {
+ const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean();
+ const { bool: deleting, setTrue: deletify, setFalse: antiDelete } = useBoolean();
+ const { bool: batchDeleting, setTrue: batchDeletify, setFalse: antiBatchDelete } = useBoolean();
+
+ const operateType = ref('add');
+
+ const getRowByDataId = (id: T['id']) => data.value.find(item => item.id === id) || null;
+
+ function handleAdd() {
+ operateType.value = 'add';
+ openDrawer();
+ }
+
+ /** the editing row data */
+ const editingData: Ref = ref(null);
+
+ function handleEdit(id: T['id']) {
+ operateType.value = 'edit';
+ editingData.value = jsonClone(getRowByDataId(id));
+
+ openDrawer();
+ }
+
+ /** the checked row keys of table */
+ const checkedRowKeys = ref([]);
+
+ /** handler to batch delete rows */
+ async function handleBatchDelete() {
+ batchDeletify();
+ try {
+ const rows = checkedRowKeys.value.map(id => getRowByDataId(id)).filter(Boolean);
+ await operations.batchDelete?.(rows as T[]);
+ window.$message?.success($t('common.deleteSuccess'));
+ checkedRowKeys.value = [];
+ } finally {
+ antiBatchDelete();
+ }
+ }
+
+ /** handler to delete row */
+ async function handleDelete(id: T['id']) {
+ deletify();
+ const row = getRowByDataId(id);
+ if (!row) return;
+ try {
+ await operations.delete?.(row);
+ window.$message?.success($t('common.deleteSuccess'));
+ checkedRowKeys.value = [];
+ } finally {
+ antiDelete();
+ }
+ }
+
+ return {
+ drawerVisible,
+ openDrawer,
+ closeDrawer,
+ operateType,
+ handleAdd,
+ editingData,
+ handleEdit,
+ checkedRowKeys,
+ deleting,
+ handleDelete,
+ batchDeleting,
+ handleBatchDelete
+ };
+}
diff --git a/src/views/alova/user/index.vue b/src/views/alova/user/index.vue
new file mode 100644
index 00000000..d99d8535
--- /dev/null
+++ b/src/views/alova/user/index.vue
@@ -0,0 +1,226 @@
+
+
+
+
+
+
+
diff --git a/src/views/alova/user/modules/user-operate-drawer.vue b/src/views/alova/user/modules/user-operate-drawer.vue
new file mode 100644
index 00000000..9c07130c
--- /dev/null
+++ b/src/views/alova/user/modules/user-operate-drawer.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('common.cancel') }}
+ {{ $t('common.confirm') }}
+
+
+
+
+
+
+
diff --git a/src/views/alova/user/modules/user-search.vue b/src/views/alova/user/modules/user-search.vue
new file mode 100644
index 00000000..9b458d79
--- /dev/null
+++ b/src/views/alova/user/modules/user-search.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('common.reset') }}
+
+
+
+
+
+ {{ $t('common.search') }}
+
+
+
+
+
+
+
+
+
+
+